@codyswann/lisa 4.10.0 → 4.10.2
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/all/copy-overwrite/scripts/lisa-run-gates.mjs +18 -2
- package/dist/codex/scripts/setup-jira-cli.sh +87 -18
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +2 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +3 -2
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/lisa-session-bootstrap.ts +11 -2
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/setup-jira-cli.sh +88 -22
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/setup-jira-cli.sh +88 -22
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/setup-jira-cli.sh +88 -22
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/setup-jira-cli.sh +88 -22
|
@@ -81,8 +81,17 @@ export const LisaSessionBootstrap = async ({
|
|
|
81
81
|
value = (value as Record<string, unknown>)[key];
|
|
82
82
|
}
|
|
83
83
|
if (typeof value === "string" && value) return value;
|
|
84
|
-
} catch {
|
|
85
|
-
//
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// VISIBLE, not silent. A config that exists but cannot be parsed is
|
|
86
|
+
// a failure, not an answer: treating it as absent reports the
|
|
87
|
+
// project as unconfigured when nobody can see that anything went
|
|
88
|
+
// wrong. The two shell implementations exit non-zero here. This one
|
|
89
|
+
// cannot — throwing out of the plugin factory would take the whole
|
|
90
|
+
// session down — so the compensating rung is a warning naming the
|
|
91
|
+
// file, per the AGENTS.md rule on gaps a harness cannot represent.
|
|
92
|
+
console.error(
|
|
93
|
+
`lisa-session-bootstrap: ${configPath} is not valid JSON; treating it as absent: ${String(error)}`
|
|
94
|
+
);
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
return undefined;
|
package/package.json
CHANGED
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"zod-validation-error": "^4.0.0"
|
|
150
150
|
},
|
|
151
151
|
"name": "@codyswann/lisa",
|
|
152
|
-
"version": "4.10.
|
|
152
|
+
"version": "4.10.2",
|
|
153
153
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
154
154
|
"main": "dist/index.js",
|
|
155
155
|
"exports": {
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
# Gated on the project's configured tracker: this only writes anything when
|
|
7
7
|
# `.lisa.config*.json` declares `tracker: "jira"`. A project on Linear or GitHub
|
|
8
8
|
# Issues has no use for a jira-cli config, and writing one made a false implicit
|
|
9
|
-
# claim about which tracker the project runs on. The gate fails closed —
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# claim about which tracker the project runs on. The gate fails closed — a
|
|
10
|
+
# tracker that cannot be READ now says so on stderr and exits non-zero, rather
|
|
11
|
+
# than exiting 0 as though the project had declared nothing.
|
|
12
12
|
#
|
|
13
13
|
# Required env vars (must be created in your Claude Code Web environment):
|
|
14
14
|
# JIRA_INSTALLATION - cloud or local
|
|
@@ -26,36 +26,95 @@ set -euo pipefail
|
|
|
26
26
|
# Drain the hook envelope before any early return.
|
|
27
27
|
cat >/dev/null 2>&1 || true
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# WHERE THE CONFIG IS READ FROM.
|
|
31
|
+
#
|
|
32
|
+
# The write target has always been absolute (`${PROJECT_DIR}/.lisa/jira-cli`)
|
|
33
|
+
# while the reads were relative to the process working directory. A session
|
|
34
|
+
# launched anywhere but the project root therefore read a different directory
|
|
35
|
+
# than it wrote, and `jq` on a missing file yields empty, which every consumer
|
|
36
|
+
# below treats as "not configured" — so the hook reported success having
|
|
37
|
+
# configured nothing. Both ends now resolve against PROJECT_DIR, matching the
|
|
38
|
+
# OpenCode implementation, which has always resolved against the worktree root
|
|
39
|
+
# it is handed.
|
|
40
|
+
#
|
|
41
|
+
# CLAUDE_PROJECT_DIR first (the harness's own declaration of the root, and
|
|
42
|
+
# inert on harnesses that never set it), then the git toplevel, then pwd. This
|
|
43
|
+
# block is deliberately identical in both shell implementations: two copies of
|
|
44
|
+
# one resolution rule, allowed to drift, is what produced the Codex half of
|
|
45
|
+
# this defect in the first place.
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
|
|
48
|
+
if [[ -z "${PROJECT_DIR}" || ! -d "${PROJECT_DIR}" ]]; then
|
|
49
|
+
PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
50
|
+
fi
|
|
51
|
+
LISA_CONFIG_LOCAL="${PROJECT_DIR}/.lisa.config.local.json"
|
|
52
|
+
LISA_CONFIG_MAIN="${PROJECT_DIR}/.lisa.config.json"
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
# A project with no Lisa config at all is genuinely unconfigured: nothing to
|
|
55
|
+
# read, nothing to write. That case exits silently, exactly as it always has —
|
|
56
|
+
# it is the control that keeps this change from being a behavior change for
|
|
57
|
+
# every non-Lisa directory a harness might launch in.
|
|
58
|
+
if [[ ! -f "${LISA_CONFIG_LOCAL}" && ! -f "${LISA_CONFIG_MAIN}" ]]; then
|
|
59
|
+
exit 0
|
|
60
|
+
fi
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
62
|
+
# FAIL CLOSED FROM HERE. A config file exists, so anything that stops us
|
|
63
|
+
# reading it is a failure, not an answer. Reporting "no tracker configured"
|
|
64
|
+
# because jq is absent or the JSON is malformed is the silent-inert-control
|
|
65
|
+
# failure this hook family keeps producing; it is now visible on stderr and
|
|
66
|
+
# non-zero rather than an exit 0 that claims the project configured nothing.
|
|
67
|
+
if ! command -v jq &>/dev/null; then
|
|
68
|
+
printf '%s\n' \
|
|
69
|
+
"setup-jira-cli: cannot read ${LISA_CONFIG_MAIN}: jq is not installed." \
|
|
70
|
+
"setup-jira-cli: refusing to report this project as unconfigured. Install jq." >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
for lisa_config_candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
75
|
+
if [[ -f "${lisa_config_candidate}" ]] &&
|
|
76
|
+
! jq empty "${lisa_config_candidate}" >/dev/null 2>&1; then
|
|
77
|
+
printf '%s\n' \
|
|
78
|
+
"setup-jira-cli: ${lisa_config_candidate} is not valid JSON." \
|
|
79
|
+
"setup-jira-cli: refusing to report this project as unconfigured." >&2
|
|
80
|
+
exit 1
|
|
43
81
|
fi
|
|
82
|
+
done
|
|
44
83
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
# Reads the first non-empty value for `query` across the local override and the
|
|
85
|
+
# committed config, both resolved from PROJECT_DIR. A jq failure returns
|
|
86
|
+
# non-zero so the caller can fail closed; it never degrades to an empty string.
|
|
87
|
+
read_lisa_config() {
|
|
88
|
+
local query="$1"
|
|
89
|
+
local value=""
|
|
90
|
+
local candidate
|
|
91
|
+
|
|
92
|
+
for candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
93
|
+
[[ -f "${candidate}" ]] || continue
|
|
94
|
+
if ! value="$(jq -r "${query} // empty" "${candidate}")"; then
|
|
95
|
+
printf '%s\n' \
|
|
96
|
+
"setup-jira-cli: failed to read ${query} from ${candidate}." >&2
|
|
97
|
+
return 1
|
|
98
|
+
fi
|
|
99
|
+
[[ -n "${value}" ]] && break
|
|
100
|
+
done
|
|
48
101
|
|
|
49
102
|
printf '%s' "${value}"
|
|
50
103
|
}
|
|
51
104
|
|
|
52
|
-
# Tracker gate: do nothing unless this project actually runs on JIRA.
|
|
53
|
-
|
|
105
|
+
# Tracker gate: do nothing unless this project actually runs on JIRA. An
|
|
106
|
+
# unreadable tracker is a hard stop, not a "no".
|
|
107
|
+
if ! LISA_TRACKER="$(read_lisa_config '.tracker')"; then
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
if [[ "${LISA_TRACKER}" != "jira" ]]; then
|
|
54
111
|
exit 0
|
|
55
112
|
fi
|
|
56
113
|
|
|
57
114
|
if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
58
|
-
ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"
|
|
115
|
+
if ! ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"; then
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
59
118
|
if [[ -n "${ATLASSIAN_SITE}" ]]; then
|
|
60
119
|
if [[ "${ATLASSIAN_SITE}" == http://* || "${ATLASSIAN_SITE}" == https://* ]]; then
|
|
61
120
|
JIRA_SERVER="${ATLASSIAN_SITE}"
|
|
@@ -66,10 +125,17 @@ if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
|
66
125
|
fi
|
|
67
126
|
|
|
68
127
|
if [[ -z "${JIRA_PROJECT:-}" ]]; then
|
|
69
|
-
JIRA_PROJECT="$(read_lisa_config '.jira.project')"
|
|
128
|
+
if ! JIRA_PROJECT="$(read_lisa_config '.jira.project')"; then
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
70
131
|
fi
|
|
71
132
|
|
|
72
|
-
|
|
133
|
+
CONFIG_DIR="${PROJECT_DIR}/.lisa/jira-cli"
|
|
134
|
+
CONFIG_FILE="${CONFIG_DIR}/.config.yml"
|
|
135
|
+
|
|
136
|
+
# Skip config write if required vars are missing. These are identity/secret
|
|
137
|
+
# values supplied by the environment, never by .lisa.config*.json, so their
|
|
138
|
+
# absence is an unconfigured environment rather than an unreadable one.
|
|
73
139
|
if [[ -z "${JIRA_SERVER:-}" || -z "${JIRA_LOGIN:-}" ]]; then
|
|
74
140
|
exit 0
|
|
75
141
|
fi
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
# Gated on the project's configured tracker: this only writes anything when
|
|
7
7
|
# `.lisa.config*.json` declares `tracker: "jira"`. A project on Linear or GitHub
|
|
8
8
|
# Issues has no use for a jira-cli config, and writing one made a false implicit
|
|
9
|
-
# claim about which tracker the project runs on. The gate fails closed —
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# claim about which tracker the project runs on. The gate fails closed — a
|
|
10
|
+
# tracker that cannot be READ now says so on stderr and exits non-zero, rather
|
|
11
|
+
# than exiting 0 as though the project had declared nothing.
|
|
12
12
|
#
|
|
13
13
|
# Required env vars (must be created in your Claude Code Web environment):
|
|
14
14
|
# JIRA_INSTALLATION - cloud or local
|
|
@@ -26,36 +26,95 @@ set -euo pipefail
|
|
|
26
26
|
# Drain the hook envelope before any early return.
|
|
27
27
|
cat >/dev/null 2>&1 || true
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# WHERE THE CONFIG IS READ FROM.
|
|
31
|
+
#
|
|
32
|
+
# The write target has always been absolute (`${PROJECT_DIR}/.lisa/jira-cli`)
|
|
33
|
+
# while the reads were relative to the process working directory. A session
|
|
34
|
+
# launched anywhere but the project root therefore read a different directory
|
|
35
|
+
# than it wrote, and `jq` on a missing file yields empty, which every consumer
|
|
36
|
+
# below treats as "not configured" — so the hook reported success having
|
|
37
|
+
# configured nothing. Both ends now resolve against PROJECT_DIR, matching the
|
|
38
|
+
# OpenCode implementation, which has always resolved against the worktree root
|
|
39
|
+
# it is handed.
|
|
40
|
+
#
|
|
41
|
+
# CLAUDE_PROJECT_DIR first (the harness's own declaration of the root, and
|
|
42
|
+
# inert on harnesses that never set it), then the git toplevel, then pwd. This
|
|
43
|
+
# block is deliberately identical in both shell implementations: two copies of
|
|
44
|
+
# one resolution rule, allowed to drift, is what produced the Codex half of
|
|
45
|
+
# this defect in the first place.
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
|
|
48
|
+
if [[ -z "${PROJECT_DIR}" || ! -d "${PROJECT_DIR}" ]]; then
|
|
49
|
+
PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
50
|
+
fi
|
|
51
|
+
LISA_CONFIG_LOCAL="${PROJECT_DIR}/.lisa.config.local.json"
|
|
52
|
+
LISA_CONFIG_MAIN="${PROJECT_DIR}/.lisa.config.json"
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
# A project with no Lisa config at all is genuinely unconfigured: nothing to
|
|
55
|
+
# read, nothing to write. That case exits silently, exactly as it always has —
|
|
56
|
+
# it is the control that keeps this change from being a behavior change for
|
|
57
|
+
# every non-Lisa directory a harness might launch in.
|
|
58
|
+
if [[ ! -f "${LISA_CONFIG_LOCAL}" && ! -f "${LISA_CONFIG_MAIN}" ]]; then
|
|
59
|
+
exit 0
|
|
60
|
+
fi
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
62
|
+
# FAIL CLOSED FROM HERE. A config file exists, so anything that stops us
|
|
63
|
+
# reading it is a failure, not an answer. Reporting "no tracker configured"
|
|
64
|
+
# because jq is absent or the JSON is malformed is the silent-inert-control
|
|
65
|
+
# failure this hook family keeps producing; it is now visible on stderr and
|
|
66
|
+
# non-zero rather than an exit 0 that claims the project configured nothing.
|
|
67
|
+
if ! command -v jq &>/dev/null; then
|
|
68
|
+
printf '%s\n' \
|
|
69
|
+
"setup-jira-cli: cannot read ${LISA_CONFIG_MAIN}: jq is not installed." \
|
|
70
|
+
"setup-jira-cli: refusing to report this project as unconfigured. Install jq." >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
for lisa_config_candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
75
|
+
if [[ -f "${lisa_config_candidate}" ]] &&
|
|
76
|
+
! jq empty "${lisa_config_candidate}" >/dev/null 2>&1; then
|
|
77
|
+
printf '%s\n' \
|
|
78
|
+
"setup-jira-cli: ${lisa_config_candidate} is not valid JSON." \
|
|
79
|
+
"setup-jira-cli: refusing to report this project as unconfigured." >&2
|
|
80
|
+
exit 1
|
|
43
81
|
fi
|
|
82
|
+
done
|
|
44
83
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
# Reads the first non-empty value for `query` across the local override and the
|
|
85
|
+
# committed config, both resolved from PROJECT_DIR. A jq failure returns
|
|
86
|
+
# non-zero so the caller can fail closed; it never degrades to an empty string.
|
|
87
|
+
read_lisa_config() {
|
|
88
|
+
local query="$1"
|
|
89
|
+
local value=""
|
|
90
|
+
local candidate
|
|
91
|
+
|
|
92
|
+
for candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
93
|
+
[[ -f "${candidate}" ]] || continue
|
|
94
|
+
if ! value="$(jq -r "${query} // empty" "${candidate}")"; then
|
|
95
|
+
printf '%s\n' \
|
|
96
|
+
"setup-jira-cli: failed to read ${query} from ${candidate}." >&2
|
|
97
|
+
return 1
|
|
98
|
+
fi
|
|
99
|
+
[[ -n "${value}" ]] && break
|
|
100
|
+
done
|
|
48
101
|
|
|
49
102
|
printf '%s' "${value}"
|
|
50
103
|
}
|
|
51
104
|
|
|
52
|
-
# Tracker gate: do nothing unless this project actually runs on JIRA.
|
|
53
|
-
|
|
105
|
+
# Tracker gate: do nothing unless this project actually runs on JIRA. An
|
|
106
|
+
# unreadable tracker is a hard stop, not a "no".
|
|
107
|
+
if ! LISA_TRACKER="$(read_lisa_config '.tracker')"; then
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
if [[ "${LISA_TRACKER}" != "jira" ]]; then
|
|
54
111
|
exit 0
|
|
55
112
|
fi
|
|
56
113
|
|
|
57
114
|
if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
58
|
-
ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"
|
|
115
|
+
if ! ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"; then
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
59
118
|
if [[ -n "${ATLASSIAN_SITE}" ]]; then
|
|
60
119
|
if [[ "${ATLASSIAN_SITE}" == http://* || "${ATLASSIAN_SITE}" == https://* ]]; then
|
|
61
120
|
JIRA_SERVER="${ATLASSIAN_SITE}"
|
|
@@ -66,10 +125,17 @@ if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
|
66
125
|
fi
|
|
67
126
|
|
|
68
127
|
if [[ -z "${JIRA_PROJECT:-}" ]]; then
|
|
69
|
-
JIRA_PROJECT="$(read_lisa_config '.jira.project')"
|
|
128
|
+
if ! JIRA_PROJECT="$(read_lisa_config '.jira.project')"; then
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
70
131
|
fi
|
|
71
132
|
|
|
72
|
-
|
|
133
|
+
CONFIG_DIR="${PROJECT_DIR}/.lisa/jira-cli"
|
|
134
|
+
CONFIG_FILE="${CONFIG_DIR}/.config.yml"
|
|
135
|
+
|
|
136
|
+
# Skip config write if required vars are missing. These are identity/secret
|
|
137
|
+
# values supplied by the environment, never by .lisa.config*.json, so their
|
|
138
|
+
# absence is an unconfigured environment rather than an unreadable one.
|
|
73
139
|
if [[ -z "${JIRA_SERVER:-}" || -z "${JIRA_LOGIN:-}" ]]; then
|
|
74
140
|
exit 0
|
|
75
141
|
fi
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
# Gated on the project's configured tracker: this only writes anything when
|
|
7
7
|
# `.lisa.config*.json` declares `tracker: "jira"`. A project on Linear or GitHub
|
|
8
8
|
# Issues has no use for a jira-cli config, and writing one made a false implicit
|
|
9
|
-
# claim about which tracker the project runs on. The gate fails closed —
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# claim about which tracker the project runs on. The gate fails closed — a
|
|
10
|
+
# tracker that cannot be READ now says so on stderr and exits non-zero, rather
|
|
11
|
+
# than exiting 0 as though the project had declared nothing.
|
|
12
12
|
#
|
|
13
13
|
# Required env vars (must be created in your Claude Code Web environment):
|
|
14
14
|
# JIRA_INSTALLATION - cloud or local
|
|
@@ -26,36 +26,95 @@ set -euo pipefail
|
|
|
26
26
|
# Drain the hook envelope before any early return.
|
|
27
27
|
cat >/dev/null 2>&1 || true
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# WHERE THE CONFIG IS READ FROM.
|
|
31
|
+
#
|
|
32
|
+
# The write target has always been absolute (`${PROJECT_DIR}/.lisa/jira-cli`)
|
|
33
|
+
# while the reads were relative to the process working directory. A session
|
|
34
|
+
# launched anywhere but the project root therefore read a different directory
|
|
35
|
+
# than it wrote, and `jq` on a missing file yields empty, which every consumer
|
|
36
|
+
# below treats as "not configured" — so the hook reported success having
|
|
37
|
+
# configured nothing. Both ends now resolve against PROJECT_DIR, matching the
|
|
38
|
+
# OpenCode implementation, which has always resolved against the worktree root
|
|
39
|
+
# it is handed.
|
|
40
|
+
#
|
|
41
|
+
# CLAUDE_PROJECT_DIR first (the harness's own declaration of the root, and
|
|
42
|
+
# inert on harnesses that never set it), then the git toplevel, then pwd. This
|
|
43
|
+
# block is deliberately identical in both shell implementations: two copies of
|
|
44
|
+
# one resolution rule, allowed to drift, is what produced the Codex half of
|
|
45
|
+
# this defect in the first place.
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
|
|
48
|
+
if [[ -z "${PROJECT_DIR}" || ! -d "${PROJECT_DIR}" ]]; then
|
|
49
|
+
PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
50
|
+
fi
|
|
51
|
+
LISA_CONFIG_LOCAL="${PROJECT_DIR}/.lisa.config.local.json"
|
|
52
|
+
LISA_CONFIG_MAIN="${PROJECT_DIR}/.lisa.config.json"
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
# A project with no Lisa config at all is genuinely unconfigured: nothing to
|
|
55
|
+
# read, nothing to write. That case exits silently, exactly as it always has —
|
|
56
|
+
# it is the control that keeps this change from being a behavior change for
|
|
57
|
+
# every non-Lisa directory a harness might launch in.
|
|
58
|
+
if [[ ! -f "${LISA_CONFIG_LOCAL}" && ! -f "${LISA_CONFIG_MAIN}" ]]; then
|
|
59
|
+
exit 0
|
|
60
|
+
fi
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
62
|
+
# FAIL CLOSED FROM HERE. A config file exists, so anything that stops us
|
|
63
|
+
# reading it is a failure, not an answer. Reporting "no tracker configured"
|
|
64
|
+
# because jq is absent or the JSON is malformed is the silent-inert-control
|
|
65
|
+
# failure this hook family keeps producing; it is now visible on stderr and
|
|
66
|
+
# non-zero rather than an exit 0 that claims the project configured nothing.
|
|
67
|
+
if ! command -v jq &>/dev/null; then
|
|
68
|
+
printf '%s\n' \
|
|
69
|
+
"setup-jira-cli: cannot read ${LISA_CONFIG_MAIN}: jq is not installed." \
|
|
70
|
+
"setup-jira-cli: refusing to report this project as unconfigured. Install jq." >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
for lisa_config_candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
75
|
+
if [[ -f "${lisa_config_candidate}" ]] &&
|
|
76
|
+
! jq empty "${lisa_config_candidate}" >/dev/null 2>&1; then
|
|
77
|
+
printf '%s\n' \
|
|
78
|
+
"setup-jira-cli: ${lisa_config_candidate} is not valid JSON." \
|
|
79
|
+
"setup-jira-cli: refusing to report this project as unconfigured." >&2
|
|
80
|
+
exit 1
|
|
43
81
|
fi
|
|
82
|
+
done
|
|
44
83
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
# Reads the first non-empty value for `query` across the local override and the
|
|
85
|
+
# committed config, both resolved from PROJECT_DIR. A jq failure returns
|
|
86
|
+
# non-zero so the caller can fail closed; it never degrades to an empty string.
|
|
87
|
+
read_lisa_config() {
|
|
88
|
+
local query="$1"
|
|
89
|
+
local value=""
|
|
90
|
+
local candidate
|
|
91
|
+
|
|
92
|
+
for candidate in "${LISA_CONFIG_LOCAL}" "${LISA_CONFIG_MAIN}"; do
|
|
93
|
+
[[ -f "${candidate}" ]] || continue
|
|
94
|
+
if ! value="$(jq -r "${query} // empty" "${candidate}")"; then
|
|
95
|
+
printf '%s\n' \
|
|
96
|
+
"setup-jira-cli: failed to read ${query} from ${candidate}." >&2
|
|
97
|
+
return 1
|
|
98
|
+
fi
|
|
99
|
+
[[ -n "${value}" ]] && break
|
|
100
|
+
done
|
|
48
101
|
|
|
49
102
|
printf '%s' "${value}"
|
|
50
103
|
}
|
|
51
104
|
|
|
52
|
-
# Tracker gate: do nothing unless this project actually runs on JIRA.
|
|
53
|
-
|
|
105
|
+
# Tracker gate: do nothing unless this project actually runs on JIRA. An
|
|
106
|
+
# unreadable tracker is a hard stop, not a "no".
|
|
107
|
+
if ! LISA_TRACKER="$(read_lisa_config '.tracker')"; then
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
if [[ "${LISA_TRACKER}" != "jira" ]]; then
|
|
54
111
|
exit 0
|
|
55
112
|
fi
|
|
56
113
|
|
|
57
114
|
if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
58
|
-
ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"
|
|
115
|
+
if ! ATLASSIAN_SITE="$(read_lisa_config '.atlassian.site')"; then
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
59
118
|
if [[ -n "${ATLASSIAN_SITE}" ]]; then
|
|
60
119
|
if [[ "${ATLASSIAN_SITE}" == http://* || "${ATLASSIAN_SITE}" == https://* ]]; then
|
|
61
120
|
JIRA_SERVER="${ATLASSIAN_SITE}"
|
|
@@ -66,10 +125,17 @@ if [[ -z "${JIRA_SERVER:-}" ]]; then
|
|
|
66
125
|
fi
|
|
67
126
|
|
|
68
127
|
if [[ -z "${JIRA_PROJECT:-}" ]]; then
|
|
69
|
-
JIRA_PROJECT="$(read_lisa_config '.jira.project')"
|
|
128
|
+
if ! JIRA_PROJECT="$(read_lisa_config '.jira.project')"; then
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
70
131
|
fi
|
|
71
132
|
|
|
72
|
-
|
|
133
|
+
CONFIG_DIR="${PROJECT_DIR}/.lisa/jira-cli"
|
|
134
|
+
CONFIG_FILE="${CONFIG_DIR}/.config.yml"
|
|
135
|
+
|
|
136
|
+
# Skip config write if required vars are missing. These are identity/secret
|
|
137
|
+
# values supplied by the environment, never by .lisa.config*.json, so their
|
|
138
|
+
# absence is an unconfigured environment rather than an unreadable one.
|
|
73
139
|
if [[ -z "${JIRA_SERVER:-}" || -z "${JIRA_LOGIN:-}" ]]; then
|
|
74
140
|
exit 0
|
|
75
141
|
fi
|