hub-launch 1.2.3 → 1.4.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 +28 -0
- package/README.md +39 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +135 -4
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +23 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/merge.d.ts.map +1 -1
- package/dist/commands/merge.js +73 -0
- package/dist/commands/merge.js.map +1 -1
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +16 -75
- package/dist/commands/upload.js.map +1 -1
- package/dist/services/git/FilePublishService.d.ts +68 -0
- package/dist/services/git/FilePublishService.d.ts.map +1 -0
- package/dist/services/git/FilePublishService.js +141 -0
- package/dist/services/git/FilePublishService.js.map +1 -0
- package/dist/services/git/WorktreeService.d.ts +14 -0
- package/dist/services/git/WorktreeService.d.ts.map +1 -1
- package/dist/services/git/WorktreeService.js +37 -0
- package/dist/services/git/WorktreeService.js.map +1 -1
- package/dist/templates/scripts/hula-execute-manage.sh +159 -0
- package/dist/templates/scripts/hula-execute-run.sh +139 -0
- package/dist/templates/skill-creation-instructions.md +142 -0
- package/dist/templates/skills/hula-execute/SKILL.md +256 -0
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-execute-manage.sh — management surface for the /hula-execute skill
|
|
3
|
+
# Usage (exactly one action per call):
|
|
4
|
+
# bash .github/scripts/hula-execute-manage.sh --list
|
|
5
|
+
# bash .github/scripts/hula-execute-manage.sh --list-schedules
|
|
6
|
+
# bash .github/scripts/hula-execute-manage.sh --show <runId>
|
|
7
|
+
# bash .github/scripts/hula-execute-manage.sh --run-now <scheduleId>
|
|
8
|
+
# bash .github/scripts/hula-execute-manage.sh --cancel-schedule <scheduleId>
|
|
9
|
+
# bash .github/scripts/hula-execute-manage.sh --publish-skill <repo-relative-path>
|
|
10
|
+
# bash .github/scripts/hula-execute-manage.sh --delete-skill <repo-relative-path>
|
|
11
|
+
#
|
|
12
|
+
# Each invocation maps to the corresponding `hula execute …` 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-execute-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 execute 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 execute --%s...\n' "$ACTION" >&2
|
|
130
|
+
|
|
131
|
+
OUTPUT=$(hula execute "${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
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-execute-run.sh — runs the hula.execute workflow for the /hula-execute skill
|
|
3
|
+
# Usage:
|
|
4
|
+
# bash .github/scripts/hula-execute-run.sh \
|
|
5
|
+
# (--built-in <name> | --action-path <path>) \
|
|
6
|
+
# [--entry-point <path>] [--outcome-type <pr|plan|feedback>] [--schedule "<cron>"]
|
|
7
|
+
#
|
|
8
|
+
# Performs: hula execute (one-off run, or a recurring schedule when --schedule is given).
|
|
9
|
+
# Outputs a single structured JSON object to stdout; all other output goes to stderr.
|
|
10
|
+
#
|
|
11
|
+
# JSON contract (stdout):
|
|
12
|
+
# one-off run : {"status":"success","kind":"run","runId":"…","prUrl":"…","cliOutput":"…"}
|
|
13
|
+
# schedule : {"status":"success","kind":"schedule","scheduleId":"…","cronExpr":"…","cliOutput":"…"}
|
|
14
|
+
# failure : {"status":"error","code":<n>,"message":"…"}
|
|
15
|
+
#
|
|
16
|
+
# Exit codes: 0=success, 1=user error, 2=tool error
|
|
17
|
+
#
|
|
18
|
+
# Security: never prints secrets. Credentials are resolved inside the CLI from
|
|
19
|
+
# flags/config/env — they are NOT passed by this script.
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
# ── helpers ────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
json_escape() {
|
|
26
|
+
if command -v jq &>/dev/null; then
|
|
27
|
+
# jq -Rs . reads stdin as a raw string and outputs a JSON string (with surrounding quotes).
|
|
28
|
+
# Strip the surrounding quotes; the escaped content is safe to embed in JSON.
|
|
29
|
+
printf '%s' "$1" | jq -Rs . | sed 's/^"//;s/"$//'
|
|
30
|
+
else
|
|
31
|
+
# Fallback: escape backslashes and double quotes, collapse control chars to spaces
|
|
32
|
+
printf '%s' "$1" \
|
|
33
|
+
| sed 's/\\/\\\\/g; s/"/\\"/g' \
|
|
34
|
+
| tr '\n\r\t' ' '
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
die() {
|
|
39
|
+
local code="$1"; shift
|
|
40
|
+
local safe_msg
|
|
41
|
+
safe_msg=$(json_escape "$*")
|
|
42
|
+
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
43
|
+
exit "$code"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
BUILT_IN=''
|
|
49
|
+
ACTION_PATH=''
|
|
50
|
+
ENTRY_POINT=''
|
|
51
|
+
OUTCOME_TYPE=''
|
|
52
|
+
SCHEDULE=''
|
|
53
|
+
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--built-in) BUILT_IN="$2"; shift 2 ;;
|
|
57
|
+
--built-in=*) BUILT_IN="${1#--built-in=}"; shift ;;
|
|
58
|
+
--action-path) ACTION_PATH="$2"; shift 2 ;;
|
|
59
|
+
--action-path=*) ACTION_PATH="${1#--action-path=}"; shift ;;
|
|
60
|
+
--entry-point) ENTRY_POINT="$2"; shift 2 ;;
|
|
61
|
+
--entry-point=*) ENTRY_POINT="${1#--entry-point=}"; shift ;;
|
|
62
|
+
--outcome-type) OUTCOME_TYPE="$2"; shift 2 ;;
|
|
63
|
+
--outcome-type=*) OUTCOME_TYPE="${1#--outcome-type=}"; shift ;;
|
|
64
|
+
--schedule) SCHEDULE="$2"; shift 2 ;;
|
|
65
|
+
--schedule=*) SCHEDULE="${1#--schedule=}"; shift ;;
|
|
66
|
+
*) die 1 "Unexpected argument: $1" ;;
|
|
67
|
+
esac
|
|
68
|
+
done
|
|
69
|
+
|
|
70
|
+
# Exactly one of --built-in / --action-path is required.
|
|
71
|
+
if [[ -n "$BUILT_IN" && -n "$ACTION_PATH" ]]; then
|
|
72
|
+
die 1 "Provide either --built-in or --action-path, not both."
|
|
73
|
+
fi
|
|
74
|
+
if [[ -z "$BUILT_IN" && -z "$ACTION_PATH" ]]; then
|
|
75
|
+
die 1 "Usage: bash .github/scripts/hula-execute-run.sh (--built-in <name> | --action-path <path>) [--entry-point <path>] [--outcome-type <pr|plan|feedback>] [--schedule \"<cron>\"]"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Reject directory-traversal in --action-path, except for https:// URLs.
|
|
79
|
+
if [[ -n "$ACTION_PATH" ]] && [[ "$ACTION_PATH" != https://* ]]; then
|
|
80
|
+
if printf '%s' "$ACTION_PATH" | grep -q '\.\.'; then
|
|
81
|
+
die 1 "Invalid action path (path traversal not allowed): ${ACTION_PATH}"
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ── Run hula execute ─────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
ARGS=()
|
|
88
|
+
if [[ -n "$BUILT_IN" ]]; then
|
|
89
|
+
ARGS+=(--built-in "$BUILT_IN")
|
|
90
|
+
else
|
|
91
|
+
ARGS+=(--action-path "$ACTION_PATH")
|
|
92
|
+
fi
|
|
93
|
+
[[ -n "$ENTRY_POINT" ]] && ARGS+=(--entry-point "$ENTRY_POINT")
|
|
94
|
+
[[ -n "$OUTCOME_TYPE" ]] && ARGS+=(--outcome-type "$OUTCOME_TYPE")
|
|
95
|
+
[[ -n "$SCHEDULE" ]] && ARGS+=(--schedule "$SCHEDULE")
|
|
96
|
+
|
|
97
|
+
if [[ -n "$SCHEDULE" ]]; then
|
|
98
|
+
printf '⏰ Scheduling execute-action (%s)...\n' "$SCHEDULE" >&2
|
|
99
|
+
else
|
|
100
|
+
printf '🚀 Running execute-action...\n' >&2
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
OUTPUT=$(hula execute "${ARGS[@]}" 2>&1) || die 2 "Execute failed: $OUTPUT"
|
|
104
|
+
|
|
105
|
+
# ── Parse identifiers from the human-readable CLI output ─────────────────────
|
|
106
|
+
# Parsing is intentionally tolerant: a missing identifier becomes an empty
|
|
107
|
+
# string and we still report success, falling back to the raw cliOutput.
|
|
108
|
+
|
|
109
|
+
# Distinguish a schedule from a one-off run by the CLI's success banner.
|
|
110
|
+
if printf '%s' "$OUTPUT" | grep -q 'Schedule created successfully'; then
|
|
111
|
+
KIND='schedule'
|
|
112
|
+
elif printf '%s' "$OUTPUT" | grep -q 'Execute action queued successfully'; then
|
|
113
|
+
KIND='run'
|
|
114
|
+
elif [[ -n "$SCHEDULE" ]]; then
|
|
115
|
+
KIND='schedule'
|
|
116
|
+
else
|
|
117
|
+
KIND='run'
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# The CLI prints " ID: <id>" for the run/schedule identifier.
|
|
121
|
+
PARSED_ID=$(printf '%s' "$OUTPUT" | grep -E '^\s*ID:' | head -1 | sed -E 's/^\s*ID:\s*//') || true
|
|
122
|
+
SAFE_OUTPUT=$(json_escape "$OUTPUT")
|
|
123
|
+
|
|
124
|
+
if [[ "$KIND" == 'schedule' ]]; then
|
|
125
|
+
# " Cron: <expr>" — fall back to the schedule we sent if the label is absent.
|
|
126
|
+
CRON_EXPR=$(printf '%s' "$OUTPUT" | grep -E '^\s*Cron:' | head -1 | sed -E 's/^\s*Cron:\s*//') || true
|
|
127
|
+
[[ -z "$CRON_EXPR" ]] && CRON_EXPR="$SCHEDULE"
|
|
128
|
+
SAFE_ID=$(json_escape "$PARSED_ID")
|
|
129
|
+
SAFE_CRON=$(json_escape "$CRON_EXPR")
|
|
130
|
+
printf '{"status":"success","kind":"schedule","scheduleId":"%s","cronExpr":"%s","cliOutput":"%s"}\n' \
|
|
131
|
+
"$SAFE_ID" "$SAFE_CRON" "$SAFE_OUTPUT"
|
|
132
|
+
else
|
|
133
|
+
# " PR: <url>"
|
|
134
|
+
PR_URL=$(printf '%s' "$OUTPUT" | grep -E '^\s*PR:' | head -1 | sed -E 's/^\s*PR:\s*//') || true
|
|
135
|
+
SAFE_ID=$(json_escape "$PARSED_ID")
|
|
136
|
+
SAFE_PR=$(json_escape "$PR_URL")
|
|
137
|
+
printf '{"status":"success","kind":"run","runId":"%s","prUrl":"%s","cliOutput":"%s"}\n' \
|
|
138
|
+
"$SAFE_ID" "$SAFE_PR" "$SAFE_OUTPUT"
|
|
139
|
+
fi
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Execute Skill Creation Instructions
|
|
2
|
+
|
|
3
|
+
These instructions drive the **create-from-description** mode of the
|
|
4
|
+
`/hula-execute` skill: turning a plain-language description into a published
|
|
5
|
+
action file that `hula execute` can run or schedule.
|
|
6
|
+
|
|
7
|
+
The action file is plain instruction markdown that the hula-project server reads
|
|
8
|
+
from `origin/<uploadBranch>` (default `main`) at run time. It is **not** an Agent
|
|
9
|
+
Skill and is never registered as a slash command — it lives under
|
|
10
|
+
`.hublaunch/skills/`.
|
|
11
|
+
|
|
12
|
+
Follow these steps in order. Do **not** skip the question step, and do **not**
|
|
13
|
+
run `hula execute` before the file is successfully published.
|
|
14
|
+
|
|
15
|
+
## Step 1: Ask clarifying questions first (then STOP)
|
|
16
|
+
|
|
17
|
+
Mirror the `/hula-plan` question-first behavior. Before writing anything, ask the
|
|
18
|
+
user the questions you cannot confidently answer from their description:
|
|
19
|
+
|
|
20
|
+
1. **What should the action do?** The concrete task to perform on each run
|
|
21
|
+
(e.g. "remove unreachable code", "upgrade dependencies and fix breakages").
|
|
22
|
+
2. **Target entry point** (optional): the file, directory, or URL the action
|
|
23
|
+
focuses on (e.g. `src/`). Maps to `--entry-point`.
|
|
24
|
+
3. **Outcome type**: `pr` (open a pull request), `plan` (produce a plan), or
|
|
25
|
+
`feedback` (review/report only). Maps to `--outcome-type`. Default `pr`.
|
|
26
|
+
4. **One-off or scheduled?** If recurring, get the schedule phrase (e.g. "every
|
|
27
|
+
night", "every Monday 9am").
|
|
28
|
+
5. **Any constraints / scope limits** the run must respect (optional).
|
|
29
|
+
|
|
30
|
+
**STOP and wait for the answers.** Do not proceed to authoring until the user
|
|
31
|
+
responds. If anything is still ambiguous after the answers, ask a focused
|
|
32
|
+
follow-up round.
|
|
33
|
+
|
|
34
|
+
## Step 2: Confirm the action name
|
|
35
|
+
|
|
36
|
+
Derive a default slug from the description: lowercase, hyphen-separated, no
|
|
37
|
+
spaces or punctuation (e.g. "remove unreachable code in src" →
|
|
38
|
+
`remove-unreachable-code`). Propose it to the user and let them override. Confirm
|
|
39
|
+
the final name before writing the file.
|
|
40
|
+
|
|
41
|
+
## Step 3: Resolve the schedule (if recurring)
|
|
42
|
+
|
|
43
|
+
If the action is scheduled, translate the phrase to a 5-field cron expression
|
|
44
|
+
using the preset table in `SKILL.md`, then **echo the cron with a plain-English
|
|
45
|
+
readback and confirm it** before publishing/running. There is no server-side
|
|
46
|
+
cron validation, so the readback + confirmation is mandatory.
|
|
47
|
+
|
|
48
|
+
## Step 4: Compute the filename and path
|
|
49
|
+
|
|
50
|
+
- Filename: `YYYY-MM-DD-HH:MM-<name-slug>.md`
|
|
51
|
+
- Use today's date and the current time on a 24-hour clock.
|
|
52
|
+
- `<name-slug>` is the confirmed, lowercase-hyphenated action name.
|
|
53
|
+
- Repo-relative path: `.hublaunch/skills/<filename>`
|
|
54
|
+
- Create the `.hublaunch/skills/` directory if it does not exist.
|
|
55
|
+
|
|
56
|
+
## Step 5: Write the action file (free-form instruction markdown, NO frontmatter)
|
|
57
|
+
|
|
58
|
+
Use the `Write` tool to create the file at the computed path. The content MUST be
|
|
59
|
+
free-form instruction markdown with **no YAML frontmatter** (frontmatter would
|
|
60
|
+
make it look like an Agent Skill). Follow this template:
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
# <Action Title>
|
|
64
|
+
|
|
65
|
+
## Goal
|
|
66
|
+
<1–3 sentences describing what this action should accomplish.>
|
|
67
|
+
|
|
68
|
+
## Steps
|
|
69
|
+
1. <step>
|
|
70
|
+
2. <step>
|
|
71
|
+
3. <step>
|
|
72
|
+
|
|
73
|
+
## Constraints
|
|
74
|
+
- <optional: what must not change / scope limits>
|
|
75
|
+
|
|
76
|
+
## Outcome
|
|
77
|
+
<What the run should produce, consistent with the chosen --outcome-type:
|
|
78
|
+
pr | plan | feedback.>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Fill the template from the user's answers. Omit the `## Constraints` section if
|
|
82
|
+
the user gave no constraints.
|
|
83
|
+
|
|
84
|
+
## Step 6: Publish to origin/main BEFORE running (mandatory ordering)
|
|
85
|
+
|
|
86
|
+
The server clones the repository and reads the action file from the default
|
|
87
|
+
branch at run time (and re-reads it on every scheduled fire). The file MUST be on
|
|
88
|
+
the branch before the run, so publish it first:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bash .github/scripts/hula-execute-manage.sh --publish-skill .hublaunch/skills/<filename>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Parse the single JSON object it prints:
|
|
95
|
+
|
|
96
|
+
- If `status` is `"error"`: display `❌ <message>` and **STOP**. Do **not** run
|
|
97
|
+
`hula execute` — the file is not on the branch, so the run would fail.
|
|
98
|
+
- If `status` is `"success"`: continue to Step 7.
|
|
99
|
+
|
|
100
|
+
## Step 7: Run or schedule the action
|
|
101
|
+
|
|
102
|
+
Only after a successful publish, invoke the run wrapper with `--action-path`
|
|
103
|
+
pointing at the committed file:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bash .github/scripts/hula-execute-run.sh --action-path .hublaunch/skills/<filename> [--entry-point <path>] [--outcome-type <type>] [--schedule "<cron>"]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Pass only the flags you resolved. Quote the cron expression.
|
|
110
|
+
|
|
111
|
+
## Step 8: Report the result
|
|
112
|
+
|
|
113
|
+
Parse the run wrapper's JSON (same contract as the normal run flow) and report:
|
|
114
|
+
|
|
115
|
+
- The **created file path** (`.hublaunch/skills/<filename>`) and that it was
|
|
116
|
+
published to `origin/<uploadBranch>`.
|
|
117
|
+
- The **run or schedule result**:
|
|
118
|
+
- One-off run → the Run ID and PR link (if any), plus
|
|
119
|
+
`hula execute --show <runId>` to check status.
|
|
120
|
+
- Schedule → the Schedule ID and cron expression, plus a note that you can
|
|
121
|
+
manage it with `/hula-execute list`, `/hula-execute run now <id>`,
|
|
122
|
+
`/hula-execute cancel <id>`, or `/hula-execute update <id> …`.
|
|
123
|
+
|
|
124
|
+
Example success report:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
✅ Action created and published
|
|
128
|
+
|
|
129
|
+
📄 **File**: .hublaunch/skills/2026-06-19-16:35-remove-unreachable-code.md (on origin/main)
|
|
130
|
+
🔖 **Schedule ID**: sch_abc123
|
|
131
|
+
⏰ **Cron**: 0 3 * * * (every day at 3:00 AM)
|
|
132
|
+
|
|
133
|
+
Manage it with: /hula-execute list · /hula-execute run now sch_abc123 · /hula-execute cancel sch_abc123
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Notes
|
|
137
|
+
|
|
138
|
+
- Never echo secrets. Credentials resolve inside the CLI.
|
|
139
|
+
- The action file is committed to `origin/<uploadBranch>` via a temporary
|
|
140
|
+
worktree — the user's current branch and working tree are never touched.
|
|
141
|
+
- If the publish succeeds but the run fails, the file remains on the branch; the
|
|
142
|
+
user can re-run it later with `/hula-execute .hublaunch/skills/<filename>`.
|