hub-launch 1.16.0 → 1.18.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/CHANGELOG.md +14 -0
- package/README.md +102 -2
- package/dist/commands/init.d.ts +61 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +125 -114
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/instructions.d.ts +17 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +51 -0
- package/dist/commands/instructions.js.map +1 -0
- package/dist/commands/launch.d.ts +21 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +61 -0
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/script.d.ts +28 -0
- package/dist/commands/script.d.ts.map +1 -0
- package/dist/commands/script.js +69 -0
- package/dist/commands/script.js.map +1 -0
- package/dist/commands/session-hook.d.ts +26 -0
- package/dist/commands/session-hook.d.ts.map +1 -0
- package/dist/commands/session-hook.js +146 -0
- package/dist/commands/session-hook.js.map +1 -0
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/scripts/fix-commit.d.ts +2 -0
- package/dist/scripts/fix-commit.d.ts.map +1 -0
- package/dist/scripts/fix-commit.js +125 -0
- package/dist/scripts/fix-commit.js.map +1 -0
- package/dist/scripts/fix-setup.d.ts +2 -0
- package/dist/scripts/fix-setup.d.ts.map +1 -0
- package/dist/scripts/fix-setup.js +161 -0
- package/dist/scripts/fix-setup.js.map +1 -0
- package/dist/scripts/launch-run.d.ts +14 -0
- package/dist/scripts/launch-run.d.ts.map +1 -0
- package/dist/scripts/launch-run.js +117 -0
- package/dist/scripts/launch-run.js.map +1 -0
- package/dist/scripts/lib/exec.d.ts +48 -0
- package/dist/scripts/lib/exec.d.ts.map +1 -0
- package/dist/scripts/lib/exec.js +61 -0
- package/dist/scripts/lib/exec.js.map +1 -0
- package/dist/scripts/lib/io.d.ts +48 -0
- package/dist/scripts/lib/io.d.ts.map +1 -0
- package/dist/scripts/lib/io.js +72 -0
- package/dist/scripts/lib/io.js.map +1 -0
- package/dist/scripts/lib/read-config.d.ts +26 -0
- package/dist/scripts/lib/read-config.d.ts.map +1 -0
- package/dist/scripts/lib/read-config.js +65 -0
- package/dist/scripts/lib/read-config.js.map +1 -0
- package/dist/scripts/merge-local.d.ts +2 -0
- package/dist/scripts/merge-local.d.ts.map +1 -0
- package/dist/scripts/merge-local.js +146 -0
- package/dist/scripts/merge-local.js.map +1 -0
- package/dist/scripts/merge-remote.d.ts +12 -0
- package/dist/scripts/merge-remote.d.ts.map +1 -0
- package/dist/scripts/merge-remote.js +71 -0
- package/dist/scripts/merge-remote.js.map +1 -0
- package/dist/scripts/schedule-manage.d.ts +2 -0
- package/dist/scripts/schedule-manage.d.ts.map +1 -0
- package/dist/scripts/schedule-manage.js +151 -0
- package/dist/scripts/schedule-manage.js.map +1 -0
- package/dist/scripts/schedule-run.d.ts +2 -0
- package/dist/scripts/schedule-run.d.ts.map +1 -0
- package/dist/scripts/schedule-run.js +146 -0
- package/dist/scripts/schedule-run.js.map +1 -0
- package/dist/scripts/verify-gather.d.ts +2 -0
- package/dist/scripts/verify-gather.d.ts.map +1 -0
- package/dist/scripts/verify-gather.js +206 -0
- package/dist/scripts/verify-gather.js.map +1 -0
- package/dist/scripts/verify-post.d.ts +12 -0
- package/dist/scripts/verify-post.d.ts.map +1 -0
- package/dist/scripts/verify-post.js +91 -0
- package/dist/scripts/verify-post.js.map +1 -0
- package/dist/templates/planning-instructions.md +7 -7
- package/dist/templates/skill-creation-instructions.md +2 -2
- package/dist/templates/skills/hula-confirm/SKILL.md +3 -3
- package/dist/templates/skills/hula-fix/SKILL.md +3 -3
- package/dist/templates/skills/hula-help/SKILL.md +117 -0
- package/dist/templates/skills/hula-launch/SKILL.md +14 -8
- package/dist/templates/skills/hula-merge/SKILL.md +2 -2
- package/dist/templates/skills/hula-plan/SKILL.md +5 -5
- package/dist/templates/skills/hula-schedule/SKILL.md +16 -16
- package/dist/templates/skills/hula-verify/SKILL.md +32 -33
- package/dist/types/config.schema.d.ts +235 -0
- package/dist/types/config.schema.d.ts.map +1 -1
- package/dist/types/config.schema.js +66 -0
- package/dist/types/config.schema.js.map +1 -1
- package/dist/utils/client-session.d.ts +2 -2
- package/dist/utils/client-session.js +2 -2
- package/package.json +6 -5
- package/scripts/postinstall.mjs +102 -0
- package/dist/templates/scripts/hula-fix-commit.sh +0 -114
- package/dist/templates/scripts/hula-fix-setup.sh +0 -152
- package/dist/templates/scripts/hula-launch-run.sh +0 -140
- package/dist/templates/scripts/hula-merge-local.sh +0 -138
- package/dist/templates/scripts/hula-merge-remote.sh +0 -94
- package/dist/templates/scripts/hula-read-config.sh +0 -38
- package/dist/templates/scripts/hula-schedule-manage.sh +0 -159
- package/dist/templates/scripts/hula-schedule-run.sh +0 -148
- package/dist/templates/scripts/hula-session-hook.sh +0 -68
- package/dist/templates/scripts/hula-verify-gather.sh +0 -135
- package/dist/templates/scripts/hula-verify-post.sh +0 -84
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hula-schedule-manage.sh — management surface for the /hula-schedule skill
|
|
3
|
-
# Usage (exactly one action per call):
|
|
4
|
-
# bash .github/scripts/hula-schedule-manage.sh --list
|
|
5
|
-
# bash .github/scripts/hula-schedule-manage.sh --list-schedules
|
|
6
|
-
# bash .github/scripts/hula-schedule-manage.sh --show <runId>
|
|
7
|
-
# bash .github/scripts/hula-schedule-manage.sh --run-now <scheduleId>
|
|
8
|
-
# bash .github/scripts/hula-schedule-manage.sh --cancel-schedule <scheduleId>
|
|
9
|
-
# bash .github/scripts/hula-schedule-manage.sh --publish-skill <repo-relative-path>
|
|
10
|
-
# bash .github/scripts/hula-schedule-manage.sh --delete-skill <repo-relative-path>
|
|
11
|
-
#
|
|
12
|
-
# Each invocation maps to the corresponding `hula schedule …` call, captures its
|
|
13
|
-
# output, and emits a single structured JSON object to stdout; all other output
|
|
14
|
-
# goes to stderr.
|
|
15
|
-
#
|
|
16
|
-
# JSON contract (stdout):
|
|
17
|
-
# read verbs : {"status":"success","kind":"list|list-schedules|show","cliOutput":"…"}
|
|
18
|
-
# run-now : {"status":"success","kind":"run-now","runId":"…","cliOutput":"…"}
|
|
19
|
-
# cancel : {"status":"success","kind":"cancel-schedule","scheduleId":"…","cliOutput":"…"}
|
|
20
|
-
# publish-skill : {"status":"success","kind":"publish-skill","path":"…","cliOutput":"…"}
|
|
21
|
-
# delete-skill : {"status":"success","kind":"delete-skill","path":"…","cliOutput":"…"}
|
|
22
|
-
# failure : {"status":"error","code":<n>,"message":"…"}
|
|
23
|
-
#
|
|
24
|
-
# Exit codes: 0=success, 1=user error, 2=tool error
|
|
25
|
-
#
|
|
26
|
-
# Security: never prints secrets. Credentials are resolved inside the CLI from
|
|
27
|
-
# flags/config/env — they are NOT passed by this script. --publish-skill and
|
|
28
|
-
# --delete-skill reject path traversal and require a .hublaunch/skills/ path.
|
|
29
|
-
|
|
30
|
-
set -euo pipefail
|
|
31
|
-
|
|
32
|
-
# ── helpers ────────────────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
json_escape() {
|
|
35
|
-
if command -v jq &>/dev/null; then
|
|
36
|
-
# jq -Rs . reads stdin as a raw string and outputs a JSON string (with surrounding quotes).
|
|
37
|
-
# Strip the surrounding quotes; the escaped content is safe to embed in JSON.
|
|
38
|
-
printf '%s' "$1" | jq -Rs . | sed 's/^"//;s/"$//'
|
|
39
|
-
else
|
|
40
|
-
# Fallback: escape backslashes and double quotes, collapse control chars to spaces
|
|
41
|
-
printf '%s' "$1" \
|
|
42
|
-
| sed 's/\\/\\\\/g; s/"/\\"/g' \
|
|
43
|
-
| tr '\n\r\t' ' '
|
|
44
|
-
fi
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
die() {
|
|
48
|
-
local code="$1"; shift
|
|
49
|
-
local safe_msg
|
|
50
|
-
safe_msg=$(json_escape "$*")
|
|
51
|
-
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
52
|
-
exit "$code"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# Validate a skill path: must be repo-relative, under .hublaunch/skills/, no traversal.
|
|
56
|
-
validate_skill_path() {
|
|
57
|
-
local p="$1"
|
|
58
|
-
case "$p" in
|
|
59
|
-
/*) die 1 "Skill path must be repo-relative, not absolute: ${p}" ;;
|
|
60
|
-
esac
|
|
61
|
-
if printf '%s' "$p" | grep -q '\.\.'; then
|
|
62
|
-
die 1 "Invalid skill path (path traversal not allowed): ${p}"
|
|
63
|
-
fi
|
|
64
|
-
case "$p" in
|
|
65
|
-
.hublaunch/skills/*) : ;;
|
|
66
|
-
*) die 1 "Skill path must be under .hublaunch/skills/ — got ${p}" ;;
|
|
67
|
-
esac
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
71
|
-
# Exactly one action flag is allowed per call.
|
|
72
|
-
|
|
73
|
-
ACTION=''
|
|
74
|
-
ARG=''
|
|
75
|
-
|
|
76
|
-
set_action() {
|
|
77
|
-
if [[ -n "$ACTION" ]]; then
|
|
78
|
-
die 1 "Provide exactly one action (got --$ACTION and $1)."
|
|
79
|
-
fi
|
|
80
|
-
ACTION="$1"
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
while [[ $# -gt 0 ]]; do
|
|
84
|
-
case "$1" in
|
|
85
|
-
--list) set_action 'list'; shift ;;
|
|
86
|
-
--list-schedules) set_action 'list-schedules'; shift ;;
|
|
87
|
-
--show) set_action 'show'; ARG="$2"; shift 2 ;;
|
|
88
|
-
--show=*) set_action 'show'; ARG="${1#--show=}"; shift ;;
|
|
89
|
-
--run-now) set_action 'run-now'; ARG="$2"; shift 2 ;;
|
|
90
|
-
--run-now=*) set_action 'run-now'; ARG="${1#--run-now=}"; shift ;;
|
|
91
|
-
--cancel-schedule) set_action 'cancel-schedule'; ARG="$2"; shift 2 ;;
|
|
92
|
-
--cancel-schedule=*) set_action 'cancel-schedule'; ARG="${1#--cancel-schedule=}"; shift ;;
|
|
93
|
-
--publish-skill) set_action 'publish-skill'; ARG="$2"; shift 2 ;;
|
|
94
|
-
--publish-skill=*) set_action 'publish-skill'; ARG="${1#--publish-skill=}"; shift ;;
|
|
95
|
-
--delete-skill) set_action 'delete-skill'; ARG="$2"; shift 2 ;;
|
|
96
|
-
--delete-skill=*) set_action 'delete-skill'; ARG="${1#--delete-skill=}"; shift ;;
|
|
97
|
-
*) die 1 "Unexpected argument: $1" ;;
|
|
98
|
-
esac
|
|
99
|
-
done
|
|
100
|
-
|
|
101
|
-
if [[ -z "$ACTION" ]]; then
|
|
102
|
-
die 1 "Usage: bash .github/scripts/hula-schedule-manage.sh (--list | --list-schedules | --show <id> | --run-now <id> | --cancel-schedule <id> | --publish-skill <path> | --delete-skill <path>)"
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
# Actions that require an argument.
|
|
106
|
-
case "$ACTION" in
|
|
107
|
-
show|run-now|cancel-schedule|publish-skill|delete-skill)
|
|
108
|
-
[[ -n "$ARG" ]] || die 1 "--$ACTION requires an argument." ;;
|
|
109
|
-
esac
|
|
110
|
-
|
|
111
|
-
# Path validation for skill file actions.
|
|
112
|
-
case "$ACTION" in
|
|
113
|
-
publish-skill|delete-skill) validate_skill_path "$ARG" ;;
|
|
114
|
-
esac
|
|
115
|
-
|
|
116
|
-
# ── Build and run the hula schedule invocation ────────────────────────────────
|
|
117
|
-
|
|
118
|
-
CLI_ARGS=()
|
|
119
|
-
case "$ACTION" in
|
|
120
|
-
list) CLI_ARGS=(--list) ;;
|
|
121
|
-
list-schedules) CLI_ARGS=(--list-schedules) ;;
|
|
122
|
-
show) CLI_ARGS=(--show "$ARG") ;;
|
|
123
|
-
run-now) CLI_ARGS=(--run-now "$ARG") ;;
|
|
124
|
-
cancel-schedule) CLI_ARGS=(--cancel-schedule "$ARG") ;;
|
|
125
|
-
publish-skill) CLI_ARGS=(--publish-skill "$ARG") ;;
|
|
126
|
-
delete-skill) CLI_ARGS=(--delete-skill "$ARG") ;;
|
|
127
|
-
esac
|
|
128
|
-
|
|
129
|
-
printf '⚙️ hula schedule --%s...\n' "$ACTION" >&2
|
|
130
|
-
|
|
131
|
-
OUTPUT=$(hula schedule "${CLI_ARGS[@]}" 2>&1) || die 2 "Management action failed: $OUTPUT"
|
|
132
|
-
|
|
133
|
-
SAFE_OUTPUT=$(json_escape "$OUTPUT")
|
|
134
|
-
|
|
135
|
-
# ── Emit the structured JSON result ──────────────────────────────────────────
|
|
136
|
-
|
|
137
|
-
case "$ACTION" in
|
|
138
|
-
list|list-schedules|show)
|
|
139
|
-
printf '{"status":"success","kind":"%s","cliOutput":"%s"}\n' \
|
|
140
|
-
"$ACTION" "$SAFE_OUTPUT"
|
|
141
|
-
;;
|
|
142
|
-
run-now)
|
|
143
|
-
# The CLI prints " ID: <id>" for the new run.
|
|
144
|
-
RUN_ID=$(printf '%s' "$OUTPUT" | grep -E '^\s*ID:' | head -1 | sed -E 's/^\s*ID:\s*//') || true
|
|
145
|
-
SAFE_ID=$(json_escape "$RUN_ID")
|
|
146
|
-
printf '{"status":"success","kind":"run-now","runId":"%s","cliOutput":"%s"}\n' \
|
|
147
|
-
"$SAFE_ID" "$SAFE_OUTPUT"
|
|
148
|
-
;;
|
|
149
|
-
cancel-schedule)
|
|
150
|
-
SAFE_ID=$(json_escape "$ARG")
|
|
151
|
-
printf '{"status":"success","kind":"cancel-schedule","scheduleId":"%s","cliOutput":"%s"}\n' \
|
|
152
|
-
"$SAFE_ID" "$SAFE_OUTPUT"
|
|
153
|
-
;;
|
|
154
|
-
publish-skill|delete-skill)
|
|
155
|
-
SAFE_PATH=$(json_escape "$ARG")
|
|
156
|
-
printf '{"status":"success","kind":"%s","path":"%s","cliOutput":"%s"}\n' \
|
|
157
|
-
"$ACTION" "$SAFE_PATH" "$SAFE_OUTPUT"
|
|
158
|
-
;;
|
|
159
|
-
esac
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hula-schedule-run.sh — runs the hula schedule workflow for the /hula-schedule skill
|
|
3
|
-
# Usage:
|
|
4
|
-
# bash .github/scripts/hula-schedule-run.sh \
|
|
5
|
-
# (--built-in <name> | --action-path <path>) \
|
|
6
|
-
# [--entry-point <path>] [--outcome-type <pr|plan|feedback>] [--schedule "<cron>"] \
|
|
7
|
-
# [--pr-policy <always|skip-if-open|close-previous>] [--update-notification-url <url>]
|
|
8
|
-
#
|
|
9
|
-
# Performs: hula schedule (one-off run, or a recurring schedule when --schedule is given).
|
|
10
|
-
# Outputs a single structured JSON object to stdout; all other output goes to stderr.
|
|
11
|
-
#
|
|
12
|
-
# JSON contract (stdout):
|
|
13
|
-
# one-off run : {"status":"success","kind":"run","runId":"…","prUrl":"…","cliOutput":"…"}
|
|
14
|
-
# schedule : {"status":"success","kind":"schedule","scheduleId":"…","cronExpr":"…","cliOutput":"…"}
|
|
15
|
-
# failure : {"status":"error","code":<n>,"message":"…"}
|
|
16
|
-
#
|
|
17
|
-
# Exit codes: 0=success, 1=user error, 2=tool error
|
|
18
|
-
#
|
|
19
|
-
# Security: never prints secrets. Credentials are resolved inside the CLI from
|
|
20
|
-
# flags/config/env — they are NOT passed by this script.
|
|
21
|
-
|
|
22
|
-
set -euo pipefail
|
|
23
|
-
|
|
24
|
-
# ── helpers ────────────────────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
json_escape() {
|
|
27
|
-
if command -v jq &>/dev/null; then
|
|
28
|
-
# jq -Rs . reads stdin as a raw string and outputs a JSON string (with surrounding quotes).
|
|
29
|
-
# Strip the surrounding quotes; the escaped content is safe to embed in JSON.
|
|
30
|
-
printf '%s' "$1" | jq -Rs . | sed 's/^"//;s/"$//'
|
|
31
|
-
else
|
|
32
|
-
# Fallback: escape backslashes and double quotes, collapse control chars to spaces
|
|
33
|
-
printf '%s' "$1" \
|
|
34
|
-
| sed 's/\\/\\\\/g; s/"/\\"/g' \
|
|
35
|
-
| tr '\n\r\t' ' '
|
|
36
|
-
fi
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
die() {
|
|
40
|
-
local code="$1"; shift
|
|
41
|
-
local safe_msg
|
|
42
|
-
safe_msg=$(json_escape "$*")
|
|
43
|
-
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
44
|
-
exit "$code"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
BUILT_IN=''
|
|
50
|
-
ACTION_PATH=''
|
|
51
|
-
ENTRY_POINT=''
|
|
52
|
-
OUTCOME_TYPE=''
|
|
53
|
-
SCHEDULE=''
|
|
54
|
-
PR_POLICY=''
|
|
55
|
-
UPDATE_NOTIFICATION_URL=''
|
|
56
|
-
|
|
57
|
-
while [[ $# -gt 0 ]]; do
|
|
58
|
-
case "$1" in
|
|
59
|
-
--built-in) BUILT_IN="$2"; shift 2 ;;
|
|
60
|
-
--built-in=*) BUILT_IN="${1#--built-in=}"; shift ;;
|
|
61
|
-
--action-path) ACTION_PATH="$2"; shift 2 ;;
|
|
62
|
-
--action-path=*) ACTION_PATH="${1#--action-path=}"; shift ;;
|
|
63
|
-
--entry-point) ENTRY_POINT="$2"; shift 2 ;;
|
|
64
|
-
--entry-point=*) ENTRY_POINT="${1#--entry-point=}"; shift ;;
|
|
65
|
-
--outcome-type) OUTCOME_TYPE="$2"; shift 2 ;;
|
|
66
|
-
--outcome-type=*) OUTCOME_TYPE="${1#--outcome-type=}"; shift ;;
|
|
67
|
-
--schedule) SCHEDULE="$2"; shift 2 ;;
|
|
68
|
-
--schedule=*) SCHEDULE="${1#--schedule=}"; shift ;;
|
|
69
|
-
--pr-policy) PR_POLICY="$2"; shift 2 ;;
|
|
70
|
-
--pr-policy=*) PR_POLICY="${1#--pr-policy=}"; shift ;;
|
|
71
|
-
--update-notification-url) UPDATE_NOTIFICATION_URL="$2"; shift 2 ;;
|
|
72
|
-
--update-notification-url=*) UPDATE_NOTIFICATION_URL="${1#--update-notification-url=}"; shift ;;
|
|
73
|
-
*) die 1 "Unexpected argument: $1" ;;
|
|
74
|
-
esac
|
|
75
|
-
done
|
|
76
|
-
|
|
77
|
-
# Exactly one of --built-in / --action-path is required.
|
|
78
|
-
if [[ -n "$BUILT_IN" && -n "$ACTION_PATH" ]]; then
|
|
79
|
-
die 1 "Provide either --built-in or --action-path, not both."
|
|
80
|
-
fi
|
|
81
|
-
if [[ -z "$BUILT_IN" && -z "$ACTION_PATH" ]]; then
|
|
82
|
-
die 1 "Usage: bash .github/scripts/hula-schedule-run.sh (--built-in <name> | --action-path <path>) [--entry-point <path>] [--outcome-type <pr|plan|feedback>] [--schedule \"<cron>\"] [--pr-policy <always|skip-if-open|close-previous>]"
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
# Reject directory-traversal in --action-path, except for https:// URLs.
|
|
86
|
-
if [[ -n "$ACTION_PATH" ]] && [[ "$ACTION_PATH" != https://* ]]; then
|
|
87
|
-
if printf '%s' "$ACTION_PATH" | grep -q '\.\.'; then
|
|
88
|
-
die 1 "Invalid action path (path traversal not allowed): ${ACTION_PATH}"
|
|
89
|
-
fi
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
# ── Run hula schedule ─────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
ARGS=()
|
|
95
|
-
if [[ -n "$BUILT_IN" ]]; then
|
|
96
|
-
ARGS+=(--built-in "$BUILT_IN")
|
|
97
|
-
else
|
|
98
|
-
ARGS+=(--action-path "$ACTION_PATH")
|
|
99
|
-
fi
|
|
100
|
-
[[ -n "$ENTRY_POINT" ]] && ARGS+=(--entry-point "$ENTRY_POINT")
|
|
101
|
-
[[ -n "$OUTCOME_TYPE" ]] && ARGS+=(--outcome-type "$OUTCOME_TYPE")
|
|
102
|
-
[[ -n "$SCHEDULE" ]] && ARGS+=(--schedule "$SCHEDULE")
|
|
103
|
-
[[ -n "$PR_POLICY" ]] && ARGS+=(--pr-policy "$PR_POLICY")
|
|
104
|
-
[[ -n "$UPDATE_NOTIFICATION_URL" ]] && ARGS+=(--update-notification-url "$UPDATE_NOTIFICATION_URL")
|
|
105
|
-
|
|
106
|
-
if [[ -n "$SCHEDULE" ]]; then
|
|
107
|
-
printf '⏰ Scheduling action (%s)...\n' "$SCHEDULE" >&2
|
|
108
|
-
else
|
|
109
|
-
printf '🚀 Running action...\n' >&2
|
|
110
|
-
fi
|
|
111
|
-
|
|
112
|
-
OUTPUT=$(hula schedule "${ARGS[@]}" 2>&1) || die 2 "Execute failed: $OUTPUT"
|
|
113
|
-
|
|
114
|
-
# ── Parse identifiers from the human-readable CLI output ─────────────────────
|
|
115
|
-
# Parsing is intentionally tolerant: a missing identifier becomes an empty
|
|
116
|
-
# string and we still report success, falling back to the raw cliOutput.
|
|
117
|
-
|
|
118
|
-
# Distinguish a schedule from a one-off run by the CLI's success banner.
|
|
119
|
-
if printf '%s' "$OUTPUT" | grep -q 'Schedule created successfully'; then
|
|
120
|
-
KIND='schedule'
|
|
121
|
-
elif printf '%s' "$OUTPUT" | grep -q 'Execute action queued successfully'; then
|
|
122
|
-
KIND='run'
|
|
123
|
-
elif [[ -n "$SCHEDULE" ]]; then
|
|
124
|
-
KIND='schedule'
|
|
125
|
-
else
|
|
126
|
-
KIND='run'
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
# The CLI prints " ID: <id>" for the run/schedule identifier.
|
|
130
|
-
PARSED_ID=$(printf '%s' "$OUTPUT" | grep -E '^\s*ID:' | head -1 | sed -E 's/^\s*ID:\s*//') || true
|
|
131
|
-
SAFE_OUTPUT=$(json_escape "$OUTPUT")
|
|
132
|
-
|
|
133
|
-
if [[ "$KIND" == 'schedule' ]]; then
|
|
134
|
-
# " Cron: <expr>" — fall back to the schedule we sent if the label is absent.
|
|
135
|
-
CRON_EXPR=$(printf '%s' "$OUTPUT" | grep -E '^\s*Cron:' | head -1 | sed -E 's/^\s*Cron:\s*//') || true
|
|
136
|
-
[[ -z "$CRON_EXPR" ]] && CRON_EXPR="$SCHEDULE"
|
|
137
|
-
SAFE_ID=$(json_escape "$PARSED_ID")
|
|
138
|
-
SAFE_CRON=$(json_escape "$CRON_EXPR")
|
|
139
|
-
printf '{"status":"success","kind":"schedule","scheduleId":"%s","cronExpr":"%s","cliOutput":"%s"}\n' \
|
|
140
|
-
"$SAFE_ID" "$SAFE_CRON" "$SAFE_OUTPUT"
|
|
141
|
-
else
|
|
142
|
-
# " PR: <url>"
|
|
143
|
-
PR_URL=$(printf '%s' "$OUTPUT" | grep -E '^\s*PR:' | head -1 | sed -E 's/^\s*PR:\s*//') || true
|
|
144
|
-
SAFE_ID=$(json_escape "$PARSED_ID")
|
|
145
|
-
SAFE_PR=$(json_escape "$PR_URL")
|
|
146
|
-
printf '{"status":"success","kind":"run","runId":"%s","prUrl":"%s","cliOutput":"%s"}\n' \
|
|
147
|
-
"$SAFE_ID" "$SAFE_PR" "$SAFE_OUTPUT"
|
|
148
|
-
fi
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hula-session-hook.sh — Claude Code PreToolUse hook (matcher: Bash).
|
|
3
|
-
#
|
|
4
|
-
# Captures the Claude Code session id of the chat session that runs
|
|
5
|
-
# /hula-launch, so the hula CLI can send it to hula-server as
|
|
6
|
-
# `clientSessionId` (launch-session provenance; hula-server PR #419).
|
|
7
|
-
#
|
|
8
|
-
# Why a hook: bash subprocesses cannot learn the session id any other way —
|
|
9
|
-
# CLAUDE_SESSION_ID is not exported to the Bash tool, and the model does not
|
|
10
|
-
# know its own id. Hooks are the one place Claude Code provides it.
|
|
11
|
-
#
|
|
12
|
-
# Installed to .github/scripts/ and registered under hooks.PreToolUse in the
|
|
13
|
-
# consumer repo's .claude/settings.json by `hula init`. Costs nothing: it is
|
|
14
|
-
# a local shell script (no tokens, no API calls) that no-ops in ~5ms for any
|
|
15
|
-
# Bash call that is not a hula-launch-run.sh invocation.
|
|
16
|
-
#
|
|
17
|
-
# Input (stdin): hook JSON with .session_id, .cwd and .tool_input.command
|
|
18
|
-
# Output: $HOME/.hublaunch-sessions/<repo-dir-name>/<branch>.json
|
|
19
|
-
# Always exits 0 — a capture failure must never block the tool call.
|
|
20
|
-
|
|
21
|
-
INPUT="$(cat)"
|
|
22
|
-
|
|
23
|
-
CMD="$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)"
|
|
24
|
-
case "$CMD" in
|
|
25
|
-
*hula-launch-run.sh*) ;; # our target — continue
|
|
26
|
-
*) exit 0 ;; # any other Bash call — ignore
|
|
27
|
-
esac
|
|
28
|
-
|
|
29
|
-
SESSION_ID="$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)"
|
|
30
|
-
CWD="$(printf '%s' "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)"
|
|
31
|
-
[ -z "$SESSION_ID" ] && exit 0
|
|
32
|
-
|
|
33
|
-
# --- extract the branch from the launch command ------------------------------
|
|
34
|
-
# Forms (see hula-launch-run.sh usage):
|
|
35
|
-
# bash .github/scripts/hula-launch-run.sh <plan-path> <branch> [--handoff u] [--test] [--kill-and-relaunch]
|
|
36
|
-
# bash .github/scripts/hula-launch-run.sh <branch> --kill (no new task -> skip)
|
|
37
|
-
AFTER="${CMD#*hula-launch-run.sh}"
|
|
38
|
-
# shellcheck disable=SC2086
|
|
39
|
-
set -- $AFTER
|
|
40
|
-
|
|
41
|
-
POSITIONAL=()
|
|
42
|
-
KILL_ONLY=false
|
|
43
|
-
while [ $# -gt 0 ]; do
|
|
44
|
-
case "$1" in
|
|
45
|
-
--handoff) shift 2 || break ;;
|
|
46
|
-
--kill) KILL_ONLY=true; shift ;;
|
|
47
|
-
--test|--kill-and-relaunch) shift ;;
|
|
48
|
-
--*) shift ;;
|
|
49
|
-
*) POSITIONAL+=("$1"); shift ;;
|
|
50
|
-
esac
|
|
51
|
-
done
|
|
52
|
-
|
|
53
|
-
$KILL_ONLY && exit 0 # pure kill: no new task launched
|
|
54
|
-
[ "${#POSITIONAL[@]}" -lt 2 ] && exit 0 # need <plan-path> <branch>
|
|
55
|
-
BRANCH="${POSITIONAL[1]}"
|
|
56
|
-
# Keep only filename-safe chars; fold any path separator into '-'.
|
|
57
|
-
BRANCH="$(printf '%s' "$BRANCH" | tr -cd 'A-Za-z0-9._/-' | tr '/' '-')"
|
|
58
|
-
[ -z "$BRANCH" ] && exit 0
|
|
59
|
-
|
|
60
|
-
REPO_NAME="$(basename "${CWD:-$PWD}")"
|
|
61
|
-
OUT_DIR="$HOME/.hublaunch-sessions/$REPO_NAME"
|
|
62
|
-
mkdir -p "$OUT_DIR"
|
|
63
|
-
|
|
64
|
-
jq -n --arg sid "$SESSION_ID" --arg branch "$BRANCH" --arg cwd "$CWD" \
|
|
65
|
-
'{sessionId:$sid, branch:$branch, cwd:$cwd, recordedAt:(now|todate)}' \
|
|
66
|
-
> "$OUT_DIR/$BRANCH.json" 2>/dev/null
|
|
67
|
-
|
|
68
|
-
exit 0
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hula-verify-gather.sh — Steps 2-5 of the hula.verify workflow
|
|
3
|
-
# Usage: bash .github/scripts/hula-verify-gather.sh <issue-number>
|
|
4
|
-
#
|
|
5
|
-
# Fetches issue details, finds associated PR, gathers PR files and diff.
|
|
6
|
-
# Outputs structured JSON to stdout; all other output goes to stderr.
|
|
7
|
-
# Exit codes: 0=success, 1=user error, 2=tool error
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
# ── helpers ────────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
json_escape() {
|
|
14
|
-
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/g' | tr -d '\n' | sed 's/\\n$//'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
die() {
|
|
18
|
-
local code="$1"; shift
|
|
19
|
-
local safe_msg
|
|
20
|
-
safe_msg=$(json_escape "$*")
|
|
21
|
-
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
22
|
-
exit "$code"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
ISSUE_NUMBER=''
|
|
28
|
-
|
|
29
|
-
while [[ $# -gt 0 ]]; do
|
|
30
|
-
case "$1" in
|
|
31
|
-
[0-9]*)
|
|
32
|
-
ISSUE_NUMBER="$1"; shift ;;
|
|
33
|
-
*)
|
|
34
|
-
die 1 "Unknown argument: $1" ;;
|
|
35
|
-
esac
|
|
36
|
-
done
|
|
37
|
-
|
|
38
|
-
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
39
|
-
die 1 "Usage: bash .github/scripts/hula-verify-gather.sh <issue-number>"
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# ── Step 2: Fetch issue details ────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
printf '🔍 Fetching issue #%s...\n' "$ISSUE_NUMBER" >&2
|
|
45
|
-
|
|
46
|
-
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" --json title,body,number,url,state 2>/dev/null) \
|
|
47
|
-
|| die 1 "Issue #${ISSUE_NUMBER} not found or not accessible."
|
|
48
|
-
|
|
49
|
-
ISSUE_TITLE=$(printf '%s' "$ISSUE_JSON" | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"$//')
|
|
50
|
-
ISSUE_STATE=$(printf '%s' "$ISSUE_JSON" | grep -o '"state":"[^"]*"' | head -1 | sed 's/"state":"//;s/"$//')
|
|
51
|
-
ISSUE_URL=$(printf '%s' "$ISSUE_JSON" | grep -o '"url":"[^"]*"' | head -1 | sed 's/"url":"//;s/"$//')
|
|
52
|
-
|
|
53
|
-
# ── Step 3: Find associated PR ────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
printf '🔗 Looking for PR linked to issue #%s...\n' "$ISSUE_NUMBER" >&2
|
|
56
|
-
|
|
57
|
-
# Try linked search first (works when PR is formally linked via GitHub)
|
|
58
|
-
PR_NUMBER=$(gh pr list --search "linked:${ISSUE_NUMBER}" --json number -q '.[0].number' 2>/dev/null) || true
|
|
59
|
-
|
|
60
|
-
if [[ -z "$PR_NUMBER" ]]; then
|
|
61
|
-
# Fallback: search PR bodies for issue references
|
|
62
|
-
# Handles both short format (#N) and full-path format (owner/repo#N)
|
|
63
|
-
# Uses gh's built-in jq (-q) — no external jq required
|
|
64
|
-
JQ_EXPR='.[] | select(.body != null) | select(.body | test("(Fixes|Closes|Resolves)[ ]+([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)?#'"${ISSUE_NUMBER}"'([^0-9]|$)|(^|[^0-9])#'"${ISSUE_NUMBER}"'([^0-9]|$)"; "i")) | .number'
|
|
65
|
-
PR_NUMBER=$(gh pr list --json number,body --limit 100 -q "$JQ_EXPR" 2>/dev/null | head -1) || true
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
if [[ -z "$PR_NUMBER" ]]; then
|
|
69
|
-
die 1 "No PR found for issue #${ISSUE_NUMBER}. The implementation may not have started yet."
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
# Fetch full PR details via pr view (reliable single-object JSON)
|
|
73
|
-
PR_DETAILS=$(gh pr view "$PR_NUMBER" --json number,title,url,state,headRefName,isDraft 2>/dev/null) \
|
|
74
|
-
|| die 2 "Failed to fetch details for PR #${PR_NUMBER}."
|
|
75
|
-
|
|
76
|
-
PR_TITLE=$(printf '%s' "$PR_DETAILS" | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"$//')
|
|
77
|
-
PR_URL=$(printf '%s' "$PR_DETAILS" | grep -o '"url":"[^"]*"' | head -1 | sed 's/"url":"//;s/"$//')
|
|
78
|
-
PR_STATE=$(printf '%s' "$PR_DETAILS" | grep -o '"state":"[^"]*"' | head -1 | sed 's/"state":"//;s/"$//')
|
|
79
|
-
PR_BRANCH=$(printf '%s' "$PR_DETAILS" | grep -o '"headRefName":"[^"]*"' | head -1 | sed 's/"headRefName":"//;s/"$//')
|
|
80
|
-
PR_DRAFT=$(printf '%s' "$PR_DETAILS" | grep -o '"isDraft":[a-z]*' | head -1 | sed 's/"isDraft"://')
|
|
81
|
-
|
|
82
|
-
# ── Step 4: Fetch PR file changes ─────────────────────────────────────────────
|
|
83
|
-
|
|
84
|
-
printf '📊 Fetching changed files for PR #%s...\n' "$PR_NUMBER" >&2
|
|
85
|
-
|
|
86
|
-
PR_FILES_JSON=$(gh pr view "$PR_NUMBER" --json files,additions,deletions 2>/dev/null) \
|
|
87
|
-
|| die 2 "Failed to fetch PR files for PR #${PR_NUMBER}."
|
|
88
|
-
|
|
89
|
-
TOTAL_ADDITIONS=$(printf '%s' "$PR_FILES_JSON" | grep -o '"additions":[0-9]*' | tail -1 | sed 's/"additions"://')
|
|
90
|
-
TOTAL_DELETIONS=$(printf '%s' "$PR_FILES_JSON" | grep -o '"deletions":[0-9]*' | tail -1 | sed 's/"deletions"://')
|
|
91
|
-
|
|
92
|
-
# Count files by category (rough heuristic)
|
|
93
|
-
FILES_LIST=$(printf '%s' "$PR_FILES_JSON" | grep -o '"path":"[^"]*"' | sed 's/"path":"//;s/"$//')
|
|
94
|
-
|
|
95
|
-
CODE_COUNT=0
|
|
96
|
-
TEST_COUNT=0
|
|
97
|
-
DOC_COUNT=0
|
|
98
|
-
CONFIG_COUNT=0
|
|
99
|
-
TOTAL_COUNT=0
|
|
100
|
-
|
|
101
|
-
while IFS= read -r fpath; do
|
|
102
|
-
[[ -z "$fpath" ]] && continue
|
|
103
|
-
TOTAL_COUNT=$((TOTAL_COUNT + 1))
|
|
104
|
-
case "$fpath" in
|
|
105
|
-
*.test.*|*.spec.*|*/__tests__/*|*/test/*|*/tests/*) TEST_COUNT=$((TEST_COUNT + 1)) ;;
|
|
106
|
-
*.md|docs/*|README*|CHANGELOG*|DOCUMENTATION*) DOC_COUNT=$((DOC_COUNT + 1)) ;;
|
|
107
|
-
package.json|package-lock.json|tsconfig*|*.config.*|*.json|*.yaml|*.yml) CONFIG_COUNT=$((CONFIG_COUNT + 1)) ;;
|
|
108
|
-
*) CODE_COUNT=$((CODE_COUNT + 1)) ;;
|
|
109
|
-
esac
|
|
110
|
-
done <<< "$FILES_LIST"
|
|
111
|
-
|
|
112
|
-
# ── Step 5: Fetch PR diff ─────────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
printf '📄 Fetching PR diff...\n' >&2
|
|
115
|
-
|
|
116
|
-
DIFF_FILE="/tmp/hula-verify-diff-${PR_NUMBER}.patch"
|
|
117
|
-
gh pr diff "$PR_NUMBER" > "$DIFF_FILE" 2>/dev/null \
|
|
118
|
-
|| printf '⚠️ Could not fetch PR diff (PR may be too large or not accessible)\n' >&2
|
|
119
|
-
|
|
120
|
-
# ── Output result ─────────────────────────────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
SAFE_ISSUE_TITLE=$(json_escape "$ISSUE_TITLE")
|
|
123
|
-
SAFE_PR_TITLE=$(json_escape "$PR_TITLE")
|
|
124
|
-
SAFE_PR_URL=$(json_escape "$PR_URL")
|
|
125
|
-
SAFE_ISSUE_URL=$(json_escape "$ISSUE_URL")
|
|
126
|
-
SAFE_PR_BRANCH=$(json_escape "$PR_BRANCH")
|
|
127
|
-
SAFE_DIFF_FILE=$(json_escape "$DIFF_FILE")
|
|
128
|
-
PR_DRAFT_VAL="${PR_DRAFT:-false}"
|
|
129
|
-
|
|
130
|
-
printf '{"status":"success","issueNumber":%s,"issueTitle":"%s","issueState":"%s","issueUrl":"%s","prNumber":%s,"prTitle":"%s","prUrl":"%s","prState":"%s","prBranch":"%s","prIsDraft":%s,"diffFile":"%s","filesChanged":{"total":%d,"code":%d,"test":%d,"doc":%d,"config":%d},"lines":{"additions":%s,"deletions":%s}}\n' \
|
|
131
|
-
"$ISSUE_NUMBER" "$SAFE_ISSUE_TITLE" "$ISSUE_STATE" "$SAFE_ISSUE_URL" \
|
|
132
|
-
"$PR_NUMBER" "$SAFE_PR_TITLE" "$SAFE_PR_URL" "$PR_STATE" "$SAFE_PR_BRANCH" "$PR_DRAFT_VAL" \
|
|
133
|
-
"$SAFE_DIFF_FILE" \
|
|
134
|
-
"$TOTAL_COUNT" "$CODE_COUNT" "$TEST_COUNT" "$DOC_COUNT" "$CONFIG_COUNT" \
|
|
135
|
-
"${TOTAL_ADDITIONS:-0}" "${TOTAL_DELETIONS:-0}"
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hula-verify-post.sh — Step 8 of the hula.verify workflow
|
|
3
|
-
# Usage: bash .github/scripts/hula-verify-post.sh <pr-number> <report-file>
|
|
4
|
-
#
|
|
5
|
-
# Posts a verification report as a PR comment mentioning @copilot.
|
|
6
|
-
# Outputs structured JSON to stdout; all other output goes to stderr.
|
|
7
|
-
# Exit codes: 0=success, 1=user error, 2=tool error
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
# ── helpers ────────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
json_escape() {
|
|
14
|
-
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/g' | tr -d '\n' | sed 's/\\n$//'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
die() {
|
|
18
|
-
local code="$1"; shift
|
|
19
|
-
local safe_msg
|
|
20
|
-
safe_msg=$(json_escape "$*")
|
|
21
|
-
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
22
|
-
exit "$code"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
PR_NUMBER=''
|
|
28
|
-
REPORT_FILE=''
|
|
29
|
-
|
|
30
|
-
while [[ $# -gt 0 ]]; do
|
|
31
|
-
case "$1" in
|
|
32
|
-
[0-9]*)
|
|
33
|
-
if [[ -z "$PR_NUMBER" ]]; then
|
|
34
|
-
PR_NUMBER="$1"
|
|
35
|
-
else
|
|
36
|
-
die 1 "Unexpected positional argument: $1"
|
|
37
|
-
fi
|
|
38
|
-
shift ;;
|
|
39
|
-
*)
|
|
40
|
-
if [[ -z "$REPORT_FILE" ]]; then
|
|
41
|
-
REPORT_FILE="$1"
|
|
42
|
-
else
|
|
43
|
-
die 1 "Unknown argument: $1"
|
|
44
|
-
fi
|
|
45
|
-
shift ;;
|
|
46
|
-
esac
|
|
47
|
-
done
|
|
48
|
-
|
|
49
|
-
if [[ -z "$PR_NUMBER" ]]; then
|
|
50
|
-
die 1 "Usage: bash .github/scripts/hula-verify-post.sh <pr-number> <report-file>"
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
if [[ -z "$REPORT_FILE" ]]; then
|
|
54
|
-
die 1 "Report file path is required as second argument."
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
if [[ ! -f "$REPORT_FILE" ]]; then
|
|
58
|
-
die 1 "Report file not found: ${REPORT_FILE}"
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# ── Build comment body with @copilot mention ──────────────────────────────────
|
|
62
|
-
|
|
63
|
-
COMMENT_TMP="/tmp/hula-verify-comment-${PR_NUMBER}.md"
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
printf '@copilot Please review this verification report and address any gaps.\n\n'
|
|
67
|
-
cat "$REPORT_FILE"
|
|
68
|
-
} > "$COMMENT_TMP"
|
|
69
|
-
|
|
70
|
-
# ── Post comment ──────────────────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
printf '📝 Posting verification report to PR #%s...\n' "$PR_NUMBER" >&2
|
|
73
|
-
|
|
74
|
-
COMMENT_URL=$(gh pr comment "$PR_NUMBER" --body-file "$COMMENT_TMP" 2>/dev/null) \
|
|
75
|
-
|| die 2 "Failed to post comment to PR #${PR_NUMBER}. You can copy the report manually."
|
|
76
|
-
|
|
77
|
-
rm -f "$COMMENT_TMP"
|
|
78
|
-
|
|
79
|
-
# ── Output result ─────────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
SAFE_URL=$(json_escape "${COMMENT_URL:-}")
|
|
82
|
-
|
|
83
|
-
printf '{"status":"success","prNumber":%s,"commentUrl":"%s"}\n' \
|
|
84
|
-
"$PR_NUMBER" "$SAFE_URL"
|