hub-launch 1.0.3
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 +144 -0
- package/README.md +195 -0
- package/bin/hublaunch +20 -0
- package/dist/commands/checkout.d.ts +7 -0
- package/dist/commands/checkout.d.ts.map +1 -0
- package/dist/commands/checkout.js +164 -0
- package/dist/commands/checkout.js.map +1 -0
- package/dist/commands/clean.d.ts +7 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +206 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/close.d.ts +7 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +109 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +367 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/diff.d.ts +7 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +98 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/execute.d.ts +7 -0
- package/dist/commands/execute.d.ts.map +1 -0
- package/dist/commands/execute.js +376 -0
- package/dist/commands/execute.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1243 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/interactive.d.ts +12 -0
- package/dist/commands/interactive.d.ts.map +1 -0
- package/dist/commands/interactive.js +1071 -0
- package/dist/commands/interactive.js.map +1 -0
- package/dist/commands/launch.d.ts +16 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +684 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/login.d.ts +40 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +469 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +22 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +105 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/merge.d.ts +7 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +240 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/preview.d.ts +7 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +160 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/project.d.ts +18 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +352 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/respond.d.ts +7 -0
- package/dist/commands/respond.d.ts.map +1 -0
- package/dist/commands/respond.js +112 -0
- package/dist/commands/respond.js.map +1 -0
- package/dist/commands/setPriority.d.ts +7 -0
- package/dist/commands/setPriority.d.ts.map +1 -0
- package/dist/commands/setPriority.js +112 -0
- package/dist/commands/setPriority.js.map +1 -0
- package/dist/commands/track.d.ts +7 -0
- package/dist/commands/track.d.ts.map +1 -0
- package/dist/commands/track.js +140 -0
- package/dist/commands/track.js.map +1 -0
- package/dist/commands/upload.d.ts +21 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +145 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/commands/view.d.ts +7 -0
- package/dist/commands/view.d.ts.map +1 -0
- package/dist/commands/view.js +168 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/commands/watch.d.ts +7 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +438 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/worktree.d.ts +7 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +122 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/config/command-names.d.ts +16 -0
- package/dist/config/command-names.d.ts.map +1 -0
- package/dist/config/command-names.js +17 -0
- package/dist/config/command-names.js.map +1 -0
- package/dist/config/constants.d.ts +12 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +12 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/index.d.ts +47 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +175 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/services/api/HulaApiClient.d.ts +170 -0
- package/dist/services/api/HulaApiClient.d.ts.map +1 -0
- package/dist/services/api/HulaApiClient.js +233 -0
- package/dist/services/api/HulaApiClient.js.map +1 -0
- package/dist/services/diff/DiffService.d.ts +44 -0
- package/dist/services/diff/DiffService.d.ts.map +1 -0
- package/dist/services/diff/DiffService.js +126 -0
- package/dist/services/diff/DiffService.js.map +1 -0
- package/dist/services/editor/EditorService.d.ts +53 -0
- package/dist/services/editor/EditorService.d.ts.map +1 -0
- package/dist/services/editor/EditorService.js +161 -0
- package/dist/services/editor/EditorService.js.map +1 -0
- package/dist/services/git/GitService.d.ts +124 -0
- package/dist/services/git/GitService.d.ts.map +1 -0
- package/dist/services/git/GitService.js +423 -0
- package/dist/services/git/GitService.js.map +1 -0
- package/dist/services/git/WorktreeService.d.ts +144 -0
- package/dist/services/git/WorktreeService.d.ts.map +1 -0
- package/dist/services/git/WorktreeService.js +505 -0
- package/dist/services/git/WorktreeService.js.map +1 -0
- package/dist/services/github/ActionsService.d.ts +66 -0
- package/dist/services/github/ActionsService.d.ts.map +1 -0
- package/dist/services/github/ActionsService.js +114 -0
- package/dist/services/github/ActionsService.js.map +1 -0
- package/dist/services/github/AssetUploadService.d.ts +103 -0
- package/dist/services/github/AssetUploadService.d.ts.map +1 -0
- package/dist/services/github/AssetUploadService.js +299 -0
- package/dist/services/github/AssetUploadService.js.map +1 -0
- package/dist/services/github/BatchCopilotDetectionService.d.ts +58 -0
- package/dist/services/github/BatchCopilotDetectionService.d.ts.map +1 -0
- package/dist/services/github/BatchCopilotDetectionService.js +321 -0
- package/dist/services/github/BatchCopilotDetectionService.js.map +1 -0
- package/dist/services/github/CopilotDetectionService.d.ts +40 -0
- package/dist/services/github/CopilotDetectionService.d.ts.map +1 -0
- package/dist/services/github/CopilotDetectionService.js +280 -0
- package/dist/services/github/CopilotDetectionService.js.map +1 -0
- package/dist/services/github/CopilotService.d.ts +27 -0
- package/dist/services/github/CopilotService.d.ts.map +1 -0
- package/dist/services/github/CopilotService.js +249 -0
- package/dist/services/github/CopilotService.js.map +1 -0
- package/dist/services/github/DeploymentDetectionService.d.ts +38 -0
- package/dist/services/github/DeploymentDetectionService.d.ts.map +1 -0
- package/dist/services/github/DeploymentDetectionService.js +104 -0
- package/dist/services/github/DeploymentDetectionService.js.map +1 -0
- package/dist/services/github/GithubAppService.d.ts +40 -0
- package/dist/services/github/GithubAppService.d.ts.map +1 -0
- package/dist/services/github/GithubAppService.js +187 -0
- package/dist/services/github/GithubAppService.js.map +1 -0
- package/dist/services/github/IssueService.d.ts +65 -0
- package/dist/services/github/IssueService.d.ts.map +1 -0
- package/dist/services/github/IssueService.js +231 -0
- package/dist/services/github/IssueService.js.map +1 -0
- package/dist/services/github/LabelService.d.ts +36 -0
- package/dist/services/github/LabelService.d.ts.map +1 -0
- package/dist/services/github/LabelService.js +122 -0
- package/dist/services/github/LabelService.js.map +1 -0
- package/dist/services/github/PRService.d.ts +77 -0
- package/dist/services/github/PRService.d.ts.map +1 -0
- package/dist/services/github/PRService.js +287 -0
- package/dist/services/github/PRService.js.map +1 -0
- package/dist/services/github/ProjectService.d.ts +144 -0
- package/dist/services/github/ProjectService.d.ts.map +1 -0
- package/dist/services/github/ProjectService.js +804 -0
- package/dist/services/github/ProjectService.js.map +1 -0
- package/dist/services/hooks/HookExecutor.d.ts +66 -0
- package/dist/services/hooks/HookExecutor.d.ts.map +1 -0
- package/dist/services/hooks/HookExecutor.js +227 -0
- package/dist/services/hooks/HookExecutor.js.map +1 -0
- package/dist/services/hooks/index.d.ts +2 -0
- package/dist/services/hooks/index.d.ts.map +1 -0
- package/dist/services/hooks/index.js +2 -0
- package/dist/services/hooks/index.js.map +1 -0
- package/dist/services/logs/LogFormatterService.d.ts +17 -0
- package/dist/services/logs/LogFormatterService.d.ts.map +1 -0
- package/dist/services/logs/LogFormatterService.js +35 -0
- package/dist/services/logs/LogFormatterService.js.map +1 -0
- package/dist/services/logs/TempFileService.d.ts +27 -0
- package/dist/services/logs/TempFileService.d.ts.map +1 -0
- package/dist/services/logs/TempFileService.js +90 -0
- package/dist/services/logs/TempFileService.js.map +1 -0
- package/dist/services/logs/index.d.ts +3 -0
- package/dist/services/logs/index.d.ts.map +1 -0
- package/dist/services/logs/index.js +4 -0
- package/dist/services/logs/index.js.map +1 -0
- package/dist/services/plan/PlanService.d.ts +70 -0
- package/dist/services/plan/PlanService.d.ts.map +1 -0
- package/dist/services/plan/PlanService.js +211 -0
- package/dist/services/plan/PlanService.js.map +1 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.d.ts +16 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.js +87 -0
- package/dist/services/polling/GitHubDirectPollingAdapter.js.map +1 -0
- package/dist/services/polling/HulaServerPollingAdapter.d.ts +23 -0
- package/dist/services/polling/HulaServerPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/HulaServerPollingAdapter.js +78 -0
- package/dist/services/polling/HulaServerPollingAdapter.js.map +1 -0
- package/dist/services/polling/IStatusPollingAdapter.d.ts +29 -0
- package/dist/services/polling/IStatusPollingAdapter.d.ts.map +1 -0
- package/dist/services/polling/IStatusPollingAdapter.js +2 -0
- package/dist/services/polling/IStatusPollingAdapter.js.map +1 -0
- package/dist/services/polling/StatusPollingAdapterFactory.d.ts +14 -0
- package/dist/services/polling/StatusPollingAdapterFactory.d.ts.map +1 -0
- package/dist/services/polling/StatusPollingAdapterFactory.js +41 -0
- package/dist/services/polling/StatusPollingAdapterFactory.js.map +1 -0
- package/dist/services/polling/index.d.ts +5 -0
- package/dist/services/polling/index.d.ts.map +1 -0
- package/dist/services/polling/index.js +5 -0
- package/dist/services/polling/index.js.map +1 -0
- package/dist/services/preview/PreviewService.d.ts +86 -0
- package/dist/services/preview/PreviewService.d.ts.map +1 -0
- package/dist/services/preview/PreviewService.js +439 -0
- package/dist/services/preview/PreviewService.js.map +1 -0
- package/dist/services/tracking/ProjectTrackingAdapter.d.ts +41 -0
- package/dist/services/tracking/ProjectTrackingAdapter.d.ts.map +1 -0
- package/dist/services/tracking/ProjectTrackingAdapter.js +230 -0
- package/dist/services/tracking/ProjectTrackingAdapter.js.map +1 -0
- package/dist/services/tracking/RemoteTrackingAdapter.d.ts +62 -0
- package/dist/services/tracking/RemoteTrackingAdapter.d.ts.map +1 -0
- package/dist/services/tracking/RemoteTrackingAdapter.js +242 -0
- package/dist/services/tracking/RemoteTrackingAdapter.js.map +1 -0
- package/dist/services/tracking/TrackingAdapterFactory.d.ts +25 -0
- package/dist/services/tracking/TrackingAdapterFactory.d.ts.map +1 -0
- package/dist/services/tracking/TrackingAdapterFactory.js +55 -0
- package/dist/services/tracking/TrackingAdapterFactory.js.map +1 -0
- package/dist/services/tracking/TrackingService.d.ts +104 -0
- package/dist/services/tracking/TrackingService.d.ts.map +1 -0
- package/dist/services/tracking/TrackingService.js +256 -0
- package/dist/services/tracking/TrackingService.js.map +1 -0
- package/dist/services/tracking/UntrackedIssuesService.d.ts +20 -0
- package/dist/services/tracking/UntrackedIssuesService.d.ts.map +1 -0
- package/dist/services/tracking/UntrackedIssuesService.js +148 -0
- package/dist/services/tracking/UntrackedIssuesService.js.map +1 -0
- package/dist/services/tracking/local-file-migration.d.ts +16 -0
- package/dist/services/tracking/local-file-migration.d.ts.map +1 -0
- package/dist/services/tracking/local-file-migration.js +86 -0
- package/dist/services/tracking/local-file-migration.js.map +1 -0
- package/dist/templates/planning-instructions.md +752 -0
- package/dist/templates/proceed-instructions.md +453 -0
- package/dist/templates/scripts/hula-fix-commit.sh +110 -0
- package/dist/templates/scripts/hula-fix-setup.sh +145 -0
- package/dist/templates/scripts/hula-launch-run.sh +89 -0
- package/dist/templates/scripts/hula-merge-local.sh +138 -0
- package/dist/templates/scripts/hula-merge-remote.sh +74 -0
- package/dist/templates/scripts/hula-read-config.sh +38 -0
- package/dist/templates/scripts/hula-verify-gather.sh +135 -0
- package/dist/templates/scripts/hula-verify-post.sh +84 -0
- package/dist/templates/skills/hula-confirm/SKILL.md +361 -0
- package/dist/templates/skills/hula-create/SKILL.md +211 -0
- package/dist/templates/skills/hula-fix/SKILL.md +224 -0
- package/dist/templates/skills/hula-launch/SKILL.md +159 -0
- package/dist/templates/skills/hula-log/SKILL.md +105 -0
- package/dist/templates/skills/hula-merge/SKILL.md +387 -0
- package/dist/templates/skills/hula-plan/SKILL.md +148 -0
- package/dist/templates/skills/hula-upload/SKILL.md +115 -0
- package/dist/templates/skills/hula-verify/SKILL.md +754 -0
- package/dist/types/common.schema.d.ts +46 -0
- package/dist/types/common.schema.d.ts.map +1 -0
- package/dist/types/common.schema.js +60 -0
- package/dist/types/common.schema.js.map +1 -0
- package/dist/types/config.schema.d.ts +443 -0
- package/dist/types/config.schema.d.ts.map +1 -0
- package/dist/types/config.schema.js +126 -0
- package/dist/types/config.schema.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/issue.schema.d.ts +136 -0
- package/dist/types/issue.schema.d.ts.map +1 -0
- package/dist/types/issue.schema.js +49 -0
- package/dist/types/issue.schema.js.map +1 -0
- package/dist/types/pr.schema.d.ts +118 -0
- package/dist/types/pr.schema.d.ts.map +1 -0
- package/dist/types/pr.schema.js +40 -0
- package/dist/types/pr.schema.js.map +1 -0
- package/dist/types/tracking.schema.d.ts +340 -0
- package/dist/types/tracking.schema.d.ts.map +1 -0
- package/dist/types/tracking.schema.js +56 -0
- package/dist/types/tracking.schema.js.map +1 -0
- package/dist/utils/browser-launcher.d.ts +60 -0
- package/dist/utils/browser-launcher.d.ts.map +1 -0
- package/dist/utils/browser-launcher.js +263 -0
- package/dist/utils/browser-launcher.js.map +1 -0
- package/dist/utils/cache.d.ts +44 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +94 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config-parser.d.ts +16 -0
- package/dist/utils/config-parser.d.ts.map +1 -0
- package/dist/utils/config-parser.js +210 -0
- package/dist/utils/config-parser.js.map +1 -0
- package/dist/utils/editor.d.ts +21 -0
- package/dist/utils/editor.d.ts.map +1 -0
- package/dist/utils/editor.js +213 -0
- package/dist/utils/editor.js.map +1 -0
- package/dist/utils/git-utils.d.ts +5 -0
- package/dist/utils/git-utils.d.ts.map +1 -0
- package/dist/utils/git-utils.js +18 -0
- package/dist/utils/git-utils.js.map +1 -0
- package/dist/utils/github-cli.d.ts +62 -0
- package/dist/utils/github-cli.d.ts.map +1 -0
- package/dist/utils/github-cli.js +492 -0
- package/dist/utils/github-cli.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +118 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/open-url.d.ts +19 -0
- package/dist/utils/open-url.d.ts.map +1 -0
- package/dist/utils/open-url.js +37 -0
- package/dist/utils/open-url.js.map +1 -0
- package/dist/utils/project-resolver.d.ts +13 -0
- package/dist/utils/project-resolver.d.ts.map +1 -0
- package/dist/utils/project-resolver.js +35 -0
- package/dist/utils/project-resolver.js.map +1 -0
- package/dist/utils/prompts.d.ts +80 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +326 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/shell.d.ts +54 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +152 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/validators.d.ts +32 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +64 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-fix-setup.sh — Steps 2-6 of the hula.fix workflow
|
|
3
|
+
# Usage: bash .github/scripts/hula-fix-setup.sh <issue-number> [--repo-root <path>]
|
|
4
|
+
#
|
|
5
|
+
# Performs: issue lookup → worktree creation → session file writing
|
|
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
|
+
# Escape a string for safe embedding inside a JSON double-quoted value
|
|
15
|
+
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/g' | tr -d '\n' | sed 's/\\n$//'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
die() {
|
|
19
|
+
local code="$1"; shift
|
|
20
|
+
local safe_msg
|
|
21
|
+
safe_msg=$(json_escape "$*")
|
|
22
|
+
printf '{"status":"error","code":%d,"message":"%s"}\n' "$code" "$safe_msg" >&1
|
|
23
|
+
exit "$code"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
emit_json() {
|
|
27
|
+
# emit_json key value [key value ...] — values are already JSON-safe strings
|
|
28
|
+
local out='{'
|
|
29
|
+
local sep=''
|
|
30
|
+
while [[ $# -ge 2 ]]; do
|
|
31
|
+
local k="$1" v="$2"; shift 2
|
|
32
|
+
out="${out}${sep}\"${k}\":\"${v}\""
|
|
33
|
+
sep=','
|
|
34
|
+
done
|
|
35
|
+
printf '%s}\n' "$out"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# ── argument parsing ────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
ISSUE_NUMBER=''
|
|
41
|
+
REPO_ROOT=''
|
|
42
|
+
|
|
43
|
+
while [[ $# -gt 0 ]]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--repo-root)
|
|
46
|
+
REPO_ROOT="$2"; shift 2 ;;
|
|
47
|
+
--repo-root=*)
|
|
48
|
+
REPO_ROOT="${1#--repo-root=}"; shift ;;
|
|
49
|
+
[0-9]*)
|
|
50
|
+
ISSUE_NUMBER="$1"; shift ;;
|
|
51
|
+
*)
|
|
52
|
+
die 1 "Unknown argument: $1" ;;
|
|
53
|
+
esac
|
|
54
|
+
done
|
|
55
|
+
|
|
56
|
+
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
57
|
+
die 1 "Usage: bash .github/scripts/hula-fix-setup.sh <issue-number> [--repo-root <path>]"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ -n "$REPO_ROOT" ]]; then
|
|
61
|
+
cd "$REPO_ROOT"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# ── Step 1: Infer owner/repo and repo name from git remote ─────────────────────
|
|
65
|
+
|
|
66
|
+
NAME_WITH_OWNER=$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null) \
|
|
67
|
+
|| die 2 "Failed to determine repo from git remote. Is this a GitHub repo?"
|
|
68
|
+
|
|
69
|
+
REPO_NAME=$(gh repo view --json name -q '.name' 2>/dev/null) \
|
|
70
|
+
|| die 2 "Failed to determine repo name."
|
|
71
|
+
|
|
72
|
+
# ── Step 2: Fetch issue details ────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" --json title,body,labels 2>/dev/null) \
|
|
75
|
+
|| die 1 "Issue #${ISSUE_NUMBER} not found or not accessible."
|
|
76
|
+
|
|
77
|
+
ISSUE_TITLE=$(printf '%s' "$ISSUE_JSON" | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"$//')
|
|
78
|
+
|
|
79
|
+
# ── Step 3: Validate issue is tracked and has a PR (via hula view) ─────────────
|
|
80
|
+
|
|
81
|
+
HULA_VIEW=$(hula view 2>&1) || true
|
|
82
|
+
|
|
83
|
+
if printf '%s' "$HULA_VIEW" | grep -qi "not tracked\|no issues"; then
|
|
84
|
+
die 1 "Issue #${ISSUE_NUMBER} is not tracked. Run: hula track ${ISSUE_NUMBER}"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# ── Step 4: Find PR branch for the issue ──────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
PR_BRANCH=$(gh pr list --search "linked:${ISSUE_NUMBER}" --json headRefName -q '.[0].headRefName' 2>/dev/null) || true
|
|
90
|
+
|
|
91
|
+
if [[ -z "$PR_BRANCH" ]]; then
|
|
92
|
+
# Fallback: search PR bodies for issue references
|
|
93
|
+
# Handles both short format (#N) and full-path format (owner/repo#N)
|
|
94
|
+
# Uses gh's built-in jq (-q) — no external jq required
|
|
95
|
+
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")) | .headRefName'
|
|
96
|
+
PR_BRANCH=$(gh pr list --json headRefName,body --limit 100 -q "$JQ_EXPR" 2>/dev/null | head -1) || true
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if [[ -z "$PR_BRANCH" ]]; then
|
|
100
|
+
die 1 "No PR found for issue #${ISSUE_NUMBER}. Create a PR first with: hula checkout ${ISSUE_NUMBER}"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# ── Step 5: Determine worktree path ───────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
106
|
+
source "${SCRIPT_DIR}/hula-read-config.sh"
|
|
107
|
+
WORKTREE_PATH="${HULA_WORKTREE_BASE_PATH}/hula/${REPO_NAME}/fix-${ISSUE_NUMBER}"
|
|
108
|
+
|
|
109
|
+
# ── Step 6: Create or reuse the worktree ─────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
WORKTREE_STATUS='reused'
|
|
112
|
+
|
|
113
|
+
if git worktree list --porcelain 2>/dev/null | grep -qxF "worktree ${WORKTREE_PATH}"; then
|
|
114
|
+
printf 'ℹ️ Resuming existing fix worktree at %s\n' "$WORKTREE_PATH" >&2
|
|
115
|
+
else
|
|
116
|
+
printf '🌳 Creating fix worktree at: %s\n' "$WORKTREE_PATH" >&2
|
|
117
|
+
git fetch origin >&2 2>&1 || die 2 "git fetch origin failed"
|
|
118
|
+
git worktree add "$WORKTREE_PATH" "$PR_BRANCH" >&2 2>&1 \
|
|
119
|
+
|| die 2 "Failed to create worktree at ${WORKTREE_PATH}. Check that branch ${PR_BRANCH} exists."
|
|
120
|
+
WORKTREE_STATUS='created'
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# ── Step 7: Write session file ────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
REPO_ROOT_SESSION=$(git rev-parse --show-toplevel 2>/dev/null) || REPO_ROOT_SESSION="$(pwd)"
|
|
126
|
+
SESSION_DIR="${REPO_ROOT_SESSION}/.hublaunch/.fix-sessions"
|
|
127
|
+
mkdir -p "$SESSION_DIR"
|
|
128
|
+
|
|
129
|
+
STARTED_AT=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
130
|
+
|
|
131
|
+
SESSION_FILE="${SESSION_DIR}/issue-${ISSUE_NUMBER}.json"
|
|
132
|
+
SAFE_WT_SESSION=$(json_escape "$WORKTREE_PATH")
|
|
133
|
+
SAFE_BR_SESSION=$(json_escape "$PR_BRANCH")
|
|
134
|
+
printf '{\n "issueNumber": %s,\n "worktreePath": "%s",\n "prBranch": "%s",\n "startedAt": "%s"\n}\n' \
|
|
135
|
+
"$ISSUE_NUMBER" "$SAFE_WT_SESSION" "$SAFE_BR_SESSION" "$STARTED_AT" > "$SESSION_FILE"
|
|
136
|
+
|
|
137
|
+
# ── Output result ─────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
SAFE_TITLE=$(json_escape "$ISSUE_TITLE")
|
|
140
|
+
SAFE_BRANCH=$(json_escape "$PR_BRANCH")
|
|
141
|
+
SAFE_WORKTREE=$(json_escape "$WORKTREE_PATH")
|
|
142
|
+
SAFE_SESSION=$(json_escape "$SESSION_FILE")
|
|
143
|
+
|
|
144
|
+
printf '{"status":"success","issueNumber":%s,"issueTitle":"%s","prBranch":"%s","worktreePath":"%s","sessionFile":"%s","worktreeStatus":"%s"}\n' \
|
|
145
|
+
"$ISSUE_NUMBER" "$SAFE_TITLE" "$SAFE_BRANCH" "$SAFE_WORKTREE" "$SAFE_SESSION" "$WORKTREE_STATUS"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-launch-run.sh — Step 4 of the hula.launch workflow
|
|
3
|
+
# Usage: bash .github/scripts/hula-launch-run.sh <plan-path> <branch-name> [--handoff <username>]
|
|
4
|
+
#
|
|
5
|
+
# Performs: hula launch (which handles plan upload automatically)
|
|
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
|
+
PLAN_PATH=''
|
|
28
|
+
BRANCH_NAME=''
|
|
29
|
+
HANDOFF=''
|
|
30
|
+
|
|
31
|
+
while [[ $# -gt 0 ]]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--handoff)
|
|
34
|
+
HANDOFF="$2"; shift 2 ;;
|
|
35
|
+
--handoff=*)
|
|
36
|
+
HANDOFF="${1#--handoff=}"; shift ;;
|
|
37
|
+
*)
|
|
38
|
+
if [[ -z "$PLAN_PATH" ]]; then
|
|
39
|
+
PLAN_PATH="$1"
|
|
40
|
+
elif [[ -z "$BRANCH_NAME" ]]; then
|
|
41
|
+
BRANCH_NAME="$1"
|
|
42
|
+
else
|
|
43
|
+
die 1 "Unexpected argument: $1"
|
|
44
|
+
fi
|
|
45
|
+
shift ;;
|
|
46
|
+
esac
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
if [[ -z "$PLAN_PATH" || -z "$BRANCH_NAME" ]]; then
|
|
50
|
+
die 1 "Usage: bash .github/scripts/hula-launch-run.sh <plan-path> <branch-name> [--handoff <username>]"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [[ ! -f "$PLAN_PATH" ]]; then
|
|
54
|
+
die 1 "Plan file not found: ${PLAN_PATH}"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Reject paths with directory traversal sequences
|
|
58
|
+
if printf '%s' "$PLAN_PATH" | grep -q '\.\.'; then
|
|
59
|
+
die 1 "Invalid plan path (path traversal not allowed): ${PLAN_PATH}"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# ── Step 4: Run hula launch ─────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
printf '🚀 Launching issue from plan: %s\n' "$PLAN_PATH" >&2
|
|
65
|
+
|
|
66
|
+
if [[ -n "$HANDOFF" ]]; then
|
|
67
|
+
LAUNCH_OUTPUT=$(hula launch "$BRANCH_NAME" "$PLAN_PATH" --handoff "$HANDOFF" 2>&1) \
|
|
68
|
+
|| die 2 "Launch failed: $(json_escape "$LAUNCH_OUTPUT")"
|
|
69
|
+
else
|
|
70
|
+
LAUNCH_OUTPUT=$(hula launch "$BRANCH_NAME" "$PLAN_PATH" 2>&1) \
|
|
71
|
+
|| die 2 "Launch failed: $(json_escape "$LAUNCH_OUTPUT")"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Extract issue number from CLI output (e.g. "Created issue #42" or "issue #42")
|
|
75
|
+
ISSUE_NUMBER=$(printf '%s' "$LAUNCH_OUTPUT" | grep -oE '#[0-9]+' | head -1 | tr -d '#') || true
|
|
76
|
+
|
|
77
|
+
# ── Output result ─────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
SAFE_PLAN=$(json_escape "$PLAN_PATH")
|
|
80
|
+
SAFE_BRANCH=$(json_escape "$BRANCH_NAME")
|
|
81
|
+
SAFE_HANDOFF=$(json_escape "$HANDOFF")
|
|
82
|
+
SAFE_OUTPUT=$(json_escape "$LAUNCH_OUTPUT")
|
|
83
|
+
|
|
84
|
+
printf '{"status":"success","issueNumber":"%s","branchName":"%s","planPath":"%s","handoff":"%s","cliOutput":"%s"}\n' \
|
|
85
|
+
"${ISSUE_NUMBER:-}" \
|
|
86
|
+
"$SAFE_BRANCH" \
|
|
87
|
+
"$SAFE_PLAN" \
|
|
88
|
+
"$SAFE_HANDOFF" \
|
|
89
|
+
"$SAFE_OUTPUT"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-merge-local.sh — Path A of the hula.merge workflow (local session merge)
|
|
3
|
+
# Usage: bash .github/scripts/hula-merge-local.sh <issue-number> <worktree-path> <commit-message> [--skip-tests] [--dry-run]
|
|
4
|
+
#
|
|
5
|
+
# Performs: commit + push from worktree → hula merge → remove worktree → delete session file
|
|
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
|
+
WORKTREE_PATH=''
|
|
29
|
+
COMMIT_MSG=''
|
|
30
|
+
SKIP_TESTS=false
|
|
31
|
+
DRY_RUN=false
|
|
32
|
+
|
|
33
|
+
while [[ $# -gt 0 ]]; do
|
|
34
|
+
case "$1" in
|
|
35
|
+
--skip-tests) SKIP_TESTS=true; shift ;;
|
|
36
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
37
|
+
*)
|
|
38
|
+
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
39
|
+
ISSUE_NUMBER="$1"
|
|
40
|
+
elif [[ -z "$WORKTREE_PATH" ]]; then
|
|
41
|
+
WORKTREE_PATH="$1"
|
|
42
|
+
elif [[ -z "$COMMIT_MSG" ]]; then
|
|
43
|
+
COMMIT_MSG="$1"
|
|
44
|
+
else
|
|
45
|
+
die 1 "Unexpected argument: $1"
|
|
46
|
+
fi
|
|
47
|
+
shift ;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
if [[ -z "$ISSUE_NUMBER" || -z "$WORKTREE_PATH" || -z "$COMMIT_MSG" ]]; then
|
|
52
|
+
die 1 "Usage: bash .github/scripts/hula-merge-local.sh <issue-number> <worktree-path> <commit-message> [--skip-tests] [--dry-run]"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [[ ! -d "$WORKTREE_PATH" ]]; then
|
|
56
|
+
die 1 "Worktree not found at ${WORKTREE_PATH}."
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Compute absolute session file path before cd (so it resolves correctly later)
|
|
60
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || REPO_ROOT="$(pwd)"
|
|
61
|
+
SESSION_FILE="${REPO_ROOT}/.hublaunch/.fix-sessions/issue-${ISSUE_NUMBER}.json"
|
|
62
|
+
|
|
63
|
+
# ── Step A3: Commit and push from worktree ────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
cd "$WORKTREE_PATH"
|
|
66
|
+
|
|
67
|
+
PORCELAIN=$(git status --porcelain 2>/dev/null)
|
|
68
|
+
COMMITTED=false
|
|
69
|
+
FILES_CHANGED=0
|
|
70
|
+
|
|
71
|
+
if [[ -n "$PORCELAIN" ]]; then
|
|
72
|
+
FILES_CHANGED=$(printf '%s' "$PORCELAIN" | grep -c '' || true)
|
|
73
|
+
|
|
74
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
75
|
+
git add -A >&2 2>&1 \
|
|
76
|
+
|| die 2 "git add failed in worktree ${WORKTREE_PATH}"
|
|
77
|
+
|
|
78
|
+
git commit -m "$COMMIT_MSG" >&2 2>&1 \
|
|
79
|
+
|| die 2 "Failed to commit changes. Ensure git user.name and user.email are configured."
|
|
80
|
+
|
|
81
|
+
git push >&2 2>&1 \
|
|
82
|
+
|| die 2 "Push failed. Try: cd ${WORKTREE_PATH} && git push"
|
|
83
|
+
|
|
84
|
+
COMMITTED=true
|
|
85
|
+
else
|
|
86
|
+
printf '🔍 Dry run: would commit %d file(s) with message: %s\n' "$FILES_CHANGED" "$COMMIT_MSG" >&2
|
|
87
|
+
COMMITTED=false
|
|
88
|
+
fi
|
|
89
|
+
else
|
|
90
|
+
printf 'ℹ️ No changes to commit, proceeding to merge...\n' >&2
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# ── Step A4: Merge the PR ─────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
# Return to the main repo root (parent of worktree if possible)
|
|
96
|
+
cd - > /dev/null 2>&1 || true
|
|
97
|
+
|
|
98
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
99
|
+
printf '🔀 Merging PR for issue #%s...\n' "$ISSUE_NUMBER" >&2
|
|
100
|
+
hula merge "$ISSUE_NUMBER" >&2 2>&1 \
|
|
101
|
+
|| die 2 "Failed to merge PR for issue #${ISSUE_NUMBER}. Session preserved — run /hula-merge again after resolving."
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# ── Step A5: Remove fix worktree ──────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
107
|
+
printf '🧹 Removing fix worktree: %s\n' "$WORKTREE_PATH" >&2
|
|
108
|
+
git worktree remove "$WORKTREE_PATH" --force >&2 2>&1 \
|
|
109
|
+
|| printf '⚠️ Could not remove worktree at %s (may already be gone)\n' "$WORKTREE_PATH" >&2
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# ── Step A6: Clean up session file ───────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
SESSION_CLEANED=false
|
|
115
|
+
|
|
116
|
+
if [[ -f "$SESSION_FILE" ]]; then
|
|
117
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
118
|
+
rm -f "$SESSION_FILE"
|
|
119
|
+
SESSION_CLEANED=true
|
|
120
|
+
printf '🧹 Cleaned up session file\n' >&2
|
|
121
|
+
else
|
|
122
|
+
printf '🔍 Dry run: would delete session file %s\n' "$SESSION_FILE" >&2
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# ── Output result ─────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
SAFE_MSG=$(json_escape "$COMMIT_MSG")
|
|
129
|
+
SAFE_PATH=$(json_escape "$WORKTREE_PATH")
|
|
130
|
+
|
|
131
|
+
printf '{"status":"success","issueNumber":%s,"committed":%s,"filesChanged":%d,"worktreeRemoved":%s,"sessionCleaned":%s,"commitMessage":"%s","worktreePath":"%s"}\n' \
|
|
132
|
+
"$ISSUE_NUMBER" \
|
|
133
|
+
"$([ "$COMMITTED" == true ] && echo 'true' || echo 'false')" \
|
|
134
|
+
"$FILES_CHANGED" \
|
|
135
|
+
"$([ "$DRY_RUN" == false ] && echo 'true' || echo 'false')" \
|
|
136
|
+
"$([ "$SESSION_CLEANED" == true ] && echo 'true' || echo 'false')" \
|
|
137
|
+
"$SAFE_MSG" \
|
|
138
|
+
"$SAFE_PATH"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-merge-remote.sh — Path B of the hula.merge workflow (remote-only merge)
|
|
3
|
+
# Usage: bash .github/scripts/hula-merge-remote.sh <issue-number> [--session-path <path>]
|
|
4
|
+
#
|
|
5
|
+
# Merges the PR directly on GitHub, then optionally cleans up a stale session file.
|
|
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
|
+
SESSION_PATH=''
|
|
29
|
+
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--session-path)
|
|
33
|
+
SESSION_PATH="$2"; shift 2 ;;
|
|
34
|
+
--session-path=*)
|
|
35
|
+
SESSION_PATH="${1#--session-path=}"; shift ;;
|
|
36
|
+
[0-9]*)
|
|
37
|
+
ISSUE_NUMBER="$1"; shift ;;
|
|
38
|
+
*)
|
|
39
|
+
die 1 "Unknown argument: $1" ;;
|
|
40
|
+
esac
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
if [[ -z "$ISSUE_NUMBER" ]]; then
|
|
44
|
+
die 1 "Usage: bash .github/scripts/hula-merge-remote.sh <issue-number> [--session-path <path>]"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── Step B1: Merge the PR via hula CLI ───────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
printf '🔀 Merging PR for issue #%s (remote-only)...\n' "$ISSUE_NUMBER" >&2
|
|
50
|
+
|
|
51
|
+
hula merge "$ISSUE_NUMBER" 2>&1 >&2 \
|
|
52
|
+
|| die 2 "Failed to merge PR for issue #${ISSUE_NUMBER}. Check the PR on GitHub."
|
|
53
|
+
|
|
54
|
+
# ── Step B2: Clean up stale session file (if applicable) ─────────────────────
|
|
55
|
+
|
|
56
|
+
SESSION_CLEANED=false
|
|
57
|
+
|
|
58
|
+
# Default session file path if not provided (use absolute path so it works from any CWD)
|
|
59
|
+
if [[ -z "$SESSION_PATH" ]]; then
|
|
60
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || REPO_ROOT="$(pwd)"
|
|
61
|
+
SESSION_PATH="${REPO_ROOT}/.hublaunch/.fix-sessions/issue-${ISSUE_NUMBER}.json"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [[ -f "$SESSION_PATH" ]]; then
|
|
65
|
+
rm -f "$SESSION_PATH"
|
|
66
|
+
SESSION_CLEANED=true
|
|
67
|
+
printf '🧹 Cleaned up stale session file\n' >&2
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ── Output result ─────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
printf '{"status":"success","issueNumber":%s,"sessionCleaned":%s}\n' \
|
|
73
|
+
"$ISSUE_NUMBER" \
|
|
74
|
+
"$([ "$SESSION_CLEANED" == true ] && echo 'true' || echo 'false')"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hula-read-config.sh — Shared config reader for hula shell scripts
|
|
3
|
+
# Source this file; do NOT execute directly.
|
|
4
|
+
#
|
|
5
|
+
# After sourcing, the following variables are set:
|
|
6
|
+
# HULA_WORKTREE_BASE_PATH — absolute resolved worktreeBasePath
|
|
7
|
+
#
|
|
8
|
+
# Reads worktreeBasePath from .hublaunch/hublaunch.config.js.
|
|
9
|
+
# Falls back to ".hula-worktrees" (resolved relative to repo root).
|
|
10
|
+
|
|
11
|
+
_hula_read_worktree_base_path() {
|
|
12
|
+
local repo_root
|
|
13
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
|
|
14
|
+
printf 'Warning: could not determine repo root\n' >&2
|
|
15
|
+
repo_root="$(pwd)"
|
|
16
|
+
}
|
|
17
|
+
local config_file="${repo_root}/.hublaunch/hublaunch.config.js"
|
|
18
|
+
local base_path=""
|
|
19
|
+
|
|
20
|
+
if [[ -f "$config_file" ]]; then
|
|
21
|
+
base_path=$(grep 'worktreeBasePath' "$config_file" \
|
|
22
|
+
| grep -v '^\s*//' \
|
|
23
|
+
| sed "s/.*worktreeBasePath[^'\"]*['\"]\\([^'\"]*\\)['\"].*/\\1/" \
|
|
24
|
+
| head -1) || true
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Default to .hula-worktrees if not found or empty
|
|
28
|
+
base_path="${base_path:-.hula-worktrees}"
|
|
29
|
+
|
|
30
|
+
# Resolve relative paths against repo root
|
|
31
|
+
if [[ "$base_path" != /* ]]; then
|
|
32
|
+
base_path="${repo_root}/${base_path}"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
printf '%s' "$base_path"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
HULA_WORKTREE_BASE_PATH=$(_hula_read_worktree_base_path)
|
|
@@ -0,0 +1,135 @@
|
|
|
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}"
|