hub-launch 1.3.0 → 1.4.1

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.
@@ -1,11 +1,9 @@
1
- import { basename, dirname, join, resolve, isAbsolute } from 'path';
2
- import { readFileSync, writeFileSync, mkdirSync } from 'fs';
1
+ import { basename } from 'path';
2
+ import { readFileSync } from 'fs';
3
3
  import { PlanService } from '../services/plan/PlanService.js';
4
4
  import { logger } from '../utils/logger.js';
5
- import { WorktreeService } from '../services/git/WorktreeService.js';
6
5
  import { GitService } from '../services/git/GitService.js';
7
- import { getRepoInfo } from '../utils/github-cli.js';
8
- import { getRepoRoot } from '../utils/shell.js';
6
+ import { FilePublishService } from '../services/git/FilePublishService.js';
9
7
  /**
10
8
  * Upload command - Upload a plan to the target branch using a temporary worktree.
11
9
  *
@@ -40,23 +38,6 @@ export function uploadCommand(program, config) {
40
38
  export async function executeUpload(config, planPath) {
41
39
  const planService = new PlanService(config.planPath);
42
40
  const targetBranch = config.uploadBranch ?? 'main';
43
- const rawBasePath = config.worktreeBasePath ?? '.hula-worktrees';
44
- const repoRoot = await getRepoRoot();
45
- const worktreeBasePath = isAbsolute(rawBasePath) ? rawBasePath : resolve(repoRoot, rawBasePath);
46
- // Validate targetBranch to prevent command injection in git push refspec
47
- if (targetBranch.includes(';') ||
48
- targetBranch.includes('|') ||
49
- targetBranch.includes('&') ||
50
- targetBranch.includes('$') ||
51
- targetBranch.includes('`') ||
52
- targetBranch.includes('\\') ||
53
- targetBranch.includes('\n') ||
54
- targetBranch.includes('\r') ||
55
- targetBranch.includes('\0') ||
56
- targetBranch.trim() === '') {
57
- logger.error(`Invalid uploadBranch value in config: "${targetBranch}"`);
58
- process.exit(1);
59
- }
60
41
  // Step 1: Determine which plan to upload
61
42
  let targetPlan = null;
62
43
  if (planPath) {
@@ -99,61 +80,21 @@ export async function executeUpload(config, planPath) {
99
80
  }
100
81
  }
101
82
  logger.debug('Plan file content read into memory');
102
- // Step 3: Get repo info and create worktree service
103
- const repoInfo = await getRepoInfo();
104
- const worktreeService = new WorktreeService({
105
- basePath: worktreeBasePath,
106
- repoName: repoInfo.name,
107
- });
108
- // Use a timestamp-based identifier to avoid collisions between concurrent uploads
109
- const identifier = `${Date.now()}`;
110
- const worktreePath = worktreeService.getWorktreeDir('upload', identifier);
111
- logger.info('Creating temporary upload worktree...');
112
- let worktreeCreated = false;
83
+ // Step 3: Publish the plan file to the target branch via a temporary
84
+ // detached worktree (shared implementation with the /hula-execute skill).
85
+ const publisher = new FilePublishService(config);
86
+ const spinner = logger.spinner(`Uploading plan to origin/${targetBranch}...`);
113
87
  try {
114
- // Step 4: Create a temporary worktree off origin/main (detached HEAD)
115
- await worktreeService.createWorktreeAt(worktreePath, `origin/${targetBranch}`, true);
116
- worktreeCreated = true;
117
- // Step 5: Copy plan file into the worktree at the same relative path
118
- logger.info('Writing plan file to upload worktree...');
119
- const worktreePlanPath = join(worktreePath, relativePath);
120
- mkdirSync(dirname(worktreePlanPath), { recursive: true });
121
- writeFileSync(worktreePlanPath, planContent, 'utf-8');
122
- // Step 6: Stage and commit in the worktree
123
- logger.info('Committing plan file in upload worktree...');
124
- const hasChanges = await worktreeService.hasChangesAt(worktreePath);
125
- if (hasChanges) {
126
- await worktreeService.commitChangesAt(worktreePath, 'chore: upload plan via hula');
127
- }
128
- else {
129
- logger.info('Plan already up-to-date, proceeding to push...');
130
- }
131
- // Step 7: Push from worktree
132
- const spinner = logger.spinner(`Pushing to origin/${targetBranch}...`);
133
- try {
134
- // For a detached HEAD worktree we push HEAD directly to the target branch
135
- await worktreeService.pushAt(worktreePath, `HEAD:${targetBranch}`);
136
- spinner.stop();
137
- logger.success(`Plan uploaded to origin/${targetBranch}`);
138
- }
139
- catch (error) {
140
- spinner.stop();
141
- throw error;
142
- }
88
+ await publisher.publishFile(relativePath, planContent, {
89
+ commitMessage: 'chore: upload plan via hula',
90
+ branch: targetBranch,
91
+ });
92
+ spinner.stop();
93
+ logger.success(`Plan uploaded to origin/${targetBranch}`);
143
94
  }
144
- finally {
145
- // Step 8: Always remove the temporary worktree
146
- if (worktreeCreated) {
147
- logger.info('Cleaning up upload worktree...');
148
- try {
149
- await worktreeService.removeWorktreeAt(worktreePath);
150
- logger.debug('Upload worktree removed');
151
- }
152
- catch (error) {
153
- logger.warning(`Failed to remove upload worktree at ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`);
154
- logger.info(`You can manually remove it with: git worktree remove "${worktreePath}" --force`);
155
- }
156
- }
95
+ catch (error) {
96
+ spinner.stop();
97
+ throw error;
157
98
  }
158
99
  // Plan file remains untouched in the main working tree
159
100
  // Note: the file will be cleaned up later by launch/create after success
@@ -1 +1 @@
1
- {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -34,14 +34,17 @@ console.error = (...args) => {
34
34
  }
35
35
  return originalConsoleError.apply(console, args);
36
36
  };
37
- import { createRequire } from 'module';
37
+ import { existsSync } from 'fs';
38
+ import { join } from 'path';
38
39
  import { Command } from 'commander';
39
40
  import { logger } from './utils/logger.js';
40
41
  import { validateGhCli } from './utils/github-cli.js';
41
42
  import { loadConfig } from './config/index.js';
42
43
  import { githubAppService } from './services/github/GithubAppService.js';
43
- const require = createRequire(import.meta.url);
44
- const { version } = require('../package.json');
44
+ import { getCliVersion, isReinitNeeded } from './utils/version.js';
45
+ // Single source of truth for the running CLI version (used for --version and
46
+ // the runtime staleness check). Falls back to '0.0.0' only for program.version.
47
+ const version = getCliVersion() ?? '0.0.0';
45
48
  // Import commands
46
49
  import { initCommand } from './commands/init.js';
47
50
  import { mergeCommand } from './commands/merge.js';
@@ -93,6 +96,34 @@ async function main() {
93
96
  const config = await loadConfig(projectRoot);
94
97
  // Stop spinner once loaded
95
98
  spinner?.stop();
99
+ // Non-blocking reminder: if the project was initialized with a different
100
+ // CLI version, prompt the user to re-run `hula init` to refresh the bundled
101
+ // skills/templates/config. Wrapped in try/catch so it can never break the
102
+ // command. Excluded for `init` (the command that fixes it) and help/version.
103
+ if (!isHelpOrVersion && args[0] !== 'init') {
104
+ try {
105
+ const cliVersion = getCliVersion();
106
+ const configFileExists = [
107
+ 'hublaunch.config.ts',
108
+ 'hublaunch.config.mjs',
109
+ 'hublaunch.config.js',
110
+ ].some((f) => existsSync(join(projectRoot, '.hublaunch', f)));
111
+ if (isReinitNeeded({
112
+ configFileExists,
113
+ configVersion: config.version,
114
+ cliVersion,
115
+ })) {
116
+ const recorded = config.version
117
+ ? `v${config.version}`
118
+ : 'an older version';
119
+ logger.warning(`hub-launch was updated to v${cliVersion} (this project was initialized with ${recorded}).`);
120
+ logger.warning('Run `hula init` to refresh skills, templates, and config.');
121
+ }
122
+ }
123
+ catch {
124
+ // Never let the reminder break the CLI.
125
+ }
126
+ }
96
127
  // Check for GitHub App installation (non-intrusive, runs in background)
97
128
  // Skip for help/version/login commands
98
129
  if (!isLoginCommand && config.hulaApiKey) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { Config } from '../../types/index.js';
2
+ /**
3
+ * Options shared by publish/delete operations.
4
+ */
5
+ export interface FilePublishOptions {
6
+ /** Commit message to use for the change. */
7
+ commitMessage?: string;
8
+ /** Target branch to publish to. Defaults to `config.uploadBranch ?? 'main'`. */
9
+ branch?: string;
10
+ }
11
+ /**
12
+ * FilePublishService — commit a single repo-relative file to a remote branch
13
+ * (default `origin/main`) using a temporary detached worktree, without ever
14
+ * touching the user's current branch or working tree.
15
+ *
16
+ * This encapsulates the worktree publish/delete dance that was previously
17
+ * inlined in `executeUpload`. Both plan upload and `/hula-execute` skill
18
+ * publishing share this one tested implementation.
19
+ *
20
+ * The flow for each operation is:
21
+ * 1. validate the target branch (injection guard)
22
+ * 2. resolve an absolute worktree base path
23
+ * 3. create a detached worktree off `origin/<branch>`
24
+ * 4. write (publish) or remove (delete) the file at `<worktree>/<relativePath>`
25
+ * 5. stage + commit (`git add .` stages deletions too) when there are changes
26
+ * 6. push `HEAD:<branch>`
27
+ * 7. always remove the worktree in `finally`
28
+ */
29
+ export declare class FilePublishService {
30
+ private readonly config;
31
+ constructor(config: Config);
32
+ /**
33
+ * Default branch this service publishes to.
34
+ */
35
+ private defaultBranch;
36
+ /**
37
+ * Validate a branch value to prevent command injection in the git push
38
+ * refspec. Mirrors the guard in `executeUpload`.
39
+ */
40
+ private validateBranch;
41
+ /**
42
+ * Resolve the absolute worktree base path from config, identical to
43
+ * `executeUpload`'s behavior.
44
+ */
45
+ private resolveWorktreeBasePath;
46
+ /**
47
+ * Run `operation` inside a fresh detached worktree off `origin/<branch>`,
48
+ * commit (with `commitMessage`) if it produced changes, push to the branch,
49
+ * and always remove the worktree afterward.
50
+ */
51
+ private withWorktree;
52
+ /**
53
+ * Publish (create or update) a file on the target branch.
54
+ *
55
+ * @param relativePath - Repo-relative path of the file to write.
56
+ * @param content - The full content to write.
57
+ * @param opts - Optional commit message and branch override.
58
+ */
59
+ publishFile(relativePath: string, content: string, opts?: FilePublishOptions): Promise<void>;
60
+ /**
61
+ * Delete a file from the target branch.
62
+ *
63
+ * @param relativePath - Repo-relative path of the file to remove.
64
+ * @param opts - Optional commit message and branch override.
65
+ */
66
+ deleteFile(relativePath: string, opts?: FilePublishOptions): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=FilePublishService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePublishService.d.ts","sourceRoot":"","sources":["../../../src/services/git/FilePublishService.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAKnD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM;IAI1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAiBtB;;;OAGG;YACW,uBAAuB;IAQrC;;;;OAIG;YACW,YAAY;IAuD1B;;;;;;OAMG;IACG,WAAW,CACf,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;OAKG;IACG,UAAU,CACd,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC;CAWjB"}
@@ -0,0 +1,141 @@
1
+ import { dirname, isAbsolute, join, resolve } from 'path';
2
+ import { mkdirSync, rmSync, writeFileSync } from 'fs';
3
+ import { logger } from '../../utils/logger.js';
4
+ import { WorktreeService } from './WorktreeService.js';
5
+ import { getRepoInfo } from '../../utils/github-cli.js';
6
+ import { getRepoRoot } from '../../utils/shell.js';
7
+ /**
8
+ * FilePublishService — commit a single repo-relative file to a remote branch
9
+ * (default `origin/main`) using a temporary detached worktree, without ever
10
+ * touching the user's current branch or working tree.
11
+ *
12
+ * This encapsulates the worktree publish/delete dance that was previously
13
+ * inlined in `executeUpload`. Both plan upload and `/hula-execute` skill
14
+ * publishing share this one tested implementation.
15
+ *
16
+ * The flow for each operation is:
17
+ * 1. validate the target branch (injection guard)
18
+ * 2. resolve an absolute worktree base path
19
+ * 3. create a detached worktree off `origin/<branch>`
20
+ * 4. write (publish) or remove (delete) the file at `<worktree>/<relativePath>`
21
+ * 5. stage + commit (`git add .` stages deletions too) when there are changes
22
+ * 6. push `HEAD:<branch>`
23
+ * 7. always remove the worktree in `finally`
24
+ */
25
+ export class FilePublishService {
26
+ config;
27
+ constructor(config) {
28
+ this.config = config;
29
+ }
30
+ /**
31
+ * Default branch this service publishes to.
32
+ */
33
+ defaultBranch() {
34
+ return this.config.uploadBranch ?? 'main';
35
+ }
36
+ /**
37
+ * Validate a branch value to prevent command injection in the git push
38
+ * refspec. Mirrors the guard in `executeUpload`.
39
+ */
40
+ validateBranch(branch) {
41
+ if (branch.includes(';') ||
42
+ branch.includes('|') ||
43
+ branch.includes('&') ||
44
+ branch.includes('$') ||
45
+ branch.includes('`') ||
46
+ branch.includes('\\') ||
47
+ branch.includes('\n') ||
48
+ branch.includes('\r') ||
49
+ branch.includes('\0') ||
50
+ branch.trim() === '') {
51
+ throw new Error(`Invalid branch value: "${branch}"`);
52
+ }
53
+ }
54
+ /**
55
+ * Resolve the absolute worktree base path from config, identical to
56
+ * `executeUpload`'s behavior.
57
+ */
58
+ async resolveWorktreeBasePath() {
59
+ const rawBasePath = this.config.worktreeBasePath ?? '.hula-worktrees';
60
+ const repoRoot = await getRepoRoot();
61
+ return isAbsolute(rawBasePath)
62
+ ? rawBasePath
63
+ : resolve(repoRoot, rawBasePath);
64
+ }
65
+ /**
66
+ * Run `operation` inside a fresh detached worktree off `origin/<branch>`,
67
+ * commit (with `commitMessage`) if it produced changes, push to the branch,
68
+ * and always remove the worktree afterward.
69
+ */
70
+ async withWorktree(type, branch, commitMessage, operation) {
71
+ this.validateBranch(branch);
72
+ const worktreeBasePath = await this.resolveWorktreeBasePath();
73
+ const repoInfo = await getRepoInfo();
74
+ const worktreeService = new WorktreeService({
75
+ basePath: worktreeBasePath,
76
+ repoName: repoInfo.name,
77
+ });
78
+ // Timestamp-based identifier to avoid collisions between concurrent publishes.
79
+ const identifier = `${Date.now()}`;
80
+ const worktreePath = worktreeService.getWorktreeDir(type, identifier);
81
+ let worktreeCreated = false;
82
+ try {
83
+ await worktreeService.createWorktreeAt(worktreePath, `origin/${branch}`, true);
84
+ worktreeCreated = true;
85
+ operation(worktreePath);
86
+ const hasChanges = await worktreeService.hasChangesAt(worktreePath);
87
+ if (hasChanges) {
88
+ await worktreeService.commitChangesAt(worktreePath, commitMessage);
89
+ }
90
+ else {
91
+ logger.info('No changes to commit, proceeding to push...');
92
+ }
93
+ // For a detached HEAD worktree we push HEAD directly to the target branch.
94
+ await worktreeService.pushAt(worktreePath, `HEAD:${branch}`);
95
+ }
96
+ finally {
97
+ if (worktreeCreated) {
98
+ try {
99
+ await worktreeService.removeWorktreeAt(worktreePath);
100
+ }
101
+ catch (error) {
102
+ logger.warning(`Failed to remove worktree at ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`);
103
+ logger.info(`You can manually remove it with: git worktree remove "${worktreePath}" --force`);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ /**
109
+ * Publish (create or update) a file on the target branch.
110
+ *
111
+ * @param relativePath - Repo-relative path of the file to write.
112
+ * @param content - The full content to write.
113
+ * @param opts - Optional commit message and branch override.
114
+ */
115
+ async publishFile(relativePath, content, opts) {
116
+ const branch = opts?.branch ?? this.defaultBranch();
117
+ const commitMessage = opts?.commitMessage ?? 'chore: publish file via hula';
118
+ await this.withWorktree('publish', branch, commitMessage, (worktreePath) => {
119
+ const filePath = join(worktreePath, relativePath);
120
+ mkdirSync(dirname(filePath), { recursive: true });
121
+ writeFileSync(filePath, content, 'utf-8');
122
+ });
123
+ }
124
+ /**
125
+ * Delete a file from the target branch.
126
+ *
127
+ * @param relativePath - Repo-relative path of the file to remove.
128
+ * @param opts - Optional commit message and branch override.
129
+ */
130
+ async deleteFile(relativePath, opts) {
131
+ const branch = opts?.branch ?? this.defaultBranch();
132
+ const commitMessage = opts?.commitMessage ?? 'chore: remove file via hula';
133
+ await this.withWorktree('publish', branch, commitMessage, (worktreePath) => {
134
+ const filePath = join(worktreePath, relativePath);
135
+ // `commitChangesAt` runs `git add .`, which stages deletions, so a plain
136
+ // filesystem remove is sufficient here.
137
+ rmSync(filePath, { force: true });
138
+ });
139
+ }
140
+ }
141
+ //# sourceMappingURL=FilePublishService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePublishService.js","sourceRoot":"","sources":["../../../src/services/git/FilePublishService.ts"],"names":[],"mappings":"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"}
@@ -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