@gethmy/harness 1.2.0 → 1.2.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.
- package/dist/cli.js +6 -0
- package/dist/index.js +21 -1
- package/package.json +1 -1
- package/src/worktree.ts +30 -1
package/dist/cli.js
CHANGED
|
@@ -81,6 +81,11 @@ var init_constants = __esm(() => {
|
|
|
81
81
|
QUERY_GC_TIME: 1000 * 60 * 60 * 24
|
|
82
82
|
};
|
|
83
83
|
});
|
|
84
|
+
// ../harmony-shared/dist/fanoutSource.js
|
|
85
|
+
var FANOUT_KEY_MARKER = "harmony:fanout-item", FANOUT_KEY_RE;
|
|
86
|
+
var init_fanoutSource = __esm(() => {
|
|
87
|
+
FANOUT_KEY_RE = new RegExp(`^\\[${FANOUT_KEY_MARKER}\\]:\\s*#(\\S+)\\s*$`, "m");
|
|
88
|
+
});
|
|
84
89
|
// ../harmony-shared/dist/gateConfigError.js
|
|
85
90
|
function gateConfigErrorReason(evaluation) {
|
|
86
91
|
if (!evaluation || evaluation.passed)
|
|
@@ -496,6 +501,7 @@ var init_dist = __esm(() => {
|
|
|
496
501
|
init_columnSort();
|
|
497
502
|
init_commentSerializer();
|
|
498
503
|
init_constants();
|
|
504
|
+
init_fanoutSource();
|
|
499
505
|
init_gateConfigError();
|
|
500
506
|
init_gateEvaluate();
|
|
501
507
|
init_logger();
|
package/dist/index.js
CHANGED
|
@@ -164,6 +164,11 @@ var init_constants = __esm(() => {
|
|
|
164
164
|
QUERY_GC_TIME: 1000 * 60 * 60 * 24
|
|
165
165
|
};
|
|
166
166
|
});
|
|
167
|
+
// ../harmony-shared/dist/fanoutSource.js
|
|
168
|
+
var FANOUT_KEY_MARKER = "harmony:fanout-item", FANOUT_KEY_RE;
|
|
169
|
+
var init_fanoutSource = __esm(() => {
|
|
170
|
+
FANOUT_KEY_RE = new RegExp(`^\\[${FANOUT_KEY_MARKER}\\]:\\s*#(\\S+)\\s*$`, "m");
|
|
171
|
+
});
|
|
167
172
|
// ../harmony-shared/dist/gateConfigError.js
|
|
168
173
|
function gateConfigErrorReason(evaluation) {
|
|
169
174
|
if (!evaluation || evaluation.passed)
|
|
@@ -579,6 +584,7 @@ var init_dist = __esm(() => {
|
|
|
579
584
|
init_columnSort();
|
|
580
585
|
init_commentSerializer();
|
|
581
586
|
init_constants();
|
|
587
|
+
init_fanoutSource();
|
|
582
588
|
init_gateConfigError();
|
|
583
589
|
init_gateEvaluate();
|
|
584
590
|
init_logger();
|
|
@@ -3647,7 +3653,7 @@ async function runStage(request, deps) {
|
|
|
3647
3653
|
// src/worktree.ts
|
|
3648
3654
|
init_log();
|
|
3649
3655
|
import { execFileSync as execFileSync7, execSync } from "node:child_process";
|
|
3650
|
-
import { existsSync as existsSync3, rmSync } from "node:fs";
|
|
3656
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, rmSync } from "node:fs";
|
|
3651
3657
|
import { resolve as resolve3 } from "node:path";
|
|
3652
3658
|
var TAG13 = "worktree";
|
|
3653
3659
|
|
|
@@ -3781,11 +3787,25 @@ function createWorktree(basePath, baseBranch, branchName, opts = {}) {
|
|
|
3781
3787
|
}
|
|
3782
3788
|
return worktreeDir;
|
|
3783
3789
|
}
|
|
3790
|
+
function containsForeignWorktrees(dir) {
|
|
3791
|
+
if (existsSync3(resolve3(dir, ".git")))
|
|
3792
|
+
return false;
|
|
3793
|
+
let children;
|
|
3794
|
+
try {
|
|
3795
|
+
children = readdirSync2(dir);
|
|
3796
|
+
} catch {
|
|
3797
|
+
return false;
|
|
3798
|
+
}
|
|
3799
|
+
return children.some((child) => existsSync3(resolve3(dir, child, ".git")));
|
|
3800
|
+
}
|
|
3784
3801
|
function cleanupWorktree(worktreePath, branchName) {
|
|
3785
3802
|
const repoRoot = execFileSync7("git", ["rev-parse", "--show-toplevel"], {
|
|
3786
3803
|
encoding: "utf-8"
|
|
3787
3804
|
}).trim();
|
|
3788
3805
|
if (existsSync3(worktreePath)) {
|
|
3806
|
+
if (containsForeignWorktrees(worktreePath)) {
|
|
3807
|
+
throw new Error(`Refusing to remove ${worktreePath}: it is not a git worktree itself, ` + `but git worktrees live directly beneath it. Removing it would ` + `destroy them — pass the individual worktree paths instead (#928).`);
|
|
3808
|
+
}
|
|
3789
3809
|
try {
|
|
3790
3810
|
execFileSync7("git", ["worktree", "remove", worktreePath, "--force"], {
|
|
3791
3811
|
cwd: repoRoot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/harness",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Execution motor for Harmony playbook stages. Runs exactly one stage per invocation: worktree, role-separated subagents, held oracle, gate evidence. It never routes, never judges, never pushes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/worktree.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync, execSync } from "node:child_process";
|
|
2
|
-
import { existsSync, rmSync } from "node:fs";
|
|
2
|
+
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { log } from "./log.js";
|
|
5
5
|
import { installCommand } from "./pm.js";
|
|
@@ -356,6 +356,25 @@ export function createWorktree(
|
|
|
356
356
|
return worktreeDir;
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
/**
|
|
360
|
+
* True when `dir` is not itself a git worktree or repository (no `.git`
|
|
361
|
+
* entry) while a direct child directory carries one — i.e. `dir` is a
|
|
362
|
+
* CONTAINER of worktrees, like `.harmony-worktrees/agent-attempts/`.
|
|
363
|
+
* Force-removing such a directory destroys every worktree beneath it, live
|
|
364
|
+
* runs included (#928), so `cleanupWorktree` refuses it outright instead of
|
|
365
|
+
* escalating to `rmSync`.
|
|
366
|
+
*/
|
|
367
|
+
function containsForeignWorktrees(dir: string): boolean {
|
|
368
|
+
if (existsSync(resolve(dir, ".git"))) return false;
|
|
369
|
+
let children: string[];
|
|
370
|
+
try {
|
|
371
|
+
children = readdirSync(dir);
|
|
372
|
+
} catch {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
return children.some((child) => existsSync(resolve(dir, child, ".git")));
|
|
376
|
+
}
|
|
377
|
+
|
|
359
378
|
/**
|
|
360
379
|
* Remove a git worktree and its branch.
|
|
361
380
|
*/
|
|
@@ -368,6 +387,16 @@ export function cleanupWorktree(
|
|
|
368
387
|
}).trim();
|
|
369
388
|
|
|
370
389
|
if (existsSync(worktreePath)) {
|
|
390
|
+
// Never escalate on a container: `git worktree remove` would fail on it
|
|
391
|
+
// (it is not a worktree), and the old rmSync fallback then deleted every
|
|
392
|
+
// worktree underneath — including live ones with uncommitted work (#928).
|
|
393
|
+
if (containsForeignWorktrees(worktreePath)) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
`Refusing to remove ${worktreePath}: it is not a git worktree itself, ` +
|
|
396
|
+
`but git worktrees live directly beneath it. Removing it would ` +
|
|
397
|
+
`destroy them — pass the individual worktree paths instead (#928).`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
371
400
|
try {
|
|
372
401
|
execFileSync("git", ["worktree", "remove", worktreePath, "--force"], {
|
|
373
402
|
cwd: repoRoot,
|