hub-launch 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +102 -2
- package/dist/commands/init.d.ts +61 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +125 -114
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/instructions.d.ts +17 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +51 -0
- package/dist/commands/instructions.js.map +1 -0
- package/dist/commands/launch.d.ts +21 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +61 -0
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/script.d.ts +28 -0
- package/dist/commands/script.d.ts.map +1 -0
- package/dist/commands/script.js +69 -0
- package/dist/commands/script.js.map +1 -0
- package/dist/commands/session-hook.d.ts +26 -0
- package/dist/commands/session-hook.d.ts.map +1 -0
- package/dist/commands/session-hook.js +146 -0
- package/dist/commands/session-hook.js.map +1 -0
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/scripts/fix-commit.d.ts +2 -0
- package/dist/scripts/fix-commit.d.ts.map +1 -0
- package/dist/scripts/fix-commit.js +125 -0
- package/dist/scripts/fix-commit.js.map +1 -0
- package/dist/scripts/fix-setup.d.ts +2 -0
- package/dist/scripts/fix-setup.d.ts.map +1 -0
- package/dist/scripts/fix-setup.js +161 -0
- package/dist/scripts/fix-setup.js.map +1 -0
- package/dist/scripts/launch-run.d.ts +14 -0
- package/dist/scripts/launch-run.d.ts.map +1 -0
- package/dist/scripts/launch-run.js +117 -0
- package/dist/scripts/launch-run.js.map +1 -0
- package/dist/scripts/lib/exec.d.ts +48 -0
- package/dist/scripts/lib/exec.d.ts.map +1 -0
- package/dist/scripts/lib/exec.js +61 -0
- package/dist/scripts/lib/exec.js.map +1 -0
- package/dist/scripts/lib/io.d.ts +48 -0
- package/dist/scripts/lib/io.d.ts.map +1 -0
- package/dist/scripts/lib/io.js +72 -0
- package/dist/scripts/lib/io.js.map +1 -0
- package/dist/scripts/lib/read-config.d.ts +26 -0
- package/dist/scripts/lib/read-config.d.ts.map +1 -0
- package/dist/scripts/lib/read-config.js +65 -0
- package/dist/scripts/lib/read-config.js.map +1 -0
- package/dist/scripts/merge-local.d.ts +2 -0
- package/dist/scripts/merge-local.d.ts.map +1 -0
- package/dist/scripts/merge-local.js +146 -0
- package/dist/scripts/merge-local.js.map +1 -0
- package/dist/scripts/merge-remote.d.ts +12 -0
- package/dist/scripts/merge-remote.d.ts.map +1 -0
- package/dist/scripts/merge-remote.js +71 -0
- package/dist/scripts/merge-remote.js.map +1 -0
- package/dist/scripts/schedule-manage.d.ts +2 -0
- package/dist/scripts/schedule-manage.d.ts.map +1 -0
- package/dist/scripts/schedule-manage.js +151 -0
- package/dist/scripts/schedule-manage.js.map +1 -0
- package/dist/scripts/schedule-run.d.ts +2 -0
- package/dist/scripts/schedule-run.d.ts.map +1 -0
- package/dist/scripts/schedule-run.js +146 -0
- package/dist/scripts/schedule-run.js.map +1 -0
- package/dist/scripts/verify-gather.d.ts +2 -0
- package/dist/scripts/verify-gather.d.ts.map +1 -0
- package/dist/scripts/verify-gather.js +206 -0
- package/dist/scripts/verify-gather.js.map +1 -0
- package/dist/scripts/verify-post.d.ts +12 -0
- package/dist/scripts/verify-post.d.ts.map +1 -0
- package/dist/scripts/verify-post.js +91 -0
- package/dist/scripts/verify-post.js.map +1 -0
- package/dist/templates/planning-instructions.md +7 -7
- package/dist/templates/skill-creation-instructions.md +2 -2
- package/dist/templates/skills/hula-confirm/SKILL.md +3 -3
- package/dist/templates/skills/hula-fix/SKILL.md +3 -3
- package/dist/templates/skills/hula-help/SKILL.md +117 -0
- package/dist/templates/skills/hula-launch/SKILL.md +14 -8
- package/dist/templates/skills/hula-merge/SKILL.md +2 -2
- package/dist/templates/skills/hula-plan/SKILL.md +5 -5
- package/dist/templates/skills/hula-schedule/SKILL.md +16 -16
- package/dist/templates/skills/hula-verify/SKILL.md +32 -33
- package/dist/types/config.schema.d.ts +235 -0
- package/dist/types/config.schema.d.ts.map +1 -1
- package/dist/types/config.schema.js +66 -0
- package/dist/types/config.schema.js.map +1 -1
- package/dist/utils/client-session.d.ts +2 -2
- package/dist/utils/client-session.js +2 -2
- package/package.json +6 -5
- package/scripts/postinstall.mjs +102 -0
- package/dist/templates/scripts/hula-fix-commit.sh +0 -114
- package/dist/templates/scripts/hula-fix-setup.sh +0 -152
- package/dist/templates/scripts/hula-launch-run.sh +0 -140
- package/dist/templates/scripts/hula-merge-local.sh +0 -138
- package/dist/templates/scripts/hula-merge-remote.sh +0 -94
- package/dist/templates/scripts/hula-read-config.sh +0 -38
- package/dist/templates/scripts/hula-schedule-manage.sh +0 -159
- package/dist/templates/scripts/hula-schedule-run.sh +0 -148
- package/dist/templates/scripts/hula-session-hook.sh +0 -68
- package/dist/templates/scripts/hula-verify-gather.sh +0 -135
- package/dist/templates/scripts/hula-verify-post.sh +0 -84
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { runHula } from './lib/exec.js';
|
|
2
|
+
import { emitJson, fail, logStderr, runScript } from './lib/io.js';
|
|
3
|
+
/**
|
|
4
|
+
* launch-run — Step 4 of the hula.launch workflow.
|
|
5
|
+
* Invoked as:
|
|
6
|
+
* `hula script launch-run -- <plan-path> <branch-name> [--handoff <username>] [--test] [--skip-regression] [--kill] [--kill-and-relaunch]`
|
|
7
|
+
* `hula script launch-run -- <branch-name> --kill` (cancel only; no plan path)
|
|
8
|
+
*
|
|
9
|
+
* Performs: hula launch (which handles plan upload automatically).
|
|
10
|
+
* Emits structured JSON to stdout; all other output goes to stderr.
|
|
11
|
+
* Exit codes: 0=success, 1=user error, 2=tool error.
|
|
12
|
+
*
|
|
13
|
+
* Node port of `hula-launch-run.sh` (cross-platform: no bash, no jq).
|
|
14
|
+
*/
|
|
15
|
+
export function main(args) {
|
|
16
|
+
return runScript(async () => {
|
|
17
|
+
// ── argument parsing ──────────────────────────────────────────────────
|
|
18
|
+
let planPath = '';
|
|
19
|
+
let branchName = '';
|
|
20
|
+
let handoff = '';
|
|
21
|
+
let test = false;
|
|
22
|
+
let skipRegression = false;
|
|
23
|
+
let kill = false;
|
|
24
|
+
let killAndRelaunch = false;
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const a = args[i];
|
|
27
|
+
if (a === '--handoff') {
|
|
28
|
+
handoff = args[++i] ?? '';
|
|
29
|
+
}
|
|
30
|
+
else if (a.startsWith('--handoff=')) {
|
|
31
|
+
handoff = a.slice('--handoff='.length);
|
|
32
|
+
}
|
|
33
|
+
else if (a === '--test') {
|
|
34
|
+
test = true;
|
|
35
|
+
}
|
|
36
|
+
else if (a === '--skip-regression') {
|
|
37
|
+
skipRegression = true;
|
|
38
|
+
}
|
|
39
|
+
else if (a === '--kill') {
|
|
40
|
+
kill = true;
|
|
41
|
+
}
|
|
42
|
+
else if (a === '--kill-and-relaunch') {
|
|
43
|
+
killAndRelaunch = true;
|
|
44
|
+
}
|
|
45
|
+
else if (!planPath) {
|
|
46
|
+
planPath = a;
|
|
47
|
+
}
|
|
48
|
+
else if (!branchName) {
|
|
49
|
+
branchName = a;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
fail(1, `Unexpected argument: ${a}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (kill && killAndRelaunch) {
|
|
56
|
+
fail(1, '--kill and --kill-and-relaunch cannot be used together');
|
|
57
|
+
}
|
|
58
|
+
if (kill) {
|
|
59
|
+
// kill needs ONLY the branch name; if a single positional was given it
|
|
60
|
+
// landed in planPath, so re-home it as the branch name.
|
|
61
|
+
if (!branchName) {
|
|
62
|
+
branchName = planPath;
|
|
63
|
+
planPath = '';
|
|
64
|
+
}
|
|
65
|
+
if (!branchName) {
|
|
66
|
+
fail(1, 'Usage: hula script launch-run -- <branch-name> --kill');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (!planPath || !branchName) {
|
|
70
|
+
fail(1, 'Usage: hula script launch-run -- <plan-path> <branch-name> [--handoff <username>] [--test] [--kill-and-relaunch]');
|
|
71
|
+
}
|
|
72
|
+
// Reject paths with directory-traversal sequences (only when a plan is in play).
|
|
73
|
+
if (planPath && planPath.includes('..')) {
|
|
74
|
+
fail(1, `Invalid plan path (path traversal not allowed): ${planPath}`);
|
|
75
|
+
}
|
|
76
|
+
// ── Step 4: Run hula launch ───────────────────────────────────────────
|
|
77
|
+
if (kill) {
|
|
78
|
+
logStderr(`🛑 Stopping in-flight task for branch: ${branchName}`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
logStderr(`🚀 Launching issue from plan: ${planPath}`);
|
|
82
|
+
}
|
|
83
|
+
// Branch name is always first; the plan path is a positional appended only
|
|
84
|
+
// when present (a bare --kill sends no plan path).
|
|
85
|
+
const launchArgs = ['launch', branchName];
|
|
86
|
+
if (planPath && !kill)
|
|
87
|
+
launchArgs.push(planPath);
|
|
88
|
+
if (handoff)
|
|
89
|
+
launchArgs.push('--handoff', handoff);
|
|
90
|
+
if (test)
|
|
91
|
+
launchArgs.push('--test');
|
|
92
|
+
if (skipRegression)
|
|
93
|
+
launchArgs.push('--skip-regression');
|
|
94
|
+
if (kill)
|
|
95
|
+
launchArgs.push('--kill');
|
|
96
|
+
if (killAndRelaunch)
|
|
97
|
+
launchArgs.push('--kill-and-relaunch');
|
|
98
|
+
const launch = await runHula(launchArgs);
|
|
99
|
+
const launchOutput = `${launch.stdout}${launch.stderr}`;
|
|
100
|
+
if (launch.code !== 0) {
|
|
101
|
+
fail(2, `Launch failed: ${launchOutput}`);
|
|
102
|
+
}
|
|
103
|
+
// Extract issue number from CLI output (e.g. "Created issue #42").
|
|
104
|
+
const match = launchOutput.match(/#[0-9]+/);
|
|
105
|
+
const issueNumber = match ? match[0].replace('#', '') : '';
|
|
106
|
+
// ── Output result ─────────────────────────────────────────────────────
|
|
107
|
+
emitJson({
|
|
108
|
+
status: 'success',
|
|
109
|
+
issueNumber,
|
|
110
|
+
branchName,
|
|
111
|
+
planPath,
|
|
112
|
+
handoff,
|
|
113
|
+
cliOutput: launchOutput,
|
|
114
|
+
});
|
|
115
|
+
}, args);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=launch-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-run.js","sourceRoot":"","sources":["../../src/scripts/launch-run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform subprocess helpers for the ported hula workflow scripts.
|
|
3
|
+
*
|
|
4
|
+
* The original `.sh` scripts shell out to `git`, `gh` and the `hula` CLI. These
|
|
5
|
+
* helpers reproduce that with Node's `child_process.spawn` and **no shell**, so
|
|
6
|
+
* argument quoting is not a cross-platform hazard (no cmd/PowerShell/bash
|
|
7
|
+
* escaping differences) — a core goal of the Windows-support port.
|
|
8
|
+
*
|
|
9
|
+
* `git` and `gh` are native executables found on PATH on every platform.
|
|
10
|
+
* `hula`, however, is an npm bin that resolves to `hula.cmd` on Windows, which
|
|
11
|
+
* `spawn` cannot launch without a shell. We sidestep that entirely by invoking
|
|
12
|
+
* the CLI through `process.execPath` (the current Node binary) + the CLI entry
|
|
13
|
+
* file ({@link cliEntry}), which is exactly the module already running.
|
|
14
|
+
*/
|
|
15
|
+
export interface ExecResult {
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
/** Process exit code (0 on success). 127 when the binary could not spawn. */
|
|
19
|
+
code: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ExecOptions {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
/** Optional string written to the child's stdin, then closed. */
|
|
24
|
+
input?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Spawn `file` with `args` (no shell) and capture stdout/stderr/exit code.
|
|
28
|
+
* Never rejects: a spawn error resolves to `code: 127` so callers branch on the
|
|
29
|
+
* exit code exactly as the bash scripts did with `|| die`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function execCapture(file: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
32
|
+
/** Run `git` with the given args. */
|
|
33
|
+
export declare function runGit(args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
34
|
+
/** Run the GitHub CLI (`gh`) with the given args. */
|
|
35
|
+
export declare function runGh(args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the path to the running hula CLI entry (`dist/index.js`). This is the
|
|
38
|
+
* second element of `process.argv` when invoked via the `hula` bin. Falls back
|
|
39
|
+
* to an empty string only in the pathological case where argv[1] is missing.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cliEntry(): string;
|
|
42
|
+
/**
|
|
43
|
+
* Invoke a hula CLI subcommand (`hula <args>`) cross-platform by running the
|
|
44
|
+
* current Node binary against the CLI entry file, avoiding the Windows
|
|
45
|
+
* `hula.cmd` spawn problem. Captures stdout/stderr/exit code.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runHula(args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
48
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/scripts/lib/exec.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,qCAAqC;AACrC,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,qDAAqD;AACrD,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAErB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAIrB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Spawn `file` with `args` (no shell) and capture stdout/stderr/exit code.
|
|
4
|
+
* Never rejects: a spawn error resolves to `code: 127` so callers branch on the
|
|
5
|
+
* exit code exactly as the bash scripts did with `|| die`.
|
|
6
|
+
*/
|
|
7
|
+
export function execCapture(file, args, options = {}) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const child = spawn(file, args, {
|
|
10
|
+
cwd: options.cwd,
|
|
11
|
+
shell: false,
|
|
12
|
+
});
|
|
13
|
+
let stdout = '';
|
|
14
|
+
let stderr = '';
|
|
15
|
+
child.stdout.on('data', (d) => {
|
|
16
|
+
stdout += d.toString();
|
|
17
|
+
});
|
|
18
|
+
child.stderr.on('data', (d) => {
|
|
19
|
+
stderr += d.toString();
|
|
20
|
+
});
|
|
21
|
+
child.on('error', (err) => {
|
|
22
|
+
resolve({ stdout, stderr: stderr + String(err), code: 127 });
|
|
23
|
+
});
|
|
24
|
+
child.on('close', (code) => {
|
|
25
|
+
resolve({ stdout, stderr, code: code ?? 0 });
|
|
26
|
+
});
|
|
27
|
+
if (options.input !== undefined) {
|
|
28
|
+
child.stdin.end(options.input);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
child.stdin.end();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Run `git` with the given args. */
|
|
36
|
+
export function runGit(args, options = {}) {
|
|
37
|
+
return execCapture('git', args, options);
|
|
38
|
+
}
|
|
39
|
+
/** Run the GitHub CLI (`gh`) with the given args. */
|
|
40
|
+
export function runGh(args, options = {}) {
|
|
41
|
+
return execCapture('gh', args, options);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the path to the running hula CLI entry (`dist/index.js`). This is the
|
|
45
|
+
* second element of `process.argv` when invoked via the `hula` bin. Falls back
|
|
46
|
+
* to an empty string only in the pathological case where argv[1] is missing.
|
|
47
|
+
*/
|
|
48
|
+
export function cliEntry() {
|
|
49
|
+
return process.argv[1] ?? '';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Invoke a hula CLI subcommand (`hula <args>`) cross-platform by running the
|
|
53
|
+
* current Node binary against the CLI entry file, avoiding the Windows
|
|
54
|
+
* `hula.cmd` spawn problem. Captures stdout/stderr/exit code.
|
|
55
|
+
*/
|
|
56
|
+
export function runHula(args, options = {}) {
|
|
57
|
+
const entry = cliEntry();
|
|
58
|
+
const spawnArgs = entry ? [entry, ...args] : args;
|
|
59
|
+
return execCapture(process.execPath, spawnArgs, options);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/scripts/lib/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA8B3C;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,IAAc,EACd,UAAuB,EAAE;IAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE;YAC9B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YACpC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YACpC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACxC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,MAAM,CACpB,IAAc,EACd,UAAuB,EAAE;IAEzB,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,KAAK,CACnB,IAAc,EACd,UAAuB,EAAE;IAEzB,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CACrB,IAAc,EACd,UAAuB,EAAE;IAEzB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClD,OAAO,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared I/O helpers for the ported hula workflow scripts.
|
|
3
|
+
*
|
|
4
|
+
* Every ported script preserves the same output contract as its original
|
|
5
|
+
* `.sh` predecessor: a single structured JSON object on **stdout**, and every
|
|
6
|
+
* human-readable log / progress line on **stderr**. These helpers make that
|
|
7
|
+
* split explicit so no script accidentally writes a log line to stdout (which
|
|
8
|
+
* would corrupt the JSON the calling skill parses).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Write a single compact JSON object to stdout, terminated by a newline.
|
|
12
|
+
* Mirrors the `printf '{...}\n'` contract of the original bash scripts.
|
|
13
|
+
* `JSON.stringify` handles all escaping (quotes, backslashes, control chars),
|
|
14
|
+
* replacing the hand-rolled `json_escape()` helper.
|
|
15
|
+
*/
|
|
16
|
+
export declare function emitJson(obj: Record<string, unknown>): void;
|
|
17
|
+
/**
|
|
18
|
+
* Write a human-readable log/progress line to stderr (the `>&2` of the bash
|
|
19
|
+
* scripts). Never use `console.log`/`logger.info` inside a ported script — they
|
|
20
|
+
* write to stdout and would break the JSON contract.
|
|
21
|
+
*/
|
|
22
|
+
export declare function logStderr(msg: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* A user- or tool-level error that maps to the JSON error contract
|
|
25
|
+
* `{"status":"error","code":<n>,"message":"..."}` and a matching process exit
|
|
26
|
+
* code. Ported from each script's `die()` helper.
|
|
27
|
+
* - code 1 = user error (bad args, missing resource)
|
|
28
|
+
* - code 2 = tool error (git/gh/hula subprocess failure)
|
|
29
|
+
*/
|
|
30
|
+
export declare class ScriptError extends Error {
|
|
31
|
+
readonly code: number;
|
|
32
|
+
constructor(code: number, message: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Throw a {@link ScriptError}. Convenience wrapper matching the bash `die`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function fail(code: number, message: string): never;
|
|
38
|
+
/**
|
|
39
|
+
* Run a ported script body and translate its outcome into the shared contract.
|
|
40
|
+
*
|
|
41
|
+
* On success the body is responsible for emitting its own success JSON (so it
|
|
42
|
+
* controls the exact shape) and `runScript` returns 0. On a thrown
|
|
43
|
+
* {@link ScriptError} (or any other error) it emits the standard error JSON to
|
|
44
|
+
* stdout and returns the associated exit code — exactly as the bash `die()`
|
|
45
|
+
* did. This is the single place the error contract lives.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runScript(body: (args: string[]) => Promise<void>, args: string[]): Promise<number>;
|
|
48
|
+
//# sourceMappingURL=io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../../src/scripts/lib/io.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED;;;;;;GAMG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACvC,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared I/O helpers for the ported hula workflow scripts.
|
|
3
|
+
*
|
|
4
|
+
* Every ported script preserves the same output contract as its original
|
|
5
|
+
* `.sh` predecessor: a single structured JSON object on **stdout**, and every
|
|
6
|
+
* human-readable log / progress line on **stderr**. These helpers make that
|
|
7
|
+
* split explicit so no script accidentally writes a log line to stdout (which
|
|
8
|
+
* would corrupt the JSON the calling skill parses).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Write a single compact JSON object to stdout, terminated by a newline.
|
|
12
|
+
* Mirrors the `printf '{...}\n'` contract of the original bash scripts.
|
|
13
|
+
* `JSON.stringify` handles all escaping (quotes, backslashes, control chars),
|
|
14
|
+
* replacing the hand-rolled `json_escape()` helper.
|
|
15
|
+
*/
|
|
16
|
+
export function emitJson(obj) {
|
|
17
|
+
process.stdout.write(JSON.stringify(obj) + '\n');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Write a human-readable log/progress line to stderr (the `>&2` of the bash
|
|
21
|
+
* scripts). Never use `console.log`/`logger.info` inside a ported script — they
|
|
22
|
+
* write to stdout and would break the JSON contract.
|
|
23
|
+
*/
|
|
24
|
+
export function logStderr(msg) {
|
|
25
|
+
process.stderr.write(msg + '\n');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A user- or tool-level error that maps to the JSON error contract
|
|
29
|
+
* `{"status":"error","code":<n>,"message":"..."}` and a matching process exit
|
|
30
|
+
* code. Ported from each script's `die()` helper.
|
|
31
|
+
* - code 1 = user error (bad args, missing resource)
|
|
32
|
+
* - code 2 = tool error (git/gh/hula subprocess failure)
|
|
33
|
+
*/
|
|
34
|
+
export class ScriptError extends Error {
|
|
35
|
+
code;
|
|
36
|
+
constructor(code, message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'ScriptError';
|
|
39
|
+
this.code = code;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Throw a {@link ScriptError}. Convenience wrapper matching the bash `die`.
|
|
44
|
+
*/
|
|
45
|
+
export function fail(code, message) {
|
|
46
|
+
throw new ScriptError(code, message);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run a ported script body and translate its outcome into the shared contract.
|
|
50
|
+
*
|
|
51
|
+
* On success the body is responsible for emitting its own success JSON (so it
|
|
52
|
+
* controls the exact shape) and `runScript` returns 0. On a thrown
|
|
53
|
+
* {@link ScriptError} (or any other error) it emits the standard error JSON to
|
|
54
|
+
* stdout and returns the associated exit code — exactly as the bash `die()`
|
|
55
|
+
* did. This is the single place the error contract lives.
|
|
56
|
+
*/
|
|
57
|
+
export async function runScript(body, args) {
|
|
58
|
+
try {
|
|
59
|
+
await body(args);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (err instanceof ScriptError) {
|
|
64
|
+
emitJson({ status: 'error', code: err.code, message: err.message });
|
|
65
|
+
return err.code;
|
|
66
|
+
}
|
|
67
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
+
emitJson({ status: 'error', code: 2, message });
|
|
69
|
+
return 2;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../../src/scripts/lib/io.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAA4B;IACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAS;IACtB,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,OAAe;IAChD,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAuC,EACvC,IAAc;IAEd,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,OAAO,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node port of `hula-read-config.sh` (a sourced bash helper). Resolves the
|
|
3
|
+
* git repo root and the effective `worktreeBasePath` for the other ported
|
|
4
|
+
* scripts, replacing the `grep`/`sed` config parsing and the
|
|
5
|
+
* `git rev-parse --show-toplevel` shell-out with cross-platform Node.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the git repository root. Mirrors the shell-out
|
|
9
|
+
* `git rev-parse --show-toplevel` with the same non-fatal fallback the bash
|
|
10
|
+
* scripts used (`|| REPO_ROOT="$(pwd)"`). On Windows + Git Bash, git may return
|
|
11
|
+
* a Unix-style path (`/d/foo`); normalize it to a native path.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveRepoRoot(cwd?: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Read `worktreeBasePath` from `.hublaunch/hublaunch.config.js` and resolve it
|
|
16
|
+
* to an absolute path, exactly as `hula-read-config.sh` did:
|
|
17
|
+
* - falls back to `.hula-worktrees` when the key is absent/empty or the config
|
|
18
|
+
* file does not exist / cannot be loaded;
|
|
19
|
+
* - resolves relative values against the repo root.
|
|
20
|
+
*
|
|
21
|
+
* The value is read by loading the config module (the config is generated by
|
|
22
|
+
* `hula init` as valid ESM) instead of regex-scraping the file. Loading is done
|
|
23
|
+
* silently — no logger/spinner — to preserve the "JSON only on stdout" contract.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getWorktreeBasePath(repoRoot: string): Promise<string>;
|
|
26
|
+
//# sourceMappingURL=read-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-config.d.ts","sourceRoot":"","sources":["../../../src/scripts/lib/read-config.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AAEH;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWnE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B3E"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { runGit } from './exec.js';
|
|
6
|
+
import { logStderr } from './io.js';
|
|
7
|
+
/**
|
|
8
|
+
* Node port of `hula-read-config.sh` (a sourced bash helper). Resolves the
|
|
9
|
+
* git repo root and the effective `worktreeBasePath` for the other ported
|
|
10
|
+
* scripts, replacing the `grep`/`sed` config parsing and the
|
|
11
|
+
* `git rev-parse --show-toplevel` shell-out with cross-platform Node.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the git repository root. Mirrors the shell-out
|
|
15
|
+
* `git rev-parse --show-toplevel` with the same non-fatal fallback the bash
|
|
16
|
+
* scripts used (`|| REPO_ROOT="$(pwd)"`). On Windows + Git Bash, git may return
|
|
17
|
+
* a Unix-style path (`/d/foo`); normalize it to a native path.
|
|
18
|
+
*/
|
|
19
|
+
export async function resolveRepoRoot(cwd) {
|
|
20
|
+
const res = await runGit(['rev-parse', '--show-toplevel'], { cwd });
|
|
21
|
+
if (res.code !== 0 || !res.stdout.trim()) {
|
|
22
|
+
logStderr('Warning: could not determine repo root');
|
|
23
|
+
return process.cwd();
|
|
24
|
+
}
|
|
25
|
+
const gitPath = res.stdout.trim();
|
|
26
|
+
if (process.platform === 'win32' && gitPath.startsWith('/')) {
|
|
27
|
+
return resolve(gitPath.replace(/^\/([a-zA-Z])\//, '$1:/'));
|
|
28
|
+
}
|
|
29
|
+
return gitPath;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read `worktreeBasePath` from `.hublaunch/hublaunch.config.js` and resolve it
|
|
33
|
+
* to an absolute path, exactly as `hula-read-config.sh` did:
|
|
34
|
+
* - falls back to `.hula-worktrees` when the key is absent/empty or the config
|
|
35
|
+
* file does not exist / cannot be loaded;
|
|
36
|
+
* - resolves relative values against the repo root.
|
|
37
|
+
*
|
|
38
|
+
* The value is read by loading the config module (the config is generated by
|
|
39
|
+
* `hula init` as valid ESM) instead of regex-scraping the file. Loading is done
|
|
40
|
+
* silently — no logger/spinner — to preserve the "JSON only on stdout" contract.
|
|
41
|
+
*/
|
|
42
|
+
export async function getWorktreeBasePath(repoRoot) {
|
|
43
|
+
const configFile = join(repoRoot, '.hublaunch', 'hublaunch.config.js');
|
|
44
|
+
let basePath = '';
|
|
45
|
+
if (existsSync(configFile)) {
|
|
46
|
+
try {
|
|
47
|
+
// Cache-bust so a re-run in the same process picks up edits.
|
|
48
|
+
const url = `${pathToFileURL(configFile).href}?t=${Date.now()}`;
|
|
49
|
+
const mod = (await import(url));
|
|
50
|
+
const cfg = (mod.default ?? mod.config ?? mod);
|
|
51
|
+
if (typeof cfg?.worktreeBasePath === 'string') {
|
|
52
|
+
basePath = cfg.worktreeBasePath;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Unparsable/broken config — fall through to the default, matching the
|
|
57
|
+
// bash reader which never hard-failed on a bad config.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!basePath) {
|
|
61
|
+
basePath = '.hula-worktrees';
|
|
62
|
+
}
|
|
63
|
+
return isAbsolute(basePath) ? basePath : join(repoRoot, basePath);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=read-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-config.js","sourceRoot":"","sources":["../../../src/scripts/lib/read-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC;;;;;GAKG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAY;IAChD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACpE,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACzC,SAAS,CAAC,wCAAwC,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,qBAAqB,CAAC,CAAC;IACvE,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,6DAA6D;YAC7D,MAAM,GAAG,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChE,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAI7B,CAAC;YACF,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAE5C,CAAC;YACF,IAAI,OAAO,GAAG,EAAE,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBAC9C,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC;YAClC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,uDAAuD;QACzD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,iBAAiB,CAAC;IAC/B,CAAC;IAED,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-local.d.ts","sourceRoot":"","sources":["../../src/scripts/merge-local.ts"],"names":[],"mappings":"AAwBA,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4IpD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { rm } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { runGit, runHula } from './lib/exec.js';
|
|
5
|
+
import { emitJson, fail, logStderr, runScript } from './lib/io.js';
|
|
6
|
+
import { resolveRepoRoot } from './lib/read-config.js';
|
|
7
|
+
/**
|
|
8
|
+
* merge-local — Path A of the hula.merge workflow (local session merge).
|
|
9
|
+
* Invoked as:
|
|
10
|
+
* `hula script merge-local -- <issue-number> <worktree-path> <commit-message> [--skip-tests] [--dry-run]`
|
|
11
|
+
*
|
|
12
|
+
* Performs: commit + push from worktree → hula merge → remove worktree →
|
|
13
|
+
* delete session file. Emits structured JSON to stdout; other output to stderr.
|
|
14
|
+
* Exit codes: 0=success, 1=user error, 2=tool error.
|
|
15
|
+
*
|
|
16
|
+
* Node port of `hula-merge-local.sh` (cross-platform: no bash, no jq).
|
|
17
|
+
*/
|
|
18
|
+
function countChanged(porcelain) {
|
|
19
|
+
const trimmed = porcelain.replace(/\n+$/, '');
|
|
20
|
+
if (trimmed === '')
|
|
21
|
+
return 0;
|
|
22
|
+
return trimmed.split('\n').filter((l) => l.length > 0).length;
|
|
23
|
+
}
|
|
24
|
+
export function main(args) {
|
|
25
|
+
return runScript(async () => {
|
|
26
|
+
// ── argument parsing ──────────────────────────────────────────────────
|
|
27
|
+
let issueNumber = '';
|
|
28
|
+
let worktreePath = '';
|
|
29
|
+
let commitMsg = '';
|
|
30
|
+
let skipTests = false; // parsed for compatibility; unused (parity with bash)
|
|
31
|
+
let dryRun = false;
|
|
32
|
+
for (const a of args) {
|
|
33
|
+
if (a === '--skip-tests') {
|
|
34
|
+
skipTests = true;
|
|
35
|
+
}
|
|
36
|
+
else if (a === '--dry-run') {
|
|
37
|
+
dryRun = true;
|
|
38
|
+
}
|
|
39
|
+
else if (!issueNumber) {
|
|
40
|
+
issueNumber = a;
|
|
41
|
+
}
|
|
42
|
+
else if (!worktreePath) {
|
|
43
|
+
worktreePath = a;
|
|
44
|
+
}
|
|
45
|
+
else if (!commitMsg) {
|
|
46
|
+
commitMsg = a;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
fail(1, `Unexpected argument: ${a}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
void skipTests;
|
|
53
|
+
if (!issueNumber || !worktreePath || !commitMsg) {
|
|
54
|
+
fail(1, 'Usage: hula script merge-local -- <issue-number> <worktree-path> <commit-message> [--skip-tests] [--dry-run]');
|
|
55
|
+
}
|
|
56
|
+
if (!existsSync(worktreePath)) {
|
|
57
|
+
fail(1, `Worktree not found at ${worktreePath}.`);
|
|
58
|
+
}
|
|
59
|
+
// Compute absolute session file path before any commits.
|
|
60
|
+
const repoRoot = await resolveRepoRoot();
|
|
61
|
+
const sessionFile = join(repoRoot, '.hublaunch', '.fix-sessions', `issue-${issueNumber}.json`);
|
|
62
|
+
// ── Step A3: Commit and push from worktree ────────────────────────────
|
|
63
|
+
const statusRes = await runGit(['status', '--porcelain'], {
|
|
64
|
+
cwd: worktreePath,
|
|
65
|
+
});
|
|
66
|
+
const porcelain = statusRes.stdout;
|
|
67
|
+
let committed = false;
|
|
68
|
+
let filesChanged = 0;
|
|
69
|
+
if (porcelain.trim() !== '') {
|
|
70
|
+
filesChanged = countChanged(porcelain);
|
|
71
|
+
if (!dryRun) {
|
|
72
|
+
const add = await runGit(['add', '-A'], { cwd: worktreePath });
|
|
73
|
+
if (add.code !== 0) {
|
|
74
|
+
logStderr(add.stderr);
|
|
75
|
+
fail(2, `git add failed in worktree ${worktreePath}`);
|
|
76
|
+
}
|
|
77
|
+
const commit = await runGit(['commit', '-m', commitMsg], {
|
|
78
|
+
cwd: worktreePath,
|
|
79
|
+
});
|
|
80
|
+
if (commit.code !== 0) {
|
|
81
|
+
logStderr(commit.stderr);
|
|
82
|
+
fail(2, 'Failed to commit changes. Ensure git user.name and user.email are configured.');
|
|
83
|
+
}
|
|
84
|
+
const push = await runGit(['push'], { cwd: worktreePath });
|
|
85
|
+
if (push.code !== 0) {
|
|
86
|
+
logStderr(push.stderr);
|
|
87
|
+
fail(2, `Push failed. Try: cd ${worktreePath} && git push`);
|
|
88
|
+
}
|
|
89
|
+
committed = true;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
logStderr(`🔍 Dry run: would commit ${filesChanged} file(s) with message: ${commitMsg}`);
|
|
93
|
+
committed = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
logStderr('ℹ️ No changes to commit, proceeding to merge...');
|
|
98
|
+
}
|
|
99
|
+
// ── Step A4: Merge the PR ─────────────────────────────────────────────
|
|
100
|
+
if (!dryRun) {
|
|
101
|
+
logStderr(`🔀 Merging PR for issue #${issueNumber}...`);
|
|
102
|
+
const merge = await runHula(['merge', issueNumber]);
|
|
103
|
+
if (merge.code !== 0) {
|
|
104
|
+
logStderr(`${merge.stdout}\n${merge.stderr}`);
|
|
105
|
+
fail(2, `Failed to merge PR for issue #${issueNumber}. Session preserved — run /hula-merge again after resolving.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ── Step A5: Remove fix worktree ──────────────────────────────────────
|
|
109
|
+
if (!dryRun) {
|
|
110
|
+
logStderr(`🧹 Removing fix worktree: ${worktreePath}`);
|
|
111
|
+
const remove = await runGit([
|
|
112
|
+
'worktree',
|
|
113
|
+
'remove',
|
|
114
|
+
worktreePath,
|
|
115
|
+
'--force',
|
|
116
|
+
]);
|
|
117
|
+
if (remove.code !== 0) {
|
|
118
|
+
logStderr(`⚠️ Could not remove worktree at ${worktreePath} (may already be gone)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ── Step A6: Clean up session file ────────────────────────────────────
|
|
122
|
+
let sessionCleaned = false;
|
|
123
|
+
if (existsSync(sessionFile)) {
|
|
124
|
+
if (!dryRun) {
|
|
125
|
+
await rm(sessionFile, { force: true });
|
|
126
|
+
sessionCleaned = true;
|
|
127
|
+
logStderr('🧹 Cleaned up session file');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
logStderr(`🔍 Dry run: would delete session file ${sessionFile}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ── Output result ─────────────────────────────────────────────────────
|
|
134
|
+
emitJson({
|
|
135
|
+
status: 'success',
|
|
136
|
+
issueNumber: Number(issueNumber),
|
|
137
|
+
committed,
|
|
138
|
+
filesChanged,
|
|
139
|
+
worktreeRemoved: !dryRun,
|
|
140
|
+
sessionCleaned,
|
|
141
|
+
commitMessage: commitMsg,
|
|
142
|
+
worktreePath,
|
|
143
|
+
});
|
|
144
|
+
}, args);
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=merge-local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-local.js","sourceRoot":"","sources":["../../src/scripts/merge-local.ts"],"names":[],"mappings":"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"}
|