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.
Files changed (101) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +102 -2
  3. package/dist/commands/init.d.ts +61 -0
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +125 -114
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/instructions.d.ts +17 -0
  8. package/dist/commands/instructions.d.ts.map +1 -0
  9. package/dist/commands/instructions.js +51 -0
  10. package/dist/commands/instructions.js.map +1 -0
  11. package/dist/commands/launch.d.ts +21 -0
  12. package/dist/commands/launch.d.ts.map +1 -1
  13. package/dist/commands/launch.js +61 -0
  14. package/dist/commands/launch.js.map +1 -1
  15. package/dist/commands/script.d.ts +28 -0
  16. package/dist/commands/script.d.ts.map +1 -0
  17. package/dist/commands/script.js +69 -0
  18. package/dist/commands/script.js.map +1 -0
  19. package/dist/commands/session-hook.d.ts +26 -0
  20. package/dist/commands/session-hook.d.ts.map +1 -0
  21. package/dist/commands/session-hook.js +146 -0
  22. package/dist/commands/session-hook.js.map +1 -0
  23. package/dist/index.js +22 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/scripts/fix-commit.d.ts +2 -0
  26. package/dist/scripts/fix-commit.d.ts.map +1 -0
  27. package/dist/scripts/fix-commit.js +125 -0
  28. package/dist/scripts/fix-commit.js.map +1 -0
  29. package/dist/scripts/fix-setup.d.ts +2 -0
  30. package/dist/scripts/fix-setup.d.ts.map +1 -0
  31. package/dist/scripts/fix-setup.js +161 -0
  32. package/dist/scripts/fix-setup.js.map +1 -0
  33. package/dist/scripts/launch-run.d.ts +14 -0
  34. package/dist/scripts/launch-run.d.ts.map +1 -0
  35. package/dist/scripts/launch-run.js +117 -0
  36. package/dist/scripts/launch-run.js.map +1 -0
  37. package/dist/scripts/lib/exec.d.ts +48 -0
  38. package/dist/scripts/lib/exec.d.ts.map +1 -0
  39. package/dist/scripts/lib/exec.js +61 -0
  40. package/dist/scripts/lib/exec.js.map +1 -0
  41. package/dist/scripts/lib/io.d.ts +48 -0
  42. package/dist/scripts/lib/io.d.ts.map +1 -0
  43. package/dist/scripts/lib/io.js +72 -0
  44. package/dist/scripts/lib/io.js.map +1 -0
  45. package/dist/scripts/lib/read-config.d.ts +26 -0
  46. package/dist/scripts/lib/read-config.d.ts.map +1 -0
  47. package/dist/scripts/lib/read-config.js +65 -0
  48. package/dist/scripts/lib/read-config.js.map +1 -0
  49. package/dist/scripts/merge-local.d.ts +2 -0
  50. package/dist/scripts/merge-local.d.ts.map +1 -0
  51. package/dist/scripts/merge-local.js +146 -0
  52. package/dist/scripts/merge-local.js.map +1 -0
  53. package/dist/scripts/merge-remote.d.ts +12 -0
  54. package/dist/scripts/merge-remote.d.ts.map +1 -0
  55. package/dist/scripts/merge-remote.js +71 -0
  56. package/dist/scripts/merge-remote.js.map +1 -0
  57. package/dist/scripts/schedule-manage.d.ts +2 -0
  58. package/dist/scripts/schedule-manage.d.ts.map +1 -0
  59. package/dist/scripts/schedule-manage.js +151 -0
  60. package/dist/scripts/schedule-manage.js.map +1 -0
  61. package/dist/scripts/schedule-run.d.ts +2 -0
  62. package/dist/scripts/schedule-run.d.ts.map +1 -0
  63. package/dist/scripts/schedule-run.js +146 -0
  64. package/dist/scripts/schedule-run.js.map +1 -0
  65. package/dist/scripts/verify-gather.d.ts +2 -0
  66. package/dist/scripts/verify-gather.d.ts.map +1 -0
  67. package/dist/scripts/verify-gather.js +206 -0
  68. package/dist/scripts/verify-gather.js.map +1 -0
  69. package/dist/scripts/verify-post.d.ts +12 -0
  70. package/dist/scripts/verify-post.d.ts.map +1 -0
  71. package/dist/scripts/verify-post.js +91 -0
  72. package/dist/scripts/verify-post.js.map +1 -0
  73. package/dist/templates/planning-instructions.md +7 -7
  74. package/dist/templates/skill-creation-instructions.md +2 -2
  75. package/dist/templates/skills/hula-confirm/SKILL.md +3 -3
  76. package/dist/templates/skills/hula-fix/SKILL.md +3 -3
  77. package/dist/templates/skills/hula-help/SKILL.md +117 -0
  78. package/dist/templates/skills/hula-launch/SKILL.md +14 -8
  79. package/dist/templates/skills/hula-merge/SKILL.md +2 -2
  80. package/dist/templates/skills/hula-plan/SKILL.md +5 -5
  81. package/dist/templates/skills/hula-schedule/SKILL.md +16 -16
  82. package/dist/templates/skills/hula-verify/SKILL.md +32 -33
  83. package/dist/types/config.schema.d.ts +235 -0
  84. package/dist/types/config.schema.d.ts.map +1 -1
  85. package/dist/types/config.schema.js +66 -0
  86. package/dist/types/config.schema.js.map +1 -1
  87. package/dist/utils/client-session.d.ts +2 -2
  88. package/dist/utils/client-session.js +2 -2
  89. package/package.json +6 -5
  90. package/scripts/postinstall.mjs +102 -0
  91. package/dist/templates/scripts/hula-fix-commit.sh +0 -114
  92. package/dist/templates/scripts/hula-fix-setup.sh +0 -152
  93. package/dist/templates/scripts/hula-launch-run.sh +0 -140
  94. package/dist/templates/scripts/hula-merge-local.sh +0 -138
  95. package/dist/templates/scripts/hula-merge-remote.sh +0 -94
  96. package/dist/templates/scripts/hula-read-config.sh +0 -38
  97. package/dist/templates/scripts/hula-schedule-manage.sh +0 -159
  98. package/dist/templates/scripts/hula-schedule-run.sh +0 -148
  99. package/dist/templates/scripts/hula-session-hook.sh +0 -68
  100. package/dist/templates/scripts/hula-verify-gather.sh +0 -135
  101. 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"