halfcycle 0.12.0 → 0.13.0-rc.2
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/.claude-plugin/plugin.json +1 -1
- package/bin/bin.bundle.mjs +110 -17
- package/dist/banner-facts.d.ts +19 -2
- package/dist/banner-facts.d.ts.map +1 -1
- package/dist/build-record/render-v2.d.ts.map +1 -1
- package/dist/cli.js +615 -295
- package/dist/cli.js.map +3 -3
- package/dist/index.js +53 -4
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/local-allow.d.ts +1 -9
- package/dist/local-allow.d.ts.map +1 -1
- package/dist/worker-copies.d.ts +57 -0
- package/dist/worker-copies.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2447,7 +2447,7 @@ if (switchedOn) {
|
|
|
2447
2447
|
}
|
|
2448
2448
|
|
|
2449
2449
|
// dist/bin.js
|
|
2450
|
-
import { existsSync as
|
|
2450
|
+
import { existsSync as existsSync19, statSync as statSync3, writeSync as writeSync4 } from "node:fs";
|
|
2451
2451
|
|
|
2452
2452
|
// dist/install.js
|
|
2453
2453
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -2895,13 +2895,39 @@ var leftForOwnerSchema = z6.object({
|
|
|
2895
2895
|
}).strict().refine((v) => v.tasks.length + v.waiting.length > 0, {
|
|
2896
2896
|
message: "is sent only when some work was left for the owner"
|
|
2897
2897
|
});
|
|
2898
|
+
var TASKS_FILE_PATH = "docs/tasks.md";
|
|
2899
|
+
function isTicket(ref, url) {
|
|
2900
|
+
const issue = /^#([1-9][0-9]{0,9})$/.exec(ref);
|
|
2901
|
+
if (issue === null)
|
|
2902
|
+
return ref === TASKS_FILE_PATH && (url === void 0 || url === null);
|
|
2903
|
+
if (url === void 0 || url === null)
|
|
2904
|
+
return true;
|
|
2905
|
+
const address = /^(.*)\/issues\/([1-9][0-9]{0,9})$/.exec(url);
|
|
2906
|
+
return address !== null && address[2] === issue[1] && GITHUB_REPOSITORY_ADDRESS.test(address[1]) && !looksLikeSecret(url);
|
|
2907
|
+
}
|
|
2908
|
+
var TICKET_MESSAGE = `must be #<number> or ${TASKS_FILE_PATH}, with a link only beside #<number>: https://github.com/<owner>/<repo>/issues/<number>`;
|
|
2909
|
+
var ticketSchema = z6.object({
|
|
2910
|
+
ref: z6.string(),
|
|
2911
|
+
url: z6.string().optional()
|
|
2912
|
+
}).strict().refine((t) => isTicket(t.ref, t.url), { message: TICKET_MESSAGE });
|
|
2913
|
+
var TICKET_TITLE_MAX = 240;
|
|
2914
|
+
var TICKETS_MAX = 500;
|
|
2915
|
+
var ticketEntrySchema = z6.object({
|
|
2916
|
+
title: plainText(TICKET_TITLE_MAX),
|
|
2917
|
+
ref: z6.string(),
|
|
2918
|
+
url: z6.string().nullable()
|
|
2919
|
+
}).strict().refine((t) => isTicket(t.ref, t.url), { message: TICKET_MESSAGE });
|
|
2920
|
+
var ticketsSchema = z6.object({
|
|
2921
|
+
entries: z6.array(ticketEntrySchema).min(1).max(TICKETS_MAX)
|
|
2922
|
+
}).strict();
|
|
2898
2923
|
var observedCloseSchema = z6.object({
|
|
2899
2924
|
sequence: z6.number().int().min(1),
|
|
2900
2925
|
closedAt: utcIso8601,
|
|
2901
2926
|
closedBy: closedBySchema,
|
|
2902
2927
|
crossed: crossedSchema,
|
|
2903
2928
|
seatCoverage: seatCoverageSchema.nullable(),
|
|
2904
|
-
leftForOwner: leftForOwnerSchema.optional()
|
|
2929
|
+
leftForOwner: leftForOwnerSchema.optional(),
|
|
2930
|
+
tickets: ticketsSchema.optional()
|
|
2905
2931
|
}).strict();
|
|
2906
2932
|
var couldNotRunCheckSchema = z6.object({
|
|
2907
2933
|
check: plainText(),
|
|
@@ -3471,7 +3497,8 @@ var buildTaskSchema = z8.object({
|
|
|
3471
3497
|
ships: buildTaskShipsSchema.optional(),
|
|
3472
3498
|
// The commit or version the task reported when it shipped, exactly as reported, as
|
|
3473
3499
|
// one word with no spaces. Absent until it has shipped, or when it reported none.
|
|
3474
|
-
shipped: z8.string().min(1).max(BUILD_TASK_SHIPPED_MAX).regex(/^[^\s\x00-\x1f\x7f-\x9f]*$/, { message: "must be one word, with no spaces or control characters" }).optional()
|
|
3500
|
+
shipped: z8.string().min(1).max(BUILD_TASK_SHIPPED_MAX).regex(/^[^\s\x00-\x1f\x7f-\x9f]*$/, { message: "must be one word, with no spaces or control characters" }).optional(),
|
|
3501
|
+
ticket: ticketSchema.optional()
|
|
3475
3502
|
}).strict().superRefine((task, ctx) => {
|
|
3476
3503
|
if (task.reviews !== void 0 && task.reviews === task.id) {
|
|
3477
3504
|
ctx.addIssue({
|
|
@@ -7322,6 +7349,8 @@ var ownerWaitAddResultSchema = z37.discriminatedUnion("outcome", [
|
|
|
7322
7349
|
z37.object({ outcome: z37.literal("refused"), reason: z37.literal("too-many") }).strict()
|
|
7323
7350
|
]);
|
|
7324
7351
|
var ownerWaitUndoableSchema = z37.enum(["yes", "no"]);
|
|
7352
|
+
var OWNER_WAIT_STEP_MAX = 64;
|
|
7353
|
+
var ownerWaitStepSchema = z37.string().max(OWNER_WAIT_STEP_MAX).refine((v) => stepIdSchema.safeParse(v).success, { message: "must be the id of a step, as the method names it" });
|
|
7325
7354
|
var ownerWaitDeclareRequestSchema = z37.object({
|
|
7326
7355
|
engagementId: z37.string().min(1),
|
|
7327
7356
|
item: ownerWaitItemRefSchema,
|
|
@@ -7330,7 +7359,8 @@ var ownerWaitDeclareRequestSchema = z37.object({
|
|
|
7330
7359
|
task: buildTaskIdSchema.optional(),
|
|
7331
7360
|
by: agentNameSchema.optional(),
|
|
7332
7361
|
undoable: ownerWaitUndoableSchema.optional(),
|
|
7333
|
-
how: statementText(STATEMENT_MAX).optional()
|
|
7362
|
+
how: statementText(STATEMENT_MAX).optional(),
|
|
7363
|
+
step: ownerWaitStepSchema.optional()
|
|
7334
7364
|
}).strict().refine((r) => r.undoable === "yes" === (r.how !== void 0), {
|
|
7335
7365
|
message: "says how to undo it exactly when it can be undone",
|
|
7336
7366
|
path: ["how"]
|
|
@@ -10439,7 +10469,11 @@ HC_LEAD="$HC_LEAD|"$'\\xc2\\xa0|\\xe1\\x9a\\x80|\\xe2\\x80[\\x80-\\x8a\\xa8\\xa9
|
|
|
10439
10469
|
# reading is the same set or narrower): a number of agents, the most, faster, a
|
|
10440
10470
|
# vague amount it answers with an ask for a number, or a free share.
|
|
10441
10471
|
HC_PACE='((run|use|start|go)( +with| +at)? +)?(up +to +|the +)?(faster|more +agents|(-?[0-9]+(\\.[0-9]+)?|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty) +agents?|(max|maximum|full|top) +(pace|speed)|(max|maximum)( +possible)?( +number +of)? +agents|as +many +agents +as +(possible|you +can)|(lots +of|a +lot +of|loads +of|plenty +of|many|some|several|a +few|a +bunch +of) +agents)( +(at +once|at +a +time|in +parallel|concurrently|simultaneously|together))?|keep +[0-9]{1,3}% +free|use +up +to +[0-9]{1,3}%'
|
|
10442
|
-
|
|
10472
|
+
# A pace intent anywhere in a short prompt (a short one-line prompt is read from what the
|
|
10473
|
+
# person means; the runner decides the count of words and the line, this only lets a
|
|
10474
|
+
# candidate through, so it is the same set or wider).
|
|
10475
|
+
HC_INTENT='(max|maximum|full|top|fastest)( +[a-z]+){0,2} +(pace|speed)|as +many +agents +as +possible|faster|speed +up|(increase|raise|up)( +the)? +(pace|speed)|more +agents|slower|slow +down|(decrease|lower|reduce)( +the)? +(pace|speed)|fewer +agents|(-?[0-9]+(\\.[0-9]+)?|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty) +agents?'
|
|
10476
|
+
if ! printf '%s' "$HC_INPUT" | LC_ALL=C grep -Eiq "\\"prompt\\"[[:space:]]*:[[:space:]]*\\"($HC_LEAD)*((approve|send back|accept|question|carry|drop|reject|autonomy|decline) |allow( +[0-9]{1,4})?\\.?($HC_LEAD)*\\"|[^\\"]{0,200}($HC_INTENT)[^\\"]{0,200}\\"|(keep going|stop|stop here|limit it to [0-9]{1,9}(\\.[0-9]{1,2})? (times|million|billion|million tokens|billion tokens)|limit it to [0-9]{1,3}% of the week)($HC_LEAD)*\\"|(please,? +)?($HC_PACE)(,? +please)?[.!]*($HC_LEAD)*\\")"; then
|
|
10443
10477
|
exit 0
|
|
10444
10478
|
fi
|
|
10445
10479
|
|
|
@@ -11759,6 +11793,241 @@ function installSelfBuild(options) {
|
|
|
11759
11793
|
return { writtenPaths: report.writtenPaths, credentialWritten };
|
|
11760
11794
|
}
|
|
11761
11795
|
|
|
11796
|
+
// dist/worker-copies.js
|
|
11797
|
+
import { spawnSync } from "node:child_process";
|
|
11798
|
+
import { existsSync as existsSync7, lstatSync as lstatSync2, readdirSync, realpathSync, rmdirSync, rmSync as rmSync3 } from "node:fs";
|
|
11799
|
+
import { isAbsolute as isAbsolute3, join as join9, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
11800
|
+
var WORKER_COPY_CACHE_FOLDERS = ["__pycache__", ".pytest_cache", ".mypy_cache", "node_modules"];
|
|
11801
|
+
var COPIES_SHOWN = ".claude/worktrees/";
|
|
11802
|
+
var CACHE_NAMES = new Set(WORKER_COPY_CACHE_FOLDERS);
|
|
11803
|
+
var NOTHING = { removed: 0, kept: [], message: void 0 };
|
|
11804
|
+
var NOT_TIDIED = {
|
|
11805
|
+
removed: 0,
|
|
11806
|
+
kept: [],
|
|
11807
|
+
message: `[halfcycle] The worker copies under ${COPIES_SHOWN} were not tidied, because git did not answer.`
|
|
11808
|
+
};
|
|
11809
|
+
function tidyWorkerCopies(projectDir) {
|
|
11810
|
+
try {
|
|
11811
|
+
return tidy(projectDir);
|
|
11812
|
+
} catch {
|
|
11813
|
+
return NOT_TIDIED;
|
|
11814
|
+
}
|
|
11815
|
+
}
|
|
11816
|
+
function tidy(projectDir) {
|
|
11817
|
+
let root;
|
|
11818
|
+
try {
|
|
11819
|
+
root = realpathSync(resolve2(projectDir));
|
|
11820
|
+
} catch {
|
|
11821
|
+
return NOTHING;
|
|
11822
|
+
}
|
|
11823
|
+
if (isInsideCopiesFolder(root))
|
|
11824
|
+
return NOTHING;
|
|
11825
|
+
const top = git2(root, ["rev-parse", "--show-toplevel"]);
|
|
11826
|
+
if (!top.ran)
|
|
11827
|
+
return NOT_TIDIED;
|
|
11828
|
+
if (!top.ok)
|
|
11829
|
+
return NOTHING;
|
|
11830
|
+
const listed = git2(root, ["worktree", "list", "--porcelain"]);
|
|
11831
|
+
if (!listed.ok)
|
|
11832
|
+
return NOT_TIDIED;
|
|
11833
|
+
const worktrees = parseWorktrees(listed.out);
|
|
11834
|
+
const main2 = worktrees[0];
|
|
11835
|
+
if (main2 === void 0)
|
|
11836
|
+
return NOT_TIDIED;
|
|
11837
|
+
const mainPath = canonical(main2.path);
|
|
11838
|
+
if (!samePath(mainPath, canonical(top.out.trim())))
|
|
11839
|
+
return NOTHING;
|
|
11840
|
+
const copiesDir = join9(root, ".claude", "worktrees");
|
|
11841
|
+
let removed = 0;
|
|
11842
|
+
const kept = [];
|
|
11843
|
+
for (const tree of worktrees.slice(1)) {
|
|
11844
|
+
if (tree.bare)
|
|
11845
|
+
continue;
|
|
11846
|
+
const at = canonical(tree.path);
|
|
11847
|
+
if (samePath(at, mainPath) || !isInside(copiesDir, at))
|
|
11848
|
+
continue;
|
|
11849
|
+
if (!existsSync7(at))
|
|
11850
|
+
continue;
|
|
11851
|
+
const shown2 = relative2(root, at).split(sep).join("/");
|
|
11852
|
+
const held = tree.locked ? "work" : holdsWork(at, mainPath);
|
|
11853
|
+
if (held === "work") {
|
|
11854
|
+
kept.push(shown2);
|
|
11855
|
+
continue;
|
|
11856
|
+
}
|
|
11857
|
+
clearCaches(at, at);
|
|
11858
|
+
const remove = held === "duplicates" ? ["worktree", "remove", "--force", at] : ["worktree", "remove", at];
|
|
11859
|
+
if (!git2(root, remove).ok) {
|
|
11860
|
+
kept.push(shown2);
|
|
11861
|
+
continue;
|
|
11862
|
+
}
|
|
11863
|
+
removed += 1;
|
|
11864
|
+
if (tree.branch !== void 0)
|
|
11865
|
+
git2(root, ["branch", "-d", tree.branch]);
|
|
11866
|
+
}
|
|
11867
|
+
return { removed, kept, message: lineFor(removed, kept) };
|
|
11868
|
+
}
|
|
11869
|
+
function lineFor(removed, kept) {
|
|
11870
|
+
if (removed === 0 && kept.length === 0)
|
|
11871
|
+
return void 0;
|
|
11872
|
+
const head = `[halfcycle] Removed ${removed} worker ${removed === 1 ? "copy" : "copies"} under ${COPIES_SHOWN}.`;
|
|
11873
|
+
return kept.length === 0 ? head : `${head} Kept ${kept.length} that may hold work: ${kept.join(", ")}.`;
|
|
11874
|
+
}
|
|
11875
|
+
function git2(cwd, args2) {
|
|
11876
|
+
const r = spawnSync("git", [...args2], {
|
|
11877
|
+
cwd,
|
|
11878
|
+
encoding: "utf-8",
|
|
11879
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11880
|
+
windowsHide: true,
|
|
11881
|
+
timeout: 12e4,
|
|
11882
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
11883
|
+
});
|
|
11884
|
+
if (r.error !== void 0)
|
|
11885
|
+
return { ran: false, ok: false, out: "" };
|
|
11886
|
+
return { ran: true, ok: r.status === 0, out: r.stdout ?? "" };
|
|
11887
|
+
}
|
|
11888
|
+
function parseWorktrees(porcelain) {
|
|
11889
|
+
const trees = [];
|
|
11890
|
+
let current;
|
|
11891
|
+
for (const line of porcelain.split(/\r?\n/)) {
|
|
11892
|
+
if (line.startsWith("worktree ")) {
|
|
11893
|
+
current = { path: line.slice("worktree ".length), branch: void 0, locked: false, bare: false };
|
|
11894
|
+
trees.push(current);
|
|
11895
|
+
} else if (current === void 0) {
|
|
11896
|
+
continue;
|
|
11897
|
+
} else if (line.startsWith("branch refs/heads/")) {
|
|
11898
|
+
current.branch = line.slice("branch refs/heads/".length);
|
|
11899
|
+
} else if (line === "locked" || line.startsWith("locked ")) {
|
|
11900
|
+
current.locked = true;
|
|
11901
|
+
} else if (line === "bare") {
|
|
11902
|
+
current.bare = true;
|
|
11903
|
+
}
|
|
11904
|
+
}
|
|
11905
|
+
return trees;
|
|
11906
|
+
}
|
|
11907
|
+
function holdsWork(copyDir, mainDir) {
|
|
11908
|
+
const status = git2(copyDir, ["status", "--porcelain", "-z", "--untracked-files=all"]);
|
|
11909
|
+
if (!status.ok)
|
|
11910
|
+
return "work";
|
|
11911
|
+
let duplicates = false;
|
|
11912
|
+
const fields = status.out.split("\0");
|
|
11913
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
11914
|
+
const field = fields[i];
|
|
11915
|
+
if (field === "")
|
|
11916
|
+
continue;
|
|
11917
|
+
const code = field.slice(0, 2);
|
|
11918
|
+
const path = field.slice(3);
|
|
11919
|
+
if (code === "??" && path.split("/").some((segment) => CACHE_NAMES.has(segment)))
|
|
11920
|
+
continue;
|
|
11921
|
+
if (code !== "??" && code !== " M" && code !== "M " && code !== "A ")
|
|
11922
|
+
return "work";
|
|
11923
|
+
if (!duplicatesMain(copyDir, mainDir, path))
|
|
11924
|
+
return "work";
|
|
11925
|
+
duplicates = true;
|
|
11926
|
+
}
|
|
11927
|
+
return duplicates ? "duplicates" : "clean";
|
|
11928
|
+
}
|
|
11929
|
+
function duplicatesMain(copyDir, mainDir, path) {
|
|
11930
|
+
if (path === "" || path.endsWith("/"))
|
|
11931
|
+
return false;
|
|
11932
|
+
let executable;
|
|
11933
|
+
try {
|
|
11934
|
+
const entry = lstatSync2(join9(copyDir, ...path.split("/")));
|
|
11935
|
+
if (!entry.isFile())
|
|
11936
|
+
return false;
|
|
11937
|
+
executable = (entry.mode & 64) !== 0;
|
|
11938
|
+
} catch {
|
|
11939
|
+
return false;
|
|
11940
|
+
}
|
|
11941
|
+
const listed = git2(mainDir, ["--literal-pathspecs", "ls-tree", "-z", "--full-tree", "HEAD", "--", path]);
|
|
11942
|
+
if (!listed.ok)
|
|
11943
|
+
return false;
|
|
11944
|
+
const entries = listed.out.split("\0").filter((entry) => entry !== "");
|
|
11945
|
+
if (entries.length !== 1)
|
|
11946
|
+
return false;
|
|
11947
|
+
const match = /^(100644|100755) blob ([0-9a-f]+)\t([\s\S]*)$/.exec(entries[0]);
|
|
11948
|
+
if (match === null || match[3] !== path)
|
|
11949
|
+
return false;
|
|
11950
|
+
if (process.platform !== "win32" && executable !== (match[1] === "100755"))
|
|
11951
|
+
return false;
|
|
11952
|
+
const hashed = git2(copyDir, ["hash-object", "--no-filters", "--", path]);
|
|
11953
|
+
return hashed.ok && hashed.out.trim() === match[2];
|
|
11954
|
+
}
|
|
11955
|
+
function clearCaches(copyDir, dir) {
|
|
11956
|
+
let names;
|
|
11957
|
+
try {
|
|
11958
|
+
names = readdirSync(dir);
|
|
11959
|
+
} catch {
|
|
11960
|
+
return;
|
|
11961
|
+
}
|
|
11962
|
+
for (const name of names) {
|
|
11963
|
+
if (name === ".git")
|
|
11964
|
+
continue;
|
|
11965
|
+
const at = join9(dir, name);
|
|
11966
|
+
let entry;
|
|
11967
|
+
try {
|
|
11968
|
+
entry = lstatSync2(at);
|
|
11969
|
+
} catch {
|
|
11970
|
+
continue;
|
|
11971
|
+
}
|
|
11972
|
+
if (entry.isSymbolicLink()) {
|
|
11973
|
+
if (CACHE_NAMES.has(name) && !holdsTracked(copyDir, at))
|
|
11974
|
+
removeLink(at);
|
|
11975
|
+
continue;
|
|
11976
|
+
}
|
|
11977
|
+
if (!entry.isDirectory())
|
|
11978
|
+
continue;
|
|
11979
|
+
if (CACHE_NAMES.has(name)) {
|
|
11980
|
+
if (holdsTracked(copyDir, at))
|
|
11981
|
+
continue;
|
|
11982
|
+
try {
|
|
11983
|
+
rmSync3(at, { recursive: true, force: true });
|
|
11984
|
+
} catch {
|
|
11985
|
+
}
|
|
11986
|
+
continue;
|
|
11987
|
+
}
|
|
11988
|
+
clearCaches(copyDir, at);
|
|
11989
|
+
}
|
|
11990
|
+
}
|
|
11991
|
+
function holdsTracked(copyDir, path) {
|
|
11992
|
+
const listed = git2(copyDir, ["ls-files", "-z", "--", relative2(copyDir, path).split(sep).join("/")]);
|
|
11993
|
+
return !listed.ok || listed.out !== "";
|
|
11994
|
+
}
|
|
11995
|
+
function removeLink(at) {
|
|
11996
|
+
try {
|
|
11997
|
+
rmSync3(at, { force: true });
|
|
11998
|
+
} catch {
|
|
11999
|
+
try {
|
|
12000
|
+
rmdirSync(at);
|
|
12001
|
+
} catch {
|
|
12002
|
+
}
|
|
12003
|
+
}
|
|
12004
|
+
}
|
|
12005
|
+
function canonical(path) {
|
|
12006
|
+
try {
|
|
12007
|
+
return realpathSync(path);
|
|
12008
|
+
} catch {
|
|
12009
|
+
return resolve2(path);
|
|
12010
|
+
}
|
|
12011
|
+
}
|
|
12012
|
+
function fold(path) {
|
|
12013
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
12014
|
+
}
|
|
12015
|
+
function samePath(a, b) {
|
|
12016
|
+
return fold(resolve2(a)) === fold(resolve2(b));
|
|
12017
|
+
}
|
|
12018
|
+
function isInside(parent, child) {
|
|
12019
|
+
const rel = relative2(fold(parent), fold(child));
|
|
12020
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
|
|
12021
|
+
}
|
|
12022
|
+
function isInsideCopiesFolder(path) {
|
|
12023
|
+
const segments = fold(path).split(sep);
|
|
12024
|
+
for (let i = 0; i + 2 < segments.length; i += 1) {
|
|
12025
|
+
if (segments[i] === ".claude" && segments[i + 1] === "worktrees")
|
|
12026
|
+
return true;
|
|
12027
|
+
}
|
|
12028
|
+
return false;
|
|
12029
|
+
}
|
|
12030
|
+
|
|
11762
12031
|
// dist/control-origin.js
|
|
11763
12032
|
var DEFAULT_CONTROL_ORIGIN = "https://control.halfcycle.ai";
|
|
11764
12033
|
function resolveControlOrigin(env = process.env) {
|
|
@@ -11870,14 +12139,14 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11870
12139
|
else
|
|
11871
12140
|
pending = callback;
|
|
11872
12141
|
}, engagementId, serviceUrl));
|
|
11873
|
-
const bindProblem = await new Promise((
|
|
12142
|
+
const bindProblem = await new Promise((resolve10) => {
|
|
11874
12143
|
const onError = (err) => {
|
|
11875
|
-
|
|
12144
|
+
resolve10(`${err.code ?? "bind failed"} on ${LOOPBACK_REDIRECT_HOST}:${wanted.port}`);
|
|
11876
12145
|
};
|
|
11877
12146
|
server.once("error", onError);
|
|
11878
12147
|
server.listen({ host: LOOPBACK_REDIRECT_HOST, port: wanted.port }, () => {
|
|
11879
12148
|
server.removeListener("error", onError);
|
|
11880
|
-
|
|
12149
|
+
resolve10(null);
|
|
11881
12150
|
});
|
|
11882
12151
|
});
|
|
11883
12152
|
if (bindProblem !== null) {
|
|
@@ -11893,9 +12162,9 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11893
12162
|
bound: true,
|
|
11894
12163
|
target: { port: address.port, state },
|
|
11895
12164
|
boundAddress: address.address,
|
|
11896
|
-
waitForCallback: ({ timeoutMs, onStillWaiting, stillWaitingMs = DEFAULT_STILL_WAITING_MS }) => new Promise((
|
|
12165
|
+
waitForCallback: ({ timeoutMs, onStillWaiting, stillWaitingMs = DEFAULT_STILL_WAITING_MS }) => new Promise((resolve10) => {
|
|
11897
12166
|
if (pending !== null) {
|
|
11898
|
-
|
|
12167
|
+
resolve10(pending);
|
|
11899
12168
|
return;
|
|
11900
12169
|
}
|
|
11901
12170
|
const startedAt = Date.now();
|
|
@@ -11906,7 +12175,7 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11906
12175
|
clearTimeout(timer);
|
|
11907
12176
|
deliver = null;
|
|
11908
12177
|
abandon = null;
|
|
11909
|
-
|
|
12178
|
+
resolve10(value);
|
|
11910
12179
|
};
|
|
11911
12180
|
const timer = setTimeout(() => finish(null), Math.max(0, timeoutMs));
|
|
11912
12181
|
deliver = finish;
|
|
@@ -11920,23 +12189,23 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11920
12189
|
};
|
|
11921
12190
|
}
|
|
11922
12191
|
function closeServer(server) {
|
|
11923
|
-
return new Promise((
|
|
12192
|
+
return new Promise((resolve10) => {
|
|
11924
12193
|
server.closeAllConnections();
|
|
11925
|
-
server.close(() =>
|
|
12194
|
+
server.close(() => resolve10());
|
|
11926
12195
|
});
|
|
11927
12196
|
}
|
|
11928
12197
|
|
|
11929
12198
|
// dist/sign-in-attempt.js
|
|
11930
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync, rmSync as
|
|
12199
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync2, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
11931
12200
|
import { homedir as homedir4 } from "node:os";
|
|
11932
|
-
import { join as
|
|
12201
|
+
import { join as join10 } from "node:path";
|
|
11933
12202
|
var ATTEMPT_FILENAME = "sign-in-attempt.json";
|
|
11934
12203
|
function attemptPath(home) {
|
|
11935
|
-
return
|
|
12204
|
+
return join10(home ?? homedir4(), HALFCYCLE_DIR_NAME, ATTEMPT_FILENAME);
|
|
11936
12205
|
}
|
|
11937
12206
|
function realFolder(folder2) {
|
|
11938
12207
|
try {
|
|
11939
|
-
return
|
|
12208
|
+
return realpathSync2(folder2);
|
|
11940
12209
|
} catch {
|
|
11941
12210
|
return folder2;
|
|
11942
12211
|
}
|
|
@@ -11944,7 +12213,7 @@ function realFolder(folder2) {
|
|
|
11944
12213
|
function recordSignInAttempt(folder2, expiresAt, home) {
|
|
11945
12214
|
try {
|
|
11946
12215
|
const path = attemptPath(home);
|
|
11947
|
-
mkdirSync6(
|
|
12216
|
+
mkdirSync6(join10(home ?? homedir4(), HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
|
|
11948
12217
|
const record2 = { folder: realFolder(folder2), expiresAt };
|
|
11949
12218
|
writeFileSync6(path, `${JSON.stringify(record2)}
|
|
11950
12219
|
`, { mode: 384 });
|
|
@@ -11954,7 +12223,7 @@ function recordSignInAttempt(folder2, expiresAt, home) {
|
|
|
11954
12223
|
}
|
|
11955
12224
|
function clearSignInAttempt(home) {
|
|
11956
12225
|
try {
|
|
11957
|
-
|
|
12226
|
+
rmSync4(attemptPath(home), { force: true });
|
|
11958
12227
|
} catch {
|
|
11959
12228
|
return;
|
|
11960
12229
|
}
|
|
@@ -11997,7 +12266,7 @@ function defaultWrite(text) {
|
|
|
11997
12266
|
process.stdout.write(text);
|
|
11998
12267
|
}
|
|
11999
12268
|
function defaultSleep(ms) {
|
|
12000
|
-
return new Promise((
|
|
12269
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
12001
12270
|
}
|
|
12002
12271
|
function browserOpenCommand(os, url) {
|
|
12003
12272
|
if (os === "darwin")
|
|
@@ -12044,12 +12313,12 @@ async function openBrowser(url, env = process.env, os = platform3(), spawnImpl =
|
|
|
12044
12313
|
};
|
|
12045
12314
|
}
|
|
12046
12315
|
const { command, args: args2, windowsVerbatimArguments } = browserOpenCommand(os, url);
|
|
12047
|
-
return new Promise((
|
|
12316
|
+
return new Promise((resolve10) => {
|
|
12048
12317
|
let child;
|
|
12049
12318
|
try {
|
|
12050
12319
|
child = spawnImpl(command, args2, { stdio: "ignore", env, windowsVerbatimArguments });
|
|
12051
12320
|
} catch {
|
|
12052
|
-
|
|
12321
|
+
resolve10({ opened: false, reason: `${command} could not be run` });
|
|
12053
12322
|
return;
|
|
12054
12323
|
}
|
|
12055
12324
|
let settled = false;
|
|
@@ -12058,7 +12327,7 @@ async function openBrowser(url, env = process.env, os = platform3(), spawnImpl =
|
|
|
12058
12327
|
return;
|
|
12059
12328
|
settled = true;
|
|
12060
12329
|
clearTimeout(timer);
|
|
12061
|
-
|
|
12330
|
+
resolve10(attempt2);
|
|
12062
12331
|
};
|
|
12063
12332
|
const timer = setTimeout(() => {
|
|
12064
12333
|
child.unref();
|
|
@@ -12203,17 +12472,17 @@ async function pollBesideLoopback(serviceUrl, started, wake, deps) {
|
|
|
12203
12472
|
continue;
|
|
12204
12473
|
}
|
|
12205
12474
|
if (sleep === void 0) {
|
|
12206
|
-
await new Promise((
|
|
12207
|
-
const timer = setTimeout(
|
|
12475
|
+
await new Promise((resolve10) => {
|
|
12476
|
+
const timer = setTimeout(resolve10, started.pollIntervalMs);
|
|
12208
12477
|
interrupt = () => {
|
|
12209
12478
|
clearTimeout(timer);
|
|
12210
|
-
|
|
12479
|
+
resolve10();
|
|
12211
12480
|
};
|
|
12212
12481
|
});
|
|
12213
12482
|
} else {
|
|
12214
|
-
await new Promise((
|
|
12215
|
-
interrupt =
|
|
12216
|
-
void sleep(started.pollIntervalMs).then(
|
|
12483
|
+
await new Promise((resolve10) => {
|
|
12484
|
+
interrupt = resolve10;
|
|
12485
|
+
void sleep(started.pollIntervalMs).then(resolve10);
|
|
12217
12486
|
});
|
|
12218
12487
|
}
|
|
12219
12488
|
interrupt = null;
|
|
@@ -12819,7 +13088,7 @@ function worthAnotherTry(value) {
|
|
|
12819
13088
|
return { tryAgain: true, value };
|
|
12820
13089
|
}
|
|
12821
13090
|
function realSleep(ms) {
|
|
12822
|
-
return new Promise((
|
|
13091
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
12823
13092
|
}
|
|
12824
13093
|
async function attemptInTurn(attempt2, options = {}) {
|
|
12825
13094
|
const timeouts = options.timeouts ?? ATTEMPT_TIMEOUTS_MS;
|
|
@@ -13017,9 +13286,9 @@ function wrongAccountVisibility(act) {
|
|
|
13017
13286
|
async function askLine(question2) {
|
|
13018
13287
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13019
13288
|
try {
|
|
13020
|
-
return await new Promise((
|
|
13021
|
-
rl.once("close", () =>
|
|
13022
|
-
rl.question(question2).then(
|
|
13289
|
+
return await new Promise((resolve10) => {
|
|
13290
|
+
rl.once("close", () => resolve10(""));
|
|
13291
|
+
rl.question(question2).then(resolve10, () => resolve10(""));
|
|
13023
13292
|
});
|
|
13024
13293
|
} finally {
|
|
13025
13294
|
rl.close();
|
|
@@ -13152,7 +13421,7 @@ async function confirmActingIdentity(serviceUrl, credential, act, deps = {}) {
|
|
|
13152
13421
|
}
|
|
13153
13422
|
|
|
13154
13423
|
// dist/project-name.js
|
|
13155
|
-
import { basename, resolve as
|
|
13424
|
+
import { basename, resolve as resolve3 } from "node:path";
|
|
13156
13425
|
var MAX_DERIVED_NAME_LEN = 100;
|
|
13157
13426
|
var EMPTY_SEGMENTS = /* @__PURE__ */ new Set(["", ".", "..", "/", "\\"]);
|
|
13158
13427
|
function repositoryNameFromRemote(remote) {
|
|
@@ -13172,7 +13441,7 @@ function isLocalRemote(remote) {
|
|
|
13172
13441
|
function deriveProjectName(targetRepoRoot) {
|
|
13173
13442
|
const remote = readRemote(targetRepoRoot);
|
|
13174
13443
|
const fromRemote = remote && !isLocalRemote(remote) ? repositoryNameFromRemote(remote) : void 0;
|
|
13175
|
-
const candidate = fromRemote ?? basename(
|
|
13444
|
+
const candidate = fromRemote ?? basename(resolve3(targetRepoRoot));
|
|
13176
13445
|
const name = candidate.trim();
|
|
13177
13446
|
if (EMPTY_SEGMENTS.has(name))
|
|
13178
13447
|
return void 0;
|
|
@@ -13261,9 +13530,9 @@ async function joinPinnedEngagement(serviceUrl, engagementId, targetRepo, deps =
|
|
|
13261
13530
|
}
|
|
13262
13531
|
|
|
13263
13532
|
// dist/pre-create-checks.js
|
|
13264
|
-
import { spawnSync } from "node:child_process";
|
|
13265
|
-
import { existsSync as
|
|
13266
|
-
import { delimiter, dirname as dirname5, join as
|
|
13533
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13534
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, realpathSync as realpathSync3 } from "node:fs";
|
|
13535
|
+
import { delimiter, dirname as dirname5, join as join11 } from "node:path";
|
|
13267
13536
|
var KEPT_FOLDER_ID_LINE = `Nothing on the Halfcycle service needs undoing, and this folder keeps its project id, so running "${ownBareCommand()}" again picks up where it left off.`;
|
|
13268
13537
|
var PreCreateRefused = class extends Error {
|
|
13269
13538
|
constructor(message) {
|
|
@@ -13306,14 +13575,14 @@ function refuseSystemFolder(targetRepo, platform6 = process.platform) {
|
|
|
13306
13575
|
function refuseInsideProject(targetRepo) {
|
|
13307
13576
|
let folder2 = realOrLiteral(targetRepo);
|
|
13308
13577
|
for (let parent = dirname5(folder2); parent !== folder2; folder2 = parent, parent = dirname5(parent)) {
|
|
13309
|
-
if (
|
|
13578
|
+
if (existsSync8(join11(parent, BUNDLE_PIN_REL))) {
|
|
13310
13579
|
throw new PreCreateRefused(`${targetRepo} is inside the Halfcycle project at ${parent} \u2014 open that folder in your coding agent instead, or run "${ownBareCommand()}" in a folder outside it.`);
|
|
13311
13580
|
}
|
|
13312
13581
|
}
|
|
13313
13582
|
}
|
|
13314
13583
|
function realOrLiteral(folder2) {
|
|
13315
13584
|
try {
|
|
13316
|
-
return
|
|
13585
|
+
return realpathSync3(folder2);
|
|
13317
13586
|
} catch {
|
|
13318
13587
|
return folder2;
|
|
13319
13588
|
}
|
|
@@ -13330,22 +13599,22 @@ async function offerGitForWindows() {
|
|
|
13330
13599
|
throw new PreCreateRefused(`Git for Windows is needed and is not installed \u2014 install it with "${GIT_INSTALL_COMMAND}" or from ${GIT_DOWNLOAD_PAGE}, then open a new terminal and run "${ownBareCommand()}" again.`);
|
|
13331
13600
|
}
|
|
13332
13601
|
function gitBashPath(env) {
|
|
13333
|
-
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" &&
|
|
13602
|
+
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" && existsSync8(join11(dir, "git.exe"))).map((dir) => join11(dir, "..", "bin", "bash.exe"));
|
|
13334
13603
|
const candidates = [
|
|
13335
13604
|
env["CLAUDE_CODE_GIT_BASH_PATH"],
|
|
13336
13605
|
...onPath,
|
|
13337
|
-
env["ProgramFiles"] === void 0 ? void 0 :
|
|
13606
|
+
env["ProgramFiles"] === void 0 ? void 0 : join11(env["ProgramFiles"], "Git", "bin", "bash.exe")
|
|
13338
13607
|
];
|
|
13339
|
-
return candidates.find((file) => file !== void 0 && file !== "" &&
|
|
13608
|
+
return candidates.find((file) => file !== void 0 && file !== "" && existsSync8(file));
|
|
13340
13609
|
}
|
|
13341
13610
|
function hasGitBash(env) {
|
|
13342
13611
|
return gitBashPath(env) !== void 0;
|
|
13343
13612
|
}
|
|
13344
13613
|
function runWingetInstall() {
|
|
13345
|
-
return
|
|
13614
|
+
return spawnSync2("winget", [...GIT_INSTALL_ARGS], { stdio: "inherit" }).status === 0;
|
|
13346
13615
|
}
|
|
13347
13616
|
function unreadableProjectRefusal(targetRepo) {
|
|
13348
|
-
return `${
|
|
13617
|
+
return `${join11(targetRepo, PROJECT_IDENTITY_REL)} could not be read as a Halfcycle project file \u2014 delete the .halfcycle folder in ${targetRepo} and run "${ownBareCommand()}" again to start a new project here.`;
|
|
13349
13618
|
}
|
|
13350
13619
|
function cannotWriteRefusal(targetRepo) {
|
|
13351
13620
|
return `cannot write to ${targetRepo} \u2014 run "${ownBareCommand()}" in a folder you can write to.`;
|
|
@@ -13365,7 +13634,7 @@ function claimFolderId(targetRepo) {
|
|
|
13365
13634
|
function readFolderId(targetRepo) {
|
|
13366
13635
|
let folderId;
|
|
13367
13636
|
try {
|
|
13368
|
-
const text = readFileSync9(
|
|
13637
|
+
const text = readFileSync9(join11(targetRepo, PROJECT_IDENTITY_REL), "utf-8");
|
|
13369
13638
|
folderId = JSON.parse(text)?.projectId;
|
|
13370
13639
|
} catch {
|
|
13371
13640
|
return void 0;
|
|
@@ -13382,7 +13651,7 @@ function checkSettingsParse(targetRepo) {
|
|
|
13382
13651
|
}
|
|
13383
13652
|
|
|
13384
13653
|
// dist/folder-refusals.js
|
|
13385
|
-
import { realpathSync as
|
|
13654
|
+
import { realpathSync as realpathSync4 } from "node:fs";
|
|
13386
13655
|
import { homedir as homedir5 } from "node:os";
|
|
13387
13656
|
function folderMissingRefusal(targetRepo) {
|
|
13388
13657
|
return `halfcycle: no such directory: ${targetRepo}`;
|
|
@@ -13402,8 +13671,8 @@ function homeFolderRefusal(targetRepo) {
|
|
|
13402
13671
|
}
|
|
13403
13672
|
function isHomeFolder(dir) {
|
|
13404
13673
|
try {
|
|
13405
|
-
const target =
|
|
13406
|
-
const home =
|
|
13674
|
+
const target = realpathSync4(dir);
|
|
13675
|
+
const home = realpathSync4(homedir5());
|
|
13407
13676
|
return process.platform === "win32" ? target.toLowerCase() === home.toLowerCase() : target === home;
|
|
13408
13677
|
} catch {
|
|
13409
13678
|
return false;
|
|
@@ -13411,16 +13680,16 @@ function isHomeFolder(dir) {
|
|
|
13411
13680
|
}
|
|
13412
13681
|
|
|
13413
13682
|
// dist/preview.js
|
|
13414
|
-
import { accessSync, constants, existsSync as
|
|
13415
|
-
import { join as
|
|
13683
|
+
import { accessSync, constants, existsSync as existsSync10, readFileSync as readFileSync12, statSync } from "node:fs";
|
|
13684
|
+
import { join as join14 } from "node:path";
|
|
13416
13685
|
|
|
13417
13686
|
// dist/halfcycle-monorepo.js
|
|
13418
13687
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
13419
|
-
import { join as
|
|
13688
|
+
import { join as join12 } from "node:path";
|
|
13420
13689
|
var MONOREPO_PACKAGE_NAME = "halfcycle-monorepo";
|
|
13421
13690
|
function isHalfcycleMonorepo(dir) {
|
|
13422
13691
|
try {
|
|
13423
|
-
const pkg = JSON.parse(readFileSync10(
|
|
13692
|
+
const pkg = JSON.parse(readFileSync10(join12(dir, "package.json"), "utf-8"));
|
|
13424
13693
|
return pkg.name === MONOREPO_PACKAGE_NAME;
|
|
13425
13694
|
} catch {
|
|
13426
13695
|
return false;
|
|
@@ -13432,8 +13701,8 @@ function isSelfBuildInstallForm(engagementIdArg, engagementTypeArg) {
|
|
|
13432
13701
|
|
|
13433
13702
|
// dist/local-allow.js
|
|
13434
13703
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
13435
|
-
import { existsSync as
|
|
13436
|
-
import { dirname as dirname6, join as
|
|
13704
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
13705
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
13437
13706
|
var LOCAL_SETTINGS_REL2 = ".claude/settings.local.json";
|
|
13438
13707
|
var LOCAL_ALLOW_RECORD_REL = ".halfcycle/local-allow.json";
|
|
13439
13708
|
var LOCAL_ALLOW_EDIT_MODE = "acceptEdits";
|
|
@@ -13441,7 +13710,6 @@ var SCRIPTS = ["test", "build", "lint", "typecheck"];
|
|
|
13441
13710
|
var SCRIPT_RUNNERS = ["npm run", "pnpm", "pnpm run", "yarn", "bun run"];
|
|
13442
13711
|
var HALFCYCLE_TOOLS_RULE = `mcp__${MCP_SERVER_KEY}`;
|
|
13443
13712
|
var bash = (command) => `Bash(${command}:*)`;
|
|
13444
|
-
var CONTEXT_BUDGET_IGNORE_CHECK = "git check-ignore -q .halfcycle/context-budget.json";
|
|
13445
13713
|
var COMMIT_FILE_LISTING = "git ls-tree -r --name-only";
|
|
13446
13714
|
var INSTALLED_HOOK_SCRIPTS = Object.keys(OWNED_GENERATED_HEADERS).filter((path) => path.startsWith(".claude/hooks/"));
|
|
13447
13715
|
var LOCAL_ALLOW_RULES = [
|
|
@@ -13477,7 +13745,6 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13477
13745
|
bash("git merge"),
|
|
13478
13746
|
bash("git worktree add"),
|
|
13479
13747
|
bash("git worktree list"),
|
|
13480
|
-
`Bash(${CONTEXT_BUDGET_IGNORE_CHECK})`,
|
|
13481
13748
|
bash("git pull --ff-only"),
|
|
13482
13749
|
bash("git pull --rebase"),
|
|
13483
13750
|
bash("npm install"),
|
|
@@ -13489,6 +13756,8 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13489
13756
|
bash("yarn add"),
|
|
13490
13757
|
bash("bun install"),
|
|
13491
13758
|
bash("bun add"),
|
|
13759
|
+
bash("gh repo view"),
|
|
13760
|
+
bash("gh issue create"),
|
|
13492
13761
|
bash("go mod tidy"),
|
|
13493
13762
|
bash("go get"),
|
|
13494
13763
|
bash("cargo add"),
|
|
@@ -13528,7 +13797,7 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13528
13797
|
bash("tr"),
|
|
13529
13798
|
HALFCYCLE_TOOLS_RULE
|
|
13530
13799
|
];
|
|
13531
|
-
var LOCAL_ALLOW_QUESTION = "Let your coding agent work without asking on this computer? It would run your tests and builds, install packages, run local tools, make and move files, use git locally, run Halfcycle, and edit files in this project without a permission prompt. Pushing, publishing, deploying to production
|
|
13800
|
+
var LOCAL_ALLOW_QUESTION = "Let your coding agent work without asking on this computer? It would run your tests and builds, install packages, run local tools, make and move files, use git locally, run Halfcycle, and edit files in this project without a permission prompt. Opening a ticket on GitHub does not ask either. Pushing, publishing, deploying to production and deleting still ask. This is saved only on this computer, in .claude/settings.local.json, which is not committed.\nType yes to allow it, or press Enter to keep asking: ";
|
|
13532
13801
|
var LOCAL_ALLOW_ON_LINE = `Your coding agent can now work without asking on this computer. To undo it, run: ${ownCommand()} allow --remove`;
|
|
13533
13802
|
var LOCAL_ALLOW_OFF_LINE = `Your coding agent will ask before it runs commands. To stop most of those questions on this computer, run: ${ownCommand()} allow`;
|
|
13534
13803
|
var LOCAL_ALLOW_TRACKED_LINE = "Your .claude/settings.local.json is committed to git, so Halfcycle will not add rules to it. Remove it from git first if you want them.";
|
|
@@ -13550,8 +13819,8 @@ function trackedByGit(repo) {
|
|
|
13550
13819
|
}
|
|
13551
13820
|
}
|
|
13552
13821
|
function readRecord2(repo) {
|
|
13553
|
-
const path =
|
|
13554
|
-
if (!
|
|
13822
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13823
|
+
if (!existsSync9(path))
|
|
13555
13824
|
return null;
|
|
13556
13825
|
try {
|
|
13557
13826
|
const raw = JSON.parse(readFileSync11(path, "utf8"));
|
|
@@ -13567,12 +13836,12 @@ function readRecord2(repo) {
|
|
|
13567
13836
|
}
|
|
13568
13837
|
}
|
|
13569
13838
|
function writeRecord(repo, record2) {
|
|
13570
|
-
const path =
|
|
13839
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13571
13840
|
mkdirSync7(dirname6(path), { recursive: true });
|
|
13572
13841
|
writeFileSync7(path, JSON.stringify({ rules: record2.rules, defaultMode: record2.defaultMode, before: record2.before }, null, 2) + "\n");
|
|
13573
13842
|
}
|
|
13574
13843
|
function holdsLocalAllowRecord(repo) {
|
|
13575
|
-
return
|
|
13844
|
+
return existsSync9(join13(repo, LOCAL_ALLOW_RECORD_REL));
|
|
13576
13845
|
}
|
|
13577
13846
|
function layoutOf(text) {
|
|
13578
13847
|
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -13587,8 +13856,8 @@ function render(value, like) {
|
|
|
13587
13856
|
function writeLocalAllow(repo) {
|
|
13588
13857
|
if (trackedByGit(repo))
|
|
13589
13858
|
return { kind: "tracked", line: LOCAL_ALLOW_TRACKED_LINE };
|
|
13590
|
-
const path =
|
|
13591
|
-
const existed =
|
|
13859
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13860
|
+
const existed = existsSync9(path);
|
|
13592
13861
|
const text = existed ? readFileSync11(path, "utf8") : null;
|
|
13593
13862
|
let settings = {};
|
|
13594
13863
|
if (text !== null) {
|
|
@@ -13631,14 +13900,14 @@ function writeLocalAllow(repo) {
|
|
|
13631
13900
|
}
|
|
13632
13901
|
function removeLocalAllow(repo) {
|
|
13633
13902
|
const record2 = readRecord2(repo);
|
|
13634
|
-
const recordPath =
|
|
13903
|
+
const recordPath = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13635
13904
|
if (record2 === null) {
|
|
13636
|
-
if (
|
|
13637
|
-
|
|
13905
|
+
if (existsSync9(recordPath))
|
|
13906
|
+
rmSync5(recordPath, { force: true });
|
|
13638
13907
|
return { kind: "nothing", line: LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE };
|
|
13639
13908
|
}
|
|
13640
|
-
const path =
|
|
13641
|
-
if (
|
|
13909
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13910
|
+
if (existsSync9(path)) {
|
|
13642
13911
|
const text = readFileSync11(path, "utf8");
|
|
13643
13912
|
let parsed = null;
|
|
13644
13913
|
try {
|
|
@@ -13673,13 +13942,13 @@ function removeLocalAllow(repo) {
|
|
|
13673
13942
|
if (record2.before !== null && original !== null && sameJson(next, original)) {
|
|
13674
13943
|
writeFileSync7(path, record2.before);
|
|
13675
13944
|
} else if (record2.before === null && Object.keys(next).length === 0) {
|
|
13676
|
-
|
|
13945
|
+
rmSync5(path, { force: true });
|
|
13677
13946
|
} else {
|
|
13678
13947
|
writeFileSync7(path, render(next, text));
|
|
13679
13948
|
}
|
|
13680
13949
|
}
|
|
13681
13950
|
}
|
|
13682
|
-
|
|
13951
|
+
rmSync5(recordPath, { force: true });
|
|
13683
13952
|
return { kind: "removed", line: LOCAL_ALLOW_REMOVED_LINE };
|
|
13684
13953
|
}
|
|
13685
13954
|
function parseObject(text) {
|
|
@@ -13693,21 +13962,21 @@ function parseObject(text) {
|
|
|
13693
13962
|
}
|
|
13694
13963
|
}
|
|
13695
13964
|
function sameJson(a, b) {
|
|
13696
|
-
return JSON.stringify(
|
|
13965
|
+
return JSON.stringify(canonical2(a)) === JSON.stringify(canonical2(b));
|
|
13697
13966
|
}
|
|
13698
|
-
function
|
|
13967
|
+
function canonical2(value) {
|
|
13699
13968
|
if (Array.isArray(value))
|
|
13700
|
-
return value.map(
|
|
13969
|
+
return value.map(canonical2);
|
|
13701
13970
|
if (isObject2(value)) {
|
|
13702
|
-
return Object.fromEntries(Object.keys(value).sort().map((key) => [key,
|
|
13971
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical2(value[key])]));
|
|
13703
13972
|
}
|
|
13704
13973
|
return value;
|
|
13705
13974
|
}
|
|
13706
13975
|
function shouldAskLocalAllow(repo, interactive) {
|
|
13707
13976
|
if (!interactive || holdsLocalAllowRecord(repo))
|
|
13708
13977
|
return false;
|
|
13709
|
-
const path =
|
|
13710
|
-
if (!
|
|
13978
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13979
|
+
if (!existsSync9(path))
|
|
13711
13980
|
return true;
|
|
13712
13981
|
const parsed = parseObject(readFileSync11(path, "utf8"));
|
|
13713
13982
|
const allow = isObject2(parsed?.["permissions"]) ? parsed["permissions"]["allow"] : void 0;
|
|
@@ -13728,8 +13997,8 @@ function localAllowClosingLine(repo, said) {
|
|
|
13728
13997
|
var ENABLED_SERVERS_KEY = "enabledMcpjsonServers";
|
|
13729
13998
|
var DISABLED_SERVERS_KEY = "disabledMcpjsonServers";
|
|
13730
13999
|
function registersOwnServer(repo) {
|
|
13731
|
-
const path =
|
|
13732
|
-
const registration = parseObject(
|
|
14000
|
+
const path = join13(repo, MCP_REGISTRATION_REL);
|
|
14001
|
+
const registration = parseObject(existsSync9(path) ? readFileSync11(path, "utf8") : null);
|
|
13733
14002
|
const servers = registration?.["mcpServers"];
|
|
13734
14003
|
return isObject2(servers) && isObject2(servers[MCP_SERVER_KEY]);
|
|
13735
14004
|
}
|
|
@@ -13739,15 +14008,15 @@ function stringsIn(value) {
|
|
|
13739
14008
|
function approveOwnServer(repo) {
|
|
13740
14009
|
const plan = planOwnServerApproval(repo, registersOwnServer(repo));
|
|
13741
14010
|
if (plan.after !== null) {
|
|
13742
|
-
const path =
|
|
14011
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13743
14012
|
mkdirSync7(dirname6(path), { recursive: true });
|
|
13744
14013
|
writeFileSync7(path, plan.after);
|
|
13745
14014
|
}
|
|
13746
14015
|
return plan.outcome;
|
|
13747
14016
|
}
|
|
13748
14017
|
function planOwnServerApproval(repo, registered) {
|
|
13749
|
-
const path =
|
|
13750
|
-
const text =
|
|
14018
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
14019
|
+
const text = existsSync9(path) ? readFileSync11(path, "utf8") : null;
|
|
13751
14020
|
const unchanged = (outcome) => ({ outcome, before: text, after: null });
|
|
13752
14021
|
if (!registered)
|
|
13753
14022
|
return unchanged("no-server");
|
|
@@ -13783,8 +14052,8 @@ function planOwnServerApproval(repo, registered) {
|
|
|
13783
14052
|
return { outcome: "approved", before: text, after: after === text ? null : after };
|
|
13784
14053
|
}
|
|
13785
14054
|
function removeOwnServerApproval(repo) {
|
|
13786
|
-
const path =
|
|
13787
|
-
if (!
|
|
14055
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
14056
|
+
if (!existsSync9(path) || trackedByGit(repo))
|
|
13788
14057
|
return false;
|
|
13789
14058
|
const text = readFileSync11(path, "utf8");
|
|
13790
14059
|
const settings = parseObject(text);
|
|
@@ -13818,7 +14087,7 @@ function removeOwnServerApproval(repo) {
|
|
|
13818
14087
|
if (!changed)
|
|
13819
14088
|
return false;
|
|
13820
14089
|
if (Object.keys(next).length === 0)
|
|
13821
|
-
|
|
14090
|
+
rmSync5(path, { force: true });
|
|
13822
14091
|
else
|
|
13823
14092
|
writeFileSync7(path, render(next, text));
|
|
13824
14093
|
return true;
|
|
@@ -13879,8 +14148,8 @@ function ownServerRow(targetRepo, plan) {
|
|
|
13879
14148
|
}
|
|
13880
14149
|
}
|
|
13881
14150
|
function claudeMdLine(targetRepo) {
|
|
13882
|
-
const path =
|
|
13883
|
-
if (!
|
|
14151
|
+
const path = join14(targetRepo, CLAUDE_MD_REL);
|
|
14152
|
+
if (!existsSync10(path))
|
|
13884
14153
|
return void 0;
|
|
13885
14154
|
let text;
|
|
13886
14155
|
try {
|
|
@@ -13891,7 +14160,7 @@ function claudeMdLine(targetRepo) {
|
|
|
13891
14160
|
return text.includes(AGENTS_IMPORT_LINE) ? WORDS.claudeMdAlreadyPoints : WORDS.claudeMdWillAdd;
|
|
13892
14161
|
}
|
|
13893
14162
|
function previewRefusal(targetRepo, options) {
|
|
13894
|
-
if (!
|
|
14163
|
+
if (!existsSync10(targetRepo))
|
|
13895
14164
|
return folderMissingRefusal(targetRepo);
|
|
13896
14165
|
if (!statSync(targetRepo).isDirectory())
|
|
13897
14166
|
return folderIsFileRefusal(targetRepo);
|
|
@@ -13912,7 +14181,7 @@ function previewRefusal(targetRepo, options) {
|
|
|
13912
14181
|
} catch {
|
|
13913
14182
|
return cannotWriteRefusal(targetRepo);
|
|
13914
14183
|
}
|
|
13915
|
-
if (
|
|
14184
|
+
if (existsSync10(join14(targetRepo, PROJECT_IDENTITY_REL)) && readFolderId(targetRepo) === void 0) {
|
|
13916
14185
|
return unreadableProjectRefusal(targetRepo);
|
|
13917
14186
|
}
|
|
13918
14187
|
try {
|
|
@@ -13972,10 +14241,10 @@ function renderPreview(plan, missingGitForWindows, claudeMdWords, ownServer) {
|
|
|
13972
14241
|
}
|
|
13973
14242
|
|
|
13974
14243
|
// dist/doctor.js
|
|
13975
|
-
import { spawnSync as
|
|
13976
|
-
import { existsSync as
|
|
14244
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
14245
|
+
import { existsSync as existsSync11, lstatSync as lstatSync3, readFileSync as readFileSync13, realpathSync as realpathSync5, statSync as statSync2 } from "node:fs";
|
|
13977
14246
|
import { homedir as homedir6 } from "node:os";
|
|
13978
|
-
import { join as
|
|
14247
|
+
import { join as join15 } from "node:path";
|
|
13979
14248
|
var GUARD_RUNNER_REL2 = ".claude/hooks/guard-runner.sh";
|
|
13980
14249
|
var CONNECTION_VERB = "check-connection";
|
|
13981
14250
|
var WRAPPER_TIMEOUT_MS = 3e4;
|
|
@@ -14014,7 +14283,7 @@ function runDoctor(root, options) {
|
|
|
14014
14283
|
};
|
|
14015
14284
|
}
|
|
14016
14285
|
function setUpLine(root, upToDate) {
|
|
14017
|
-
if (!
|
|
14286
|
+
if (!existsSync11(join15(root, BUNDLE_PIN_REL))) {
|
|
14018
14287
|
return {
|
|
14019
14288
|
state: "no",
|
|
14020
14289
|
hooksMissing: false,
|
|
@@ -14028,9 +14297,9 @@ function setUpLine(root, upToDate) {
|
|
|
14028
14297
|
const missing = [];
|
|
14029
14298
|
if (!pinNamesProject(root))
|
|
14030
14299
|
missing.push("Halfcycle's project record");
|
|
14031
|
-
if (!
|
|
14300
|
+
if (!existsSync11(join15(root, INSTALL_MANIFEST_REL)))
|
|
14032
14301
|
missing.push("the install record");
|
|
14033
|
-
if (!
|
|
14302
|
+
if (!existsSync11(join15(root, VENDORED_BIN_REL)))
|
|
14034
14303
|
missing.push("Halfcycle's check program");
|
|
14035
14304
|
if (!mcpNamesServer(root))
|
|
14036
14305
|
missing.push("Halfcycle's server entry");
|
|
@@ -14056,14 +14325,14 @@ function pinNamesProject(root) {
|
|
|
14056
14325
|
}
|
|
14057
14326
|
}
|
|
14058
14327
|
function mcpNamesServer(root) {
|
|
14059
|
-
const parsed = readJson(
|
|
14328
|
+
const parsed = readJson(join15(root, MCP_REGISTRATION_REL));
|
|
14060
14329
|
if (!isObject3(parsed) || !isObject3(parsed["mcpServers"]))
|
|
14061
14330
|
return false;
|
|
14062
14331
|
const entry = parsed["mcpServers"][MCP_SERVER_KEY];
|
|
14063
14332
|
return isObject3(entry) && typeof entry["url"] === "string";
|
|
14064
14333
|
}
|
|
14065
14334
|
function settingsRegisterEveryCommand(root) {
|
|
14066
|
-
const parsed = readJson(
|
|
14335
|
+
const parsed = readJson(join15(root, SETTINGS_REL));
|
|
14067
14336
|
if (!isObject3(parsed) || !isObject3(parsed["hooks"]))
|
|
14068
14337
|
return false;
|
|
14069
14338
|
const registered = /* @__PURE__ */ new Set();
|
|
@@ -14094,10 +14363,10 @@ function startWrapper(root, options) {
|
|
|
14094
14363
|
}
|
|
14095
14364
|
shell = gitBash;
|
|
14096
14365
|
}
|
|
14097
|
-
const wrapper =
|
|
14098
|
-
if (!
|
|
14366
|
+
const wrapper = join15(root, GUARD_RUNNER_REL2);
|
|
14367
|
+
if (!existsSync11(wrapper))
|
|
14099
14368
|
return failed(repairSentence());
|
|
14100
|
-
const run =
|
|
14369
|
+
const run = spawnSync3(shell, [wrapper, CONNECTION_VERB], {
|
|
14101
14370
|
cwd: root,
|
|
14102
14371
|
env: { ...options.env, CLAUDE_PROJECT_DIR: root },
|
|
14103
14372
|
encoding: "utf-8",
|
|
@@ -14122,7 +14391,7 @@ function startWrapper(root, options) {
|
|
|
14122
14391
|
}
|
|
14123
14392
|
function settingsDisablingHooks(root) {
|
|
14124
14393
|
for (const rel of [SETTINGS_REL, LOCAL_SETTINGS_REL]) {
|
|
14125
|
-
const parsed = readJson(
|
|
14394
|
+
const parsed = readJson(join15(root, rel));
|
|
14126
14395
|
if (isObject3(parsed) && parsed["disableAllHooks"] === true)
|
|
14127
14396
|
return rel;
|
|
14128
14397
|
}
|
|
@@ -14212,8 +14481,8 @@ function folderTrustLine(root, options) {
|
|
|
14212
14481
|
if (!mcpNamesServer(root))
|
|
14213
14482
|
return void 0;
|
|
14214
14483
|
const configFolder = options.env["CLAUDE_CONFIG_DIR"];
|
|
14215
|
-
const stateFile =
|
|
14216
|
-
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync:
|
|
14484
|
+
const stateFile = join15(configFolder !== void 0 && configFolder !== "" ? configFolder : options.home ?? homedir6(), CLAUDE_STATE_FILENAME);
|
|
14485
|
+
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync3, readFileSync: readFileSync13, realpathSync: realpathSync5 });
|
|
14217
14486
|
return trust === "not-trusted" ? { ok: true, text: `Folder trust: ${FOLDER_NOT_TRUSTED_SENTENCE}` } : void 0;
|
|
14218
14487
|
}
|
|
14219
14488
|
function signInCodeLine(root, running, now, options) {
|
|
@@ -14287,7 +14556,7 @@ var CODEX_EVENT_SNAKE = {
|
|
|
14287
14556
|
};
|
|
14288
14557
|
function codexHomeDir(options) {
|
|
14289
14558
|
const codexHome = options.env["CODEX_HOME"];
|
|
14290
|
-
return codexHome !== void 0 && codexHome !== "" ? codexHome :
|
|
14559
|
+
return codexHome !== void 0 && codexHome !== "" ? codexHome : join15(homedir6(), ".codex");
|
|
14291
14560
|
}
|
|
14292
14561
|
function isDirectory(path) {
|
|
14293
14562
|
try {
|
|
@@ -14297,8 +14566,8 @@ function isDirectory(path) {
|
|
|
14297
14566
|
}
|
|
14298
14567
|
}
|
|
14299
14568
|
function codexLine(root, options) {
|
|
14300
|
-
const hooksPath =
|
|
14301
|
-
if (!
|
|
14569
|
+
const hooksPath = join15(root, CODEX_HOOKS_REL2);
|
|
14570
|
+
if (!existsSync11(hooksPath))
|
|
14302
14571
|
return void 0;
|
|
14303
14572
|
if (!isDirectory(codexHomeDir(options)))
|
|
14304
14573
|
return void 0;
|
|
@@ -14340,7 +14609,7 @@ function codexEntryPositions(hooksPath) {
|
|
|
14340
14609
|
return positions;
|
|
14341
14610
|
}
|
|
14342
14611
|
function codexApprovalStatus(root, positions, options) {
|
|
14343
|
-
const configPath =
|
|
14612
|
+
const configPath = join15(codexHomeDir(options), "config.toml");
|
|
14344
14613
|
let text;
|
|
14345
14614
|
try {
|
|
14346
14615
|
text = readFileSync13(configPath, "utf-8");
|
|
@@ -14349,7 +14618,7 @@ function codexApprovalStatus(root, positions, options) {
|
|
|
14349
14618
|
}
|
|
14350
14619
|
let hooksPath;
|
|
14351
14620
|
try {
|
|
14352
|
-
hooksPath =
|
|
14621
|
+
hooksPath = join15(realpathSync5(root), CODEX_HOOKS_REL2);
|
|
14353
14622
|
} catch {
|
|
14354
14623
|
return "not-approved";
|
|
14355
14624
|
}
|
|
@@ -14379,7 +14648,7 @@ function naturalList(items) {
|
|
|
14379
14648
|
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
14380
14649
|
}
|
|
14381
14650
|
function readJson(path) {
|
|
14382
|
-
if (!
|
|
14651
|
+
if (!existsSync11(path))
|
|
14383
14652
|
return void 0;
|
|
14384
14653
|
try {
|
|
14385
14654
|
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
@@ -14393,8 +14662,8 @@ function isObject3(value) {
|
|
|
14393
14662
|
|
|
14394
14663
|
// dist/claude-version-probe.js
|
|
14395
14664
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
14396
|
-
import { existsSync as
|
|
14397
|
-
import { delimiter as delimiter2, join as
|
|
14665
|
+
import { existsSync as existsSync12, realpathSync as realpathSync6 } from "node:fs";
|
|
14666
|
+
import { delimiter as delimiter2, join as join16 } from "node:path";
|
|
14398
14667
|
function execVersionCommand(command, timeoutMs, deps) {
|
|
14399
14668
|
const platform6 = deps.platform ?? process.platform;
|
|
14400
14669
|
const exec = deps.exec ?? execFileSync4;
|
|
@@ -14437,10 +14706,10 @@ var INSTALL_METHOD_RULES = [
|
|
|
14437
14706
|
function probeClaudeCodeInstallMethod(deps = {}) {
|
|
14438
14707
|
const env = deps.env ?? process.env;
|
|
14439
14708
|
const platform6 = deps.platform ?? process.platform;
|
|
14440
|
-
const exists = deps.exists ??
|
|
14709
|
+
const exists = deps.exists ?? existsSync12;
|
|
14441
14710
|
const realpath = deps.realpath ?? ((file) => {
|
|
14442
14711
|
try {
|
|
14443
|
-
return
|
|
14712
|
+
return realpathSync6(file);
|
|
14444
14713
|
} catch {
|
|
14445
14714
|
return file;
|
|
14446
14715
|
}
|
|
@@ -14449,7 +14718,7 @@ function probeClaudeCodeInstallMethod(deps = {}) {
|
|
|
14449
14718
|
const dirs = (env["PATH"] ?? "").split(delimiter2).filter((dir) => dir !== "");
|
|
14450
14719
|
for (const dir of dirs) {
|
|
14451
14720
|
for (const name of names) {
|
|
14452
|
-
const file =
|
|
14721
|
+
const file = join16(dir, name);
|
|
14453
14722
|
if (!exists(file))
|
|
14454
14723
|
continue;
|
|
14455
14724
|
const real = realpath(file);
|
|
@@ -14484,8 +14753,8 @@ function codexInstallLine(codex) {
|
|
|
14484
14753
|
}
|
|
14485
14754
|
|
|
14486
14755
|
// dist/build-record/write.js
|
|
14487
|
-
import { existsSync as
|
|
14488
|
-
import { basename as basename2, join as
|
|
14756
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, rmSync as rmSync7 } from "node:fs";
|
|
14757
|
+
import { basename as basename2, join as join17, resolve as resolve4, relative as relative3, isAbsolute as isAbsolute4 } from "node:path";
|
|
14489
14758
|
|
|
14490
14759
|
// dist/build-record/render-v2.js
|
|
14491
14760
|
var HIDDEN = /[\p{Cc}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/u;
|
|
@@ -14831,6 +15100,20 @@ function leftForYouLines(left, p) {
|
|
|
14831
15100
|
}
|
|
14832
15101
|
return lines;
|
|
14833
15102
|
}
|
|
15103
|
+
var TASKS_FILE_LINK = "../tasks.md";
|
|
15104
|
+
function ticketLines(tickets, p) {
|
|
15105
|
+
if (tickets === void 0)
|
|
15106
|
+
return [];
|
|
15107
|
+
const lines = ["- Tickets:"];
|
|
15108
|
+
for (const entry of tickets.entries) {
|
|
15109
|
+
lines.push(` - ${entry.ref} ${p.text(entry.title)}`);
|
|
15110
|
+
if (entry.url !== null)
|
|
15111
|
+
lines.push(` - Link: [${entry.ref}](${entry.url})`);
|
|
15112
|
+
else if (entry.ref === TASKS_FILE_PATH)
|
|
15113
|
+
lines.push(` - Link: [${entry.ref}](${TASKS_FILE_LINK})`);
|
|
15114
|
+
}
|
|
15115
|
+
return lines;
|
|
15116
|
+
}
|
|
14834
15117
|
function shownBy(steps) {
|
|
14835
15118
|
const named2 = steps.filter((s) => s.name !== void 0).length;
|
|
14836
15119
|
if (named2 === 0)
|
|
@@ -14904,6 +15187,7 @@ function renderClose(heading, observed, attested, checks, p, closingRound = fals
|
|
|
14904
15187
|
`- Signed in as: ${signedInAs(observed.closedBy, p)}`,
|
|
14905
15188
|
...outstanding(observed.crossed, p),
|
|
14906
15189
|
...leftForYouLines(observed.leftForOwner, p),
|
|
15190
|
+
...ticketLines(observed.tickets, p),
|
|
14907
15191
|
machines(observed.seatCoverage, checks)
|
|
14908
15192
|
].join("\n"),
|
|
14909
15193
|
STATED_LABEL,
|
|
@@ -15076,7 +15360,7 @@ function renderBuildRecordV2Markdown(record2) {
|
|
|
15076
15360
|
}
|
|
15077
15361
|
|
|
15078
15362
|
// dist/build-record/pair-io.js
|
|
15079
|
-
import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as
|
|
15363
|
+
import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync6, writeSync as writeSync2 } from "node:fs";
|
|
15080
15364
|
function createExclusiveOnDisk(path, data) {
|
|
15081
15365
|
const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
15082
15366
|
const fd = openSync(path, "wx");
|
|
@@ -15087,7 +15371,7 @@ function createExclusiveOnDisk(path, data) {
|
|
|
15087
15371
|
}
|
|
15088
15372
|
} catch (err) {
|
|
15089
15373
|
closeSync(fd);
|
|
15090
|
-
|
|
15374
|
+
rmSync6(path, { force: true });
|
|
15091
15375
|
throw err;
|
|
15092
15376
|
}
|
|
15093
15377
|
closeSync(fd);
|
|
@@ -15121,28 +15405,28 @@ function createPairExclusive(io, jsonPath, jsonText, mdPath, mdText) {
|
|
|
15121
15405
|
return "created";
|
|
15122
15406
|
} catch (err) {
|
|
15123
15407
|
if (createdJson)
|
|
15124
|
-
|
|
15408
|
+
rmSync6(jsonPath, { force: true });
|
|
15125
15409
|
if (!isEexist(err))
|
|
15126
15410
|
throw err;
|
|
15127
15411
|
return "exists";
|
|
15128
15412
|
}
|
|
15129
15413
|
}
|
|
15130
15414
|
function replaceOne(io, tmp, finalPath, text) {
|
|
15131
|
-
|
|
15415
|
+
rmSync6(tmp, { force: true });
|
|
15132
15416
|
try {
|
|
15133
15417
|
io.createExclusive(tmp, text);
|
|
15134
15418
|
io.rename(tmp, finalPath);
|
|
15135
15419
|
} catch (err) {
|
|
15136
|
-
|
|
15420
|
+
rmSync6(tmp, { force: true });
|
|
15137
15421
|
throw err;
|
|
15138
15422
|
}
|
|
15139
15423
|
}
|
|
15140
15424
|
|
|
15141
15425
|
// dist/build-record/write.js
|
|
15142
|
-
var BUILD_RECORD_DIR =
|
|
15426
|
+
var BUILD_RECORD_DIR = join17("docs", "build-records");
|
|
15143
15427
|
function assertInsideOutDir(outDir, candidate, identity) {
|
|
15144
|
-
const rel =
|
|
15145
|
-
const isPlainFilename = rel !== "" && rel !== ".." && !
|
|
15428
|
+
const rel = relative3(outDir, resolve4(candidate));
|
|
15429
|
+
const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute4(rel) && rel === basename2(rel);
|
|
15146
15430
|
if (!isPlainFilename) {
|
|
15147
15431
|
throw new InvalidPhaseIdentityError(identity, BUILD_RECORD_DIR);
|
|
15148
15432
|
}
|
|
@@ -15175,10 +15459,10 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15175
15459
|
if (!isValidPhaseIdentity(phase)) {
|
|
15176
15460
|
throw new InvalidPhaseIdentityError(phase, BUILD_RECORD_DIR);
|
|
15177
15461
|
}
|
|
15178
|
-
const outDir =
|
|
15462
|
+
const outDir = resolve4(repoRoot, BUILD_RECORD_DIR);
|
|
15179
15463
|
const segment = renderPhaseSegment(phase);
|
|
15180
|
-
const jsonPath =
|
|
15181
|
-
const mdPath =
|
|
15464
|
+
const jsonPath = join17(outDir, `${segment}.json`);
|
|
15465
|
+
const mdPath = join17(outDir, `${segment}.md`);
|
|
15182
15466
|
const jsonTmp = `${jsonPath}.tmp`;
|
|
15183
15467
|
const mdTmp = `${mdPath}.tmp`;
|
|
15184
15468
|
for (const candidate of [jsonPath, mdPath, jsonTmp, mdTmp]) {
|
|
@@ -15192,7 +15476,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15192
15476
|
}
|
|
15193
15477
|
const existingJson = readIfPresent(jsonPath);
|
|
15194
15478
|
if (existingJson === null) {
|
|
15195
|
-
return { kind: "in-the-way", path:
|
|
15479
|
+
return { kind: "in-the-way", path: existsSync13(jsonPath) ? jsonPath : mdPath };
|
|
15196
15480
|
}
|
|
15197
15481
|
if (existingJson === jsonText) {
|
|
15198
15482
|
replaceOne(io, mdTmp, mdPath, mdText);
|
|
@@ -15208,7 +15492,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15208
15492
|
try {
|
|
15209
15493
|
replaceOne(io, jsonTmp, jsonPath, existingJson);
|
|
15210
15494
|
} catch {
|
|
15211
|
-
|
|
15495
|
+
rmSync7(jsonPath, { force: true });
|
|
15212
15496
|
}
|
|
15213
15497
|
throw err;
|
|
15214
15498
|
}
|
|
@@ -15216,24 +15500,24 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15216
15500
|
}
|
|
15217
15501
|
|
|
15218
15502
|
// dist/build-record/obtain.js
|
|
15219
|
-
import { relative as
|
|
15503
|
+
import { relative as relative6 } from "node:path";
|
|
15220
15504
|
|
|
15221
15505
|
// dist/open-phase.js
|
|
15222
15506
|
import { readFileSync as readFileSync20 } from "node:fs";
|
|
15223
|
-
import { join as
|
|
15507
|
+
import { join as join25 } from "node:path";
|
|
15224
15508
|
|
|
15225
15509
|
// dist/close-phase.js
|
|
15226
15510
|
import { execFile, execFileSync as execFileSync6 } from "node:child_process";
|
|
15227
15511
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
15228
|
-
import { join as
|
|
15512
|
+
import { join as join24 } from "node:path";
|
|
15229
15513
|
|
|
15230
15514
|
// dist/plan.js
|
|
15231
|
-
import { existsSync as
|
|
15232
|
-
import { join as
|
|
15515
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18 } from "node:fs";
|
|
15516
|
+
import { join as join23 } from "node:path";
|
|
15233
15517
|
|
|
15234
15518
|
// dist/draw/write.js
|
|
15235
|
-
import { closeSync as closeSync2, lstatSync as
|
|
15236
|
-
import { dirname as dirname7, isAbsolute as
|
|
15519
|
+
import { closeSync as closeSync2, lstatSync as lstatSync4, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync15, readSync, realpathSync as realpathSync7, rmSync as rmSync8 } from "node:fs";
|
|
15520
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join18, relative as relative4, resolve as resolve5, sep as sep2 } from "node:path";
|
|
15237
15521
|
var DOCS_DIR = "docs";
|
|
15238
15522
|
var DIAGRAMS_DIR = "diagrams";
|
|
15239
15523
|
var DIAGRAM_DIR_REL = `${DOCS_DIR}/${DIAGRAMS_DIR}`;
|
|
@@ -15288,10 +15572,10 @@ function embedImageLink(base, doc) {
|
|
|
15288
15572
|
return `${up}${DIAGRAMS_DIR}/${markdownLinkName(embedImageName(base))}`;
|
|
15289
15573
|
}
|
|
15290
15574
|
function diagramPaths(repoRoot, base) {
|
|
15291
|
-
const root =
|
|
15292
|
-
const dir =
|
|
15575
|
+
const root = resolve5(repoRoot);
|
|
15576
|
+
const dir = join18(root, DOCS_DIR, DIAGRAMS_DIR);
|
|
15293
15577
|
const names = fileNames(base);
|
|
15294
|
-
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which,
|
|
15578
|
+
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which, join18(dir, names[which])]));
|
|
15295
15579
|
return { repoRoot: root, dir, file };
|
|
15296
15580
|
}
|
|
15297
15581
|
function diagramRel(base, which) {
|
|
@@ -15308,19 +15592,19 @@ function spokenList(items) {
|
|
|
15308
15592
|
}
|
|
15309
15593
|
function lstatOrNull2(path) {
|
|
15310
15594
|
try {
|
|
15311
|
-
return
|
|
15595
|
+
return lstatSync4(path);
|
|
15312
15596
|
} catch {
|
|
15313
15597
|
return null;
|
|
15314
15598
|
}
|
|
15315
15599
|
}
|
|
15316
|
-
function
|
|
15317
|
-
const rel =
|
|
15318
|
-
return rel === "" || !
|
|
15600
|
+
function isInside2(outer, inner) {
|
|
15601
|
+
const rel = relative4(outer, inner);
|
|
15602
|
+
return rel === "" || !isAbsolute5(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
|
|
15319
15603
|
}
|
|
15320
15604
|
function checkDiagramPlace(paths) {
|
|
15321
15605
|
let root;
|
|
15322
15606
|
try {
|
|
15323
|
-
root =
|
|
15607
|
+
root = realpathSync7(paths.repoRoot);
|
|
15324
15608
|
} catch {
|
|
15325
15609
|
return { ok: false, why: `the folder ${paths.repoRoot} could not be opened. Check the path, then run this again.` };
|
|
15326
15610
|
}
|
|
@@ -15333,14 +15617,14 @@ function checkDiagramPlace(paths) {
|
|
|
15333
15617
|
}
|
|
15334
15618
|
let real;
|
|
15335
15619
|
try {
|
|
15336
|
-
real =
|
|
15620
|
+
real = realpathSync7(ancestor);
|
|
15337
15621
|
} catch {
|
|
15338
15622
|
return {
|
|
15339
15623
|
ok: false,
|
|
15340
15624
|
why: `${DIAGRAM_DIR_REL} leads to a place that does not exist. Remove the link in the way, then run this again.`
|
|
15341
15625
|
};
|
|
15342
15626
|
}
|
|
15343
|
-
if (!
|
|
15627
|
+
if (!isInside2(root, real)) {
|
|
15344
15628
|
return {
|
|
15345
15629
|
ok: false,
|
|
15346
15630
|
why: `${DIAGRAM_DIR_REL} leads outside this repository, so nothing was written. Remove the link in the way, then run this again.`
|
|
@@ -15348,12 +15632,12 @@ function checkDiagramPlace(paths) {
|
|
|
15348
15632
|
}
|
|
15349
15633
|
let isFolder = false;
|
|
15350
15634
|
try {
|
|
15351
|
-
isFolder =
|
|
15635
|
+
isFolder = lstatSync4(real).isDirectory();
|
|
15352
15636
|
} catch {
|
|
15353
15637
|
isFolder = false;
|
|
15354
15638
|
}
|
|
15355
15639
|
if (!isFolder) {
|
|
15356
|
-
const name =
|
|
15640
|
+
const name = relative4(root, real).split("\\").join("/");
|
|
15357
15641
|
return {
|
|
15358
15642
|
ok: false,
|
|
15359
15643
|
why: `${name === "" ? "this repository" : name} is not a folder, so ${DIAGRAM_DIR_REL} cannot be made. Move it, then run this again.`
|
|
@@ -15468,7 +15752,7 @@ function checkPictures(drawn, base) {
|
|
|
15468
15752
|
}
|
|
15469
15753
|
function removeQuietly(path) {
|
|
15470
15754
|
try {
|
|
15471
|
-
|
|
15755
|
+
rmSync8(path, { force: true });
|
|
15472
15756
|
} catch {
|
|
15473
15757
|
return;
|
|
15474
15758
|
}
|
|
@@ -15534,10 +15818,10 @@ function restore(io, paths, renamed, existing) {
|
|
|
15534
15818
|
const tmp = tempPath(paths, which);
|
|
15535
15819
|
try {
|
|
15536
15820
|
if (previous === null) {
|
|
15537
|
-
|
|
15821
|
+
rmSync8(finalPath, { force: true });
|
|
15538
15822
|
continue;
|
|
15539
15823
|
}
|
|
15540
|
-
|
|
15824
|
+
rmSync8(tmp, { force: true });
|
|
15541
15825
|
io.createExclusive(tmp, previous);
|
|
15542
15826
|
io.rename(tmp, finalPath);
|
|
15543
15827
|
} catch {
|
|
@@ -15564,7 +15848,7 @@ function isMissingRoute(method, status, body) {
|
|
|
15564
15848
|
}
|
|
15565
15849
|
|
|
15566
15850
|
// dist/change.js
|
|
15567
|
-
import { join as
|
|
15851
|
+
import { join as join21 } from "node:path";
|
|
15568
15852
|
|
|
15569
15853
|
// dist/cli-contract.js
|
|
15570
15854
|
var CLI_VERBS = ["install", "check-drift", "build-record", "open-phase", "close-phase", "system", "draw", "plan", "review", "check-tasks", "check-criteria", "check-report", "ready", "uninstall", "change", "pace", "route", "prove", "preview", "doctor", "restore", "request-shapes", "allow", "size", "regression", "crew"];
|
|
@@ -15809,11 +16093,11 @@ function placeholderRefusal(verb, tokens) {
|
|
|
15809
16093
|
// dist/system-report.js
|
|
15810
16094
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
15811
16095
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
15812
|
-
import { join as
|
|
16096
|
+
import { join as join19 } from "node:path";
|
|
15813
16097
|
function readDescriptionFile(repoRoot) {
|
|
15814
16098
|
let text;
|
|
15815
16099
|
try {
|
|
15816
|
-
text = readFileSync16(
|
|
16100
|
+
text = readFileSync16(join19(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8");
|
|
15817
16101
|
} catch (err) {
|
|
15818
16102
|
const code = err.code;
|
|
15819
16103
|
return code === "ENOENT" || code === "ENOTDIR" ? { kind: "missing" } : { kind: "unreadable" };
|
|
@@ -16037,8 +16321,8 @@ async function runSystem(repoRoot, deps) {
|
|
|
16037
16321
|
}
|
|
16038
16322
|
|
|
16039
16323
|
// dist/build-record/write-change.js
|
|
16040
|
-
import { existsSync as
|
|
16041
|
-
import { basename as basename3, isAbsolute as
|
|
16324
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10 } from "node:fs";
|
|
16325
|
+
import { basename as basename3, isAbsolute as isAbsolute6, join as join20, relative as relative5, resolve as resolve6 } from "node:path";
|
|
16042
16326
|
|
|
16043
16327
|
// dist/build-record/render-change.js
|
|
16044
16328
|
function counted2(n, one, many) {
|
|
@@ -16183,7 +16467,7 @@ function renderChangeRecordMarkdown(record2) {
|
|
|
16183
16467
|
}
|
|
16184
16468
|
|
|
16185
16469
|
// dist/build-record/write-change.js
|
|
16186
|
-
var CHANGE_RECORD_DIR =
|
|
16470
|
+
var CHANGE_RECORD_DIR = join20("docs", "build-records", "changes");
|
|
16187
16471
|
var InvalidChangeIdError = class extends Error {
|
|
16188
16472
|
id;
|
|
16189
16473
|
constructor(id) {
|
|
@@ -16193,8 +16477,8 @@ var InvalidChangeIdError = class extends Error {
|
|
|
16193
16477
|
}
|
|
16194
16478
|
};
|
|
16195
16479
|
function assertInsideChangeDir(outDir, candidate, id) {
|
|
16196
|
-
const rel =
|
|
16197
|
-
const isPlainFilename = rel !== "" && rel !== ".." && !
|
|
16480
|
+
const rel = relative5(outDir, resolve6(candidate));
|
|
16481
|
+
const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute6(rel) && rel === basename3(rel);
|
|
16198
16482
|
if (!isPlainFilename)
|
|
16199
16483
|
throw new InvalidChangeIdError(id);
|
|
16200
16484
|
}
|
|
@@ -16202,9 +16486,9 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
16202
16486
|
const checked = sortingIdSchema.safeParse(sortingId);
|
|
16203
16487
|
if (!checked.success)
|
|
16204
16488
|
throw new InvalidChangeIdError(sortingId);
|
|
16205
|
-
const outDir =
|
|
16206
|
-
const jsonPath =
|
|
16207
|
-
const mdPath =
|
|
16489
|
+
const outDir = resolve6(repoRoot, CHANGE_RECORD_DIR);
|
|
16490
|
+
const jsonPath = join20(outDir, `${checked.data}.json`);
|
|
16491
|
+
const mdPath = join20(outDir, `${checked.data}.md`);
|
|
16208
16492
|
const mdTmp = `${mdPath}.tmp`;
|
|
16209
16493
|
for (const candidate of [jsonPath, mdPath, mdTmp]) {
|
|
16210
16494
|
assertInsideChangeDir(outDir, candidate, sortingId);
|
|
@@ -16217,7 +16501,7 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
16217
16501
|
}
|
|
16218
16502
|
const existingJson = readIfPresent(jsonPath);
|
|
16219
16503
|
if (existingJson === null) {
|
|
16220
|
-
return { kind: "in-the-way", path:
|
|
16504
|
+
return { kind: "in-the-way", path: existsSync14(jsonPath) ? jsonPath : mdPath };
|
|
16221
16505
|
}
|
|
16222
16506
|
if (existingJson !== jsonText)
|
|
16223
16507
|
return { kind: "in-the-way", path: jsonPath };
|
|
@@ -16480,7 +16764,7 @@ function writePairLines(deps, repoRoot, sortingId, found) {
|
|
|
16480
16764
|
};
|
|
16481
16765
|
}
|
|
16482
16766
|
return {
|
|
16483
|
-
stdout: `[halfcycle] Its record is in ${
|
|
16767
|
+
stdout: `[halfcycle] Its record is in ${join21(CHANGE_RECORD_DIR, `${sortingId}.md`)}.
|
|
16484
16768
|
`,
|
|
16485
16769
|
stderr: "",
|
|
16486
16770
|
ok: true
|
|
@@ -16721,12 +17005,12 @@ function runChange(repoRoot, args2, deps) {
|
|
|
16721
17005
|
// dist/shown-record.js
|
|
16722
17006
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16723
17007
|
import { platform as platform4 } from "node:os";
|
|
16724
|
-
import { join as
|
|
17008
|
+
import { join as join22 } from "node:path";
|
|
16725
17009
|
function updateShownRecord(engagementId, update, home) {
|
|
16726
17010
|
let temp = null;
|
|
16727
17011
|
try {
|
|
16728
17012
|
const dir = engagementStateDir(engagementId, home);
|
|
16729
|
-
const path =
|
|
17013
|
+
const path = join22(dir, SHOWN_PLANS_FILENAME);
|
|
16730
17014
|
let current;
|
|
16731
17015
|
try {
|
|
16732
17016
|
current = parseShownRecord(readFileSync17(path, "utf-8"));
|
|
@@ -16736,7 +17020,7 @@ function updateShownRecord(engagementId, update, home) {
|
|
|
16736
17020
|
const next = update(current);
|
|
16737
17021
|
const body = serialiseShownRecord(next);
|
|
16738
17022
|
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
16739
|
-
temp =
|
|
17023
|
+
temp = join22(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
|
|
16740
17024
|
writeFileSync8(temp, body, { mode: 384 });
|
|
16741
17025
|
if (platform4() !== "win32")
|
|
16742
17026
|
chmodSync3(temp, 384);
|
|
@@ -16813,7 +17097,7 @@ function readAndCheckPlan(repoRoot, phase, description, verb) {
|
|
|
16813
17097
|
const fileRel = planFileRel(phase);
|
|
16814
17098
|
let text;
|
|
16815
17099
|
try {
|
|
16816
|
-
text = readFileSync18(
|
|
17100
|
+
text = readFileSync18(join23(repoRoot, fileRel), "utf-8");
|
|
16817
17101
|
} catch (err) {
|
|
16818
17102
|
const code = err.code;
|
|
16819
17103
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -16969,7 +17253,7 @@ function removalOwedLines(verb, parts) {
|
|
|
16969
17253
|
function redrawLine(repoRoot, phase) {
|
|
16970
17254
|
let exists;
|
|
16971
17255
|
try {
|
|
16972
|
-
exists =
|
|
17256
|
+
exists = existsSync15(join23(repoRoot, DIAGRAM_DIR_REL, embedImageName(phaseDiagramBase(phase))));
|
|
16973
17257
|
} catch {
|
|
16974
17258
|
return null;
|
|
16975
17259
|
}
|
|
@@ -17553,7 +17837,7 @@ function readObserveLine(stdout) {
|
|
|
17553
17837
|
function runRunnerChild(binary, args2, credential, repoRoot, readLine, fallback) {
|
|
17554
17838
|
if (binary instanceof Error)
|
|
17555
17839
|
return Promise.resolve(fallback);
|
|
17556
|
-
return new Promise((
|
|
17840
|
+
return new Promise((resolve10) => {
|
|
17557
17841
|
try {
|
|
17558
17842
|
execFile(process.execPath, [binary, ...args2], {
|
|
17559
17843
|
cwd: repoRoot,
|
|
@@ -17563,10 +17847,10 @@ function runRunnerChild(binary, args2, credential, repoRoot, readLine, fallback)
|
|
|
17563
17847
|
windowsHide: true
|
|
17564
17848
|
}, (err, stdout) => {
|
|
17565
17849
|
const stopped = err !== null && err.killed === true;
|
|
17566
|
-
|
|
17850
|
+
resolve10(stopped || typeof stdout !== "string" ? fallback : readLine(stdout));
|
|
17567
17851
|
});
|
|
17568
17852
|
} catch {
|
|
17569
|
-
|
|
17853
|
+
resolve10(fallback);
|
|
17570
17854
|
}
|
|
17571
17855
|
});
|
|
17572
17856
|
}
|
|
@@ -17929,7 +18213,7 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
|
|
|
17929
18213
|
var PACING_FOLDER = "pacing";
|
|
17930
18214
|
function pacingFile(engagementId, name, home) {
|
|
17931
18215
|
try {
|
|
17932
|
-
return
|
|
18216
|
+
return join24(engagementStateDir(engagementId, home), PACING_FOLDER, name);
|
|
17933
18217
|
} catch {
|
|
17934
18218
|
return null;
|
|
17935
18219
|
}
|
|
@@ -18101,7 +18385,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
18101
18385
|
if (pinned !== null)
|
|
18102
18386
|
assertEngagementId(pinned);
|
|
18103
18387
|
const fromStore = pinned === null ? null : readEngagementEnv(pinned, home);
|
|
18104
|
-
const looked = pinned === null ? `${
|
|
18388
|
+
const looked = pinned === null ? `${join25(repoRoot, ".halfcycle", "bundle.json")} (which names no engagement)` : engagementEnvPath(pinned, home);
|
|
18105
18389
|
const values = {};
|
|
18106
18390
|
for (const key of OPEN_PHASE_ENV_KEYS) {
|
|
18107
18391
|
const value = (env[key] ?? fromStore?.[key] ?? "").trim();
|
|
@@ -18118,7 +18402,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
18118
18402
|
}
|
|
18119
18403
|
function readPinnedEngagementId(repoRoot) {
|
|
18120
18404
|
try {
|
|
18121
|
-
const pin = JSON.parse(readFileSync20(
|
|
18405
|
+
const pin = JSON.parse(readFileSync20(join25(repoRoot, ".halfcycle", "bundle.json"), "utf-8"));
|
|
18122
18406
|
const id = pin[PIN_ENGAGEMENT_ID_FIELD];
|
|
18123
18407
|
return typeof id === "string" && id !== "" ? id : null;
|
|
18124
18408
|
} catch {
|
|
@@ -18268,12 +18552,12 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
|
|
|
18268
18552
|
};
|
|
18269
18553
|
}
|
|
18270
18554
|
if (result.kind === "in-the-way") {
|
|
18271
|
-
return { kind: "exists", path:
|
|
18555
|
+
return { kind: "exists", path: relative6(repoRoot, result.path) };
|
|
18272
18556
|
}
|
|
18273
18557
|
return {
|
|
18274
18558
|
kind: "written",
|
|
18275
|
-
jsonPath:
|
|
18276
|
-
mdPath:
|
|
18559
|
+
jsonPath: relative6(repoRoot, result.jsonPath),
|
|
18560
|
+
mdPath: relative6(repoRoot, result.mdPath),
|
|
18277
18561
|
headline: latestCloseHeadline(answer.record),
|
|
18278
18562
|
stampFailure: clearStampFor(credential.engagementId, phase, home)
|
|
18279
18563
|
};
|
|
@@ -18606,7 +18890,7 @@ async function writeDrawOutcome(repoRoot, deps, outcome, target, warnings, canno
|
|
|
18606
18890
|
|
|
18607
18891
|
// dist/check-tasks.js
|
|
18608
18892
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
18609
|
-
import { join as
|
|
18893
|
+
import { join as join26, resolve as resolve7 } from "node:path";
|
|
18610
18894
|
var CHECK_TASKS_USAGE = `${ownCommand()} check-tasks <phase> [--with-task-list] [--repo <root>]`;
|
|
18611
18895
|
function parseCheckTasksArgs(argv) {
|
|
18612
18896
|
const positionals = [];
|
|
@@ -18647,7 +18931,7 @@ function issuePlace(rel, path) {
|
|
|
18647
18931
|
function readJsonFile(repoRoot, rel, parse) {
|
|
18648
18932
|
let text;
|
|
18649
18933
|
try {
|
|
18650
|
-
text = readFileSync21(
|
|
18934
|
+
text = readFileSync21(resolve7(repoRoot, rel), "utf-8");
|
|
18651
18935
|
} catch (err) {
|
|
18652
18936
|
const code = err.code;
|
|
18653
18937
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18705,7 +18989,7 @@ function readPhasePlanParts(repoRoot, phase, description) {
|
|
|
18705
18989
|
const file = planFileRel(phase);
|
|
18706
18990
|
let text;
|
|
18707
18991
|
try {
|
|
18708
|
-
text = readFileSync21(
|
|
18992
|
+
text = readFileSync21(join26(repoRoot, file), "utf-8");
|
|
18709
18993
|
} catch (err) {
|
|
18710
18994
|
const code = err.code;
|
|
18711
18995
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18738,7 +19022,7 @@ function readPlanSpecFiles(repoRoot, plan) {
|
|
|
18738
19022
|
const rel = `${SPEC_FOLDER}/${slug}.md`;
|
|
18739
19023
|
let text;
|
|
18740
19024
|
try {
|
|
18741
|
-
text = readFileSync21(
|
|
19025
|
+
text = readFileSync21(join26(repoRoot, rel), "utf-8");
|
|
18742
19026
|
} catch (err) {
|
|
18743
19027
|
const code = err.code;
|
|
18744
19028
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -18994,8 +19278,8 @@ function runCheckReport(source, head, deps = {}) {
|
|
|
18994
19278
|
|
|
18995
19279
|
// dist/setup-committed.js
|
|
18996
19280
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
18997
|
-
import { existsSync as
|
|
18998
|
-
import { join as
|
|
19281
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
19282
|
+
import { join as join27 } from "node:path";
|
|
18999
19283
|
var SETUP_COMMIT_MESSAGE = "Add Halfcycle setup files";
|
|
19000
19284
|
var SETUP_UNCOMMITTED_LINES = {
|
|
19001
19285
|
intro: () => "Halfcycle's setup files are not committed, so the copies your agents build in would not have Halfcycle. Nothing was started. Commit them, then run this again:",
|
|
@@ -19006,7 +19290,7 @@ function shellWord(path) {
|
|
|
19006
19290
|
return /^[A-Za-z0-9._/@+=-]+$/.test(path) ? path : `'${path.replace(/'/g, `'\\''`)}'`;
|
|
19007
19291
|
}
|
|
19008
19292
|
function uncommittedSetupFiles(repoRoot) {
|
|
19009
|
-
if (!
|
|
19293
|
+
if (!existsSync16(join27(repoRoot, INSTALL_MANIFEST_REL)))
|
|
19010
19294
|
return [];
|
|
19011
19295
|
const manifest = readPreviousManifest(repoRoot);
|
|
19012
19296
|
if (manifest === null)
|
|
@@ -19250,13 +19534,13 @@ function proveCouldNotStart(err) {
|
|
|
19250
19534
|
function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
19251
19535
|
const out = deps.stdout ?? process.stdout;
|
|
19252
19536
|
const err = deps.stderr ?? process.stderr;
|
|
19253
|
-
return new Promise((
|
|
19537
|
+
return new Promise((resolve10) => {
|
|
19254
19538
|
let settled = false;
|
|
19255
19539
|
const finish = (code) => {
|
|
19256
19540
|
if (settled)
|
|
19257
19541
|
return;
|
|
19258
19542
|
settled = true;
|
|
19259
|
-
|
|
19543
|
+
resolve10(code);
|
|
19260
19544
|
};
|
|
19261
19545
|
let child;
|
|
19262
19546
|
try {
|
|
@@ -19282,9 +19566,9 @@ function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
|
19282
19566
|
}
|
|
19283
19567
|
|
|
19284
19568
|
// dist/uninstall.js
|
|
19285
|
-
import { existsSync as
|
|
19569
|
+
import { existsSync as existsSync17, lstatSync as lstatSync5, readFileSync as readFileSync23, readdirSync as readdirSync2, rmSync as rmSync9, rmdirSync as rmdirSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
19286
19570
|
import { homedir as homedir7 } from "node:os";
|
|
19287
|
-
import { dirname as dirname8, join as
|
|
19571
|
+
import { dirname as dirname8, join as join28, relative as relative7, sep as sep3 } from "node:path";
|
|
19288
19572
|
var REMOVE_CREDENTIAL_FLAG = "--remove-credential";
|
|
19289
19573
|
function parseUninstallArgs(args2) {
|
|
19290
19574
|
let removeCredential = false;
|
|
@@ -19301,7 +19585,7 @@ function parseUninstallArgs(args2) {
|
|
|
19301
19585
|
}
|
|
19302
19586
|
function lexists(path) {
|
|
19303
19587
|
try {
|
|
19304
|
-
|
|
19588
|
+
lstatSync5(path);
|
|
19305
19589
|
return true;
|
|
19306
19590
|
} catch {
|
|
19307
19591
|
return false;
|
|
@@ -19319,7 +19603,7 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19319
19603
|
const { rel } = rule;
|
|
19320
19604
|
if (symlinkOnPath(targetRepo, rel))
|
|
19321
19605
|
return { state: "symlink" };
|
|
19322
|
-
const path =
|
|
19606
|
+
const path = join28(targetRepo, rel);
|
|
19323
19607
|
if (!lexists(path))
|
|
19324
19608
|
return { state: "absent" };
|
|
19325
19609
|
let text;
|
|
@@ -19342,8 +19626,8 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19342
19626
|
}
|
|
19343
19627
|
function validate(options, rules) {
|
|
19344
19628
|
const { targetRepo, removeCredential, home } = options;
|
|
19345
|
-
const manifestPath =
|
|
19346
|
-
const pinPath =
|
|
19629
|
+
const manifestPath = join28(targetRepo, INSTALL_MANIFEST_REL);
|
|
19630
|
+
const pinPath = join28(targetRepo, BUNDLE_PIN_REL);
|
|
19347
19631
|
if (symlinkOnPath(targetRepo, INSTALL_MANIFEST_REL) || symlinkOnPath(targetRepo, BUNDLE_PIN_REL)) {
|
|
19348
19632
|
throw new Refusal(`.halfcycle, or a file Halfcycle keeps in it, is a symbolic link here, so nothing was followed and nothing was removed.`);
|
|
19349
19633
|
}
|
|
@@ -19384,7 +19668,7 @@ function validate(options, rules) {
|
|
|
19384
19668
|
throw new Refusal(`the project id in ${BUNDLE_PIN_REL} is not a valid Halfcycle project id, so it cannot name a folder on this machine. Nothing was removed, here or under ~/.halfcycle.`);
|
|
19385
19669
|
}
|
|
19386
19670
|
if (engagementId !== void 0) {
|
|
19387
|
-
const expectedParent =
|
|
19671
|
+
const expectedParent = join28(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
|
|
19388
19672
|
if (dirname8(engagementStateDir(engagementId, home)) !== expectedParent) {
|
|
19389
19673
|
throw new Refusal(`the project id in ${BUNDLE_PIN_REL} does not name a folder under ~/.halfcycle/engagements. Nothing was removed.`);
|
|
19390
19674
|
}
|
|
@@ -19410,10 +19694,10 @@ function mergedFileRules(writeSet) {
|
|
|
19410
19694
|
function writeBack(targetRepo, rel, current, next, lists, name) {
|
|
19411
19695
|
if (next === current)
|
|
19412
19696
|
return;
|
|
19413
|
-
const path =
|
|
19697
|
+
const path = join28(targetRepo, rel);
|
|
19414
19698
|
try {
|
|
19415
19699
|
if (next === null) {
|
|
19416
|
-
|
|
19700
|
+
rmSync9(path);
|
|
19417
19701
|
name(lists.removed, rel, rel);
|
|
19418
19702
|
} else {
|
|
19419
19703
|
writeFileSync9(path, next, "utf-8");
|
|
@@ -19427,7 +19711,7 @@ function statusLineClaimedFromTree(targetRepo, writeSet) {
|
|
|
19427
19711
|
try {
|
|
19428
19712
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19429
19713
|
return {};
|
|
19430
|
-
const value = JSON.parse(readFileSync23(
|
|
19714
|
+
const value = JSON.parse(readFileSync23(join28(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19431
19715
|
if (!isPlainObject4(value) || !("statusLine" in value))
|
|
19432
19716
|
return {};
|
|
19433
19717
|
const hash = canonicalSha256(value["statusLine"]);
|
|
@@ -19440,7 +19724,7 @@ function attributionClaimedFromTree(targetRepo, writeSet) {
|
|
|
19440
19724
|
try {
|
|
19441
19725
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19442
19726
|
return {};
|
|
19443
|
-
const value = JSON.parse(readFileSync23(
|
|
19727
|
+
const value = JSON.parse(readFileSync23(join28(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19444
19728
|
if (!isPlainObject4(value) || !("attribution" in value))
|
|
19445
19729
|
return {};
|
|
19446
19730
|
const hash = canonicalSha256(value["attribution"]);
|
|
@@ -19454,7 +19738,7 @@ function noRecord(targetRepo, writeSet) {
|
|
|
19454
19738
|
const claim = (rel, header, lines) => {
|
|
19455
19739
|
let text;
|
|
19456
19740
|
try {
|
|
19457
|
-
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(
|
|
19741
|
+
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(join28(targetRepo, rel), "utf-8");
|
|
19458
19742
|
} catch {
|
|
19459
19743
|
text = "";
|
|
19460
19744
|
}
|
|
@@ -19700,7 +19984,7 @@ function lastOccurrence(text, block) {
|
|
|
19700
19984
|
}
|
|
19701
19985
|
function coveredPathExists(targetRepo, line) {
|
|
19702
19986
|
const path = line.trim().replace(/^\//, "").replace(/\/$/, "");
|
|
19703
|
-
return path !== "" && lexists(
|
|
19987
|
+
return path !== "" && lexists(join28(targetRepo, path));
|
|
19704
19988
|
}
|
|
19705
19989
|
function selfBuildMergedFileRules() {
|
|
19706
19990
|
return {
|
|
@@ -19714,10 +19998,10 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19714
19998
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19715
19999
|
return;
|
|
19716
20000
|
}
|
|
19717
|
-
const abs =
|
|
20001
|
+
const abs = join28(targetRepo, rel);
|
|
19718
20002
|
let isFile;
|
|
19719
20003
|
try {
|
|
19720
|
-
isFile =
|
|
20004
|
+
isFile = lstatSync5(abs).isFile();
|
|
19721
20005
|
} catch {
|
|
19722
20006
|
return;
|
|
19723
20007
|
}
|
|
@@ -19726,7 +20010,7 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19726
20010
|
return;
|
|
19727
20011
|
}
|
|
19728
20012
|
try {
|
|
19729
|
-
|
|
20013
|
+
rmSync9(abs);
|
|
19730
20014
|
name(lists.removed, rel, rel);
|
|
19731
20015
|
} catch {
|
|
19732
20016
|
name(lists.failed, rel, rel);
|
|
@@ -19738,14 +20022,14 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19738
20022
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19739
20023
|
return;
|
|
19740
20024
|
}
|
|
19741
|
-
const abs =
|
|
20025
|
+
const abs = join28(targetRepo, rel);
|
|
19742
20026
|
let bytes;
|
|
19743
20027
|
try {
|
|
19744
20028
|
bytes = readFileSync23(abs);
|
|
19745
20029
|
} catch {
|
|
19746
20030
|
return;
|
|
19747
20031
|
}
|
|
19748
|
-
if (!
|
|
20032
|
+
if (!lstatSync5(abs).isFile()) {
|
|
19749
20033
|
name(lists.leftAlone, `${rel} (not a file)`, rel);
|
|
19750
20034
|
return;
|
|
19751
20035
|
}
|
|
@@ -19754,7 +20038,7 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19754
20038
|
return;
|
|
19755
20039
|
}
|
|
19756
20040
|
try {
|
|
19757
|
-
|
|
20041
|
+
rmSync9(abs);
|
|
19758
20042
|
name(lists.removed, rel, rel);
|
|
19759
20043
|
} catch {
|
|
19760
20044
|
name(lists.failed, rel, rel);
|
|
@@ -19765,14 +20049,14 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19765
20049
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19766
20050
|
return;
|
|
19767
20051
|
}
|
|
19768
|
-
const abs =
|
|
20052
|
+
const abs = join28(targetRepo, rel);
|
|
19769
20053
|
let bytes;
|
|
19770
20054
|
try {
|
|
19771
20055
|
bytes = readFileSync23(abs);
|
|
19772
20056
|
} catch {
|
|
19773
20057
|
return;
|
|
19774
20058
|
}
|
|
19775
|
-
if (!
|
|
20059
|
+
if (!lstatSync5(abs).isFile()) {
|
|
19776
20060
|
name(lists.leftAlone, `${rel} (not a file)`, rel);
|
|
19777
20061
|
return;
|
|
19778
20062
|
}
|
|
@@ -19781,7 +20065,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19781
20065
|
return;
|
|
19782
20066
|
}
|
|
19783
20067
|
try {
|
|
19784
|
-
|
|
20068
|
+
rmSync9(abs);
|
|
19785
20069
|
name(lists.removed, rel, rel);
|
|
19786
20070
|
} catch {
|
|
19787
20071
|
name(lists.failed, rel, rel);
|
|
@@ -19790,15 +20074,15 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19790
20074
|
function removeSelfBuildEmptyDir(targetRepo, dir, lists, name) {
|
|
19791
20075
|
if (symlinkOnPath(targetRepo, dir))
|
|
19792
20076
|
return;
|
|
19793
|
-
const abs =
|
|
20077
|
+
const abs = join28(targetRepo, dir);
|
|
19794
20078
|
try {
|
|
19795
|
-
if (!
|
|
20079
|
+
if (!lstatSync5(abs).isDirectory() || readdirSync2(abs).length > 0)
|
|
19796
20080
|
return;
|
|
19797
20081
|
} catch {
|
|
19798
20082
|
return;
|
|
19799
20083
|
}
|
|
19800
20084
|
try {
|
|
19801
|
-
|
|
20085
|
+
rmdirSync2(abs);
|
|
19802
20086
|
} catch {
|
|
19803
20087
|
name(lists.failed, `${dir}/`, dir);
|
|
19804
20088
|
}
|
|
@@ -19824,7 +20108,7 @@ function uninstallSelfBuild(options) {
|
|
|
19824
20108
|
throw err;
|
|
19825
20109
|
}
|
|
19826
20110
|
const { targetRepo } = options;
|
|
19827
|
-
const pinExisted = lexists(
|
|
20111
|
+
const pinExisted = lexists(join28(targetRepo, BUNDLE_PIN_REL));
|
|
19828
20112
|
const name = (list2, text, _path) => {
|
|
19829
20113
|
list2.push(text);
|
|
19830
20114
|
};
|
|
@@ -19911,7 +20195,7 @@ function uninstall(options) {
|
|
|
19911
20195
|
}
|
|
19912
20196
|
const serverRemoved = removeOwnServerApproval(targetRepo);
|
|
19913
20197
|
if (listRemoved || serverRemoved) {
|
|
19914
|
-
if (
|
|
20198
|
+
if (existsSync17(join28(targetRepo, LOCAL_SETTINGS_REL2))) {
|
|
19915
20199
|
name(result.restored, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
19916
20200
|
} else {
|
|
19917
20201
|
name(result.removed, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
@@ -19953,7 +20237,7 @@ function uninstall(options) {
|
|
|
19953
20237
|
const gitattributes = manifest.gitattributes ?? { created: false, appended: [] };
|
|
19954
20238
|
trimAppendedFile(targetRepo, gitattributes, deleteWhenEmpty(gitattributes.created), gitattributesRules(writeSet), result, name);
|
|
19955
20239
|
const foundNothing = result.removed.length + result.restored.length + result.kept.length + result.failed.length === 0 && result.keptIgnoreLines.length === 0;
|
|
19956
|
-
if (checked.manifest === null && !lexists(
|
|
20240
|
+
if (checked.manifest === null && !lexists(join28(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
|
|
19957
20241
|
return { outcome: "not-installed", exitCode: 0, ...result, credential: nothingToSay };
|
|
19958
20242
|
}
|
|
19959
20243
|
const credential = handleCredential(options, checked);
|
|
@@ -19961,7 +20245,7 @@ function uninstall(options) {
|
|
|
19961
20245
|
if (result.failed.length === 0 && !credentialFailed) {
|
|
19962
20246
|
removeInstalledFile(targetRepo, BUNDLE_PIN_REL, recorded.get(BUNDLE_PIN_REL), writeSet, result, name);
|
|
19963
20247
|
try {
|
|
19964
|
-
|
|
20248
|
+
rmSync9(join28(targetRepo, INSTALL_MANIFEST_REL), { force: true });
|
|
19965
20249
|
named2.add(INSTALL_MANIFEST_REL);
|
|
19966
20250
|
} catch {
|
|
19967
20251
|
name(result.failed, INSTALL_MANIFEST_REL, INSTALL_MANIFEST_REL);
|
|
@@ -19996,13 +20280,13 @@ function applyMergedJson(targetRepo, rule, file, deleteWhenEmpty, lists, name, e
|
|
|
19996
20280
|
return;
|
|
19997
20281
|
const empty = Object.keys(value).length === 0 && deleteWhenEmpty;
|
|
19998
20282
|
writeBack(targetRepo, rel, file.text, empty ? null : inLineEndingOf(file.text, serializeJson(value)), lists, name);
|
|
19999
|
-
if (adopted && lexists(
|
|
20283
|
+
if (adopted && lexists(join28(targetRepo, rel))) {
|
|
20000
20284
|
name(lists.leftAlone, `${rel} (Halfcycle's hooks were taken out. There is no record of this file from before the install, so a permission rule or "$schema" Halfcycle added may still be there \u2014 it cannot be told apart from your own)`, rel);
|
|
20001
20285
|
}
|
|
20002
20286
|
}
|
|
20003
20287
|
function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, name) {
|
|
20004
20288
|
const rel = CODEX_CONFIG_REL2;
|
|
20005
|
-
const path =
|
|
20289
|
+
const path = join28(targetRepo, rel);
|
|
20006
20290
|
if (symlinkOnPath(targetRepo, rel)) {
|
|
20007
20291
|
if (lexists(path))
|
|
20008
20292
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
@@ -20010,7 +20294,7 @@ function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, nam
|
|
|
20010
20294
|
}
|
|
20011
20295
|
let text;
|
|
20012
20296
|
try {
|
|
20013
|
-
if (!
|
|
20297
|
+
if (!lstatSync5(path).isFile())
|
|
20014
20298
|
return;
|
|
20015
20299
|
text = readFileSync23(path, "utf-8");
|
|
20016
20300
|
} catch {
|
|
@@ -20047,10 +20331,10 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20047
20331
|
name(lists.leftAlone, `${path} (a symbolic link, or inside one \u2014 not followed)`, path);
|
|
20048
20332
|
return;
|
|
20049
20333
|
}
|
|
20050
|
-
const abs =
|
|
20334
|
+
const abs = join28(targetRepo, path);
|
|
20051
20335
|
let isFile;
|
|
20052
20336
|
try {
|
|
20053
|
-
isFile =
|
|
20337
|
+
isFile = lstatSync5(abs).isFile();
|
|
20054
20338
|
} catch {
|
|
20055
20339
|
return;
|
|
20056
20340
|
}
|
|
@@ -20072,7 +20356,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20072
20356
|
}
|
|
20073
20357
|
}
|
|
20074
20358
|
try {
|
|
20075
|
-
|
|
20359
|
+
rmSync9(abs);
|
|
20076
20360
|
name(lists.removed, path, path);
|
|
20077
20361
|
} catch {
|
|
20078
20362
|
name(lists.failed, path, path);
|
|
@@ -20081,15 +20365,15 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20081
20365
|
function removeEmptyDir(targetRepo, dir, writeSet, lists, name) {
|
|
20082
20366
|
if (!writeSet.dirs.has(dir) || symlinkOnPath(targetRepo, dir))
|
|
20083
20367
|
return;
|
|
20084
|
-
const abs =
|
|
20368
|
+
const abs = join28(targetRepo, dir);
|
|
20085
20369
|
try {
|
|
20086
|
-
if (!
|
|
20370
|
+
if (!lstatSync5(abs).isDirectory() || readdirSync2(abs).length > 0)
|
|
20087
20371
|
return;
|
|
20088
20372
|
} catch {
|
|
20089
20373
|
return;
|
|
20090
20374
|
}
|
|
20091
20375
|
try {
|
|
20092
|
-
|
|
20376
|
+
rmdirSync2(abs);
|
|
20093
20377
|
} catch {
|
|
20094
20378
|
name(lists.failed, `${dir}/`, dir);
|
|
20095
20379
|
}
|
|
@@ -20100,7 +20384,7 @@ function trimAppendedFile(targetRepo, rec, deleteWhenEmpty, rules, lists, name)
|
|
|
20100
20384
|
name(lists.leftAlone, `${rel} (a symbolic link \u2014 not followed)`, rel);
|
|
20101
20385
|
return [];
|
|
20102
20386
|
}
|
|
20103
|
-
const path =
|
|
20387
|
+
const path = join28(targetRepo, rel);
|
|
20104
20388
|
let before;
|
|
20105
20389
|
try {
|
|
20106
20390
|
before = readFileSync23(path, "utf-8");
|
|
@@ -20131,12 +20415,12 @@ function handleCredential(options, checked) {
|
|
|
20131
20415
|
if (!lexists(dir))
|
|
20132
20416
|
return { kind: "not-held", dir };
|
|
20133
20417
|
try {
|
|
20134
|
-
if (
|
|
20418
|
+
if (lstatSync5(dir).isSymbolicLink())
|
|
20135
20419
|
return { kind: "failed", dir, reason: "it is a symbolic link" };
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
if (
|
|
20139
|
-
|
|
20420
|
+
rmSync9(engagementEnvPath(engagementId, home), { force: true });
|
|
20421
|
+
rmSync9(notThisAccountMarkerPath(engagementId, home), { force: true });
|
|
20422
|
+
if (readdirSync2(dir).length === 0)
|
|
20423
|
+
rmdirSync2(dir);
|
|
20140
20424
|
} catch (err) {
|
|
20141
20425
|
return { kind: "failed", dir, reason: err instanceof Error ? err.message : String(err) };
|
|
20142
20426
|
}
|
|
@@ -20147,16 +20431,16 @@ function filesUnder(root, rel) {
|
|
|
20147
20431
|
const walk = (dirRel) => {
|
|
20148
20432
|
let names;
|
|
20149
20433
|
try {
|
|
20150
|
-
if (!
|
|
20434
|
+
if (!lstatSync5(join28(root, dirRel)).isDirectory())
|
|
20151
20435
|
return;
|
|
20152
|
-
names =
|
|
20436
|
+
names = readdirSync2(join28(root, dirRel));
|
|
20153
20437
|
} catch {
|
|
20154
20438
|
return;
|
|
20155
20439
|
}
|
|
20156
20440
|
for (const entry of names.sort()) {
|
|
20157
20441
|
const childRel = `${dirRel}/${entry}`;
|
|
20158
20442
|
try {
|
|
20159
|
-
if (
|
|
20443
|
+
if (lstatSync5(join28(root, childRel)).isDirectory())
|
|
20160
20444
|
walk(childRel);
|
|
20161
20445
|
else
|
|
20162
20446
|
out.push(childRel);
|
|
@@ -20170,9 +20454,9 @@ function filesUnder(root, rel) {
|
|
|
20170
20454
|
}
|
|
20171
20455
|
function shownPath(path, home) {
|
|
20172
20456
|
const base = home ?? homedir7();
|
|
20173
|
-
const rel =
|
|
20174
|
-
const under = rel !== "" && !rel.startsWith("..") && !rel.startsWith(
|
|
20175
|
-
return `${under ? `~/${rel.split(
|
|
20457
|
+
const rel = relative7(base, path);
|
|
20458
|
+
const under = rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep3);
|
|
20459
|
+
return `${under ? `~/${rel.split(sep3).join("/")}` : path}/`;
|
|
20176
20460
|
}
|
|
20177
20461
|
function renderUninstallReport(result, home) {
|
|
20178
20462
|
if (result.outcome === "refused") {
|
|
@@ -20228,7 +20512,7 @@ function renderUninstallReport(result, home) {
|
|
|
20228
20512
|
}
|
|
20229
20513
|
|
|
20230
20514
|
// dist/banner-facts.js
|
|
20231
|
-
import { basename as basename4, resolve as
|
|
20515
|
+
import { basename as basename4, resolve as resolve8 } from "node:path";
|
|
20232
20516
|
var BRAND_URL = "halfcycle.ai";
|
|
20233
20517
|
var TAGLINE = [
|
|
20234
20518
|
"Fewer cycles.",
|
|
@@ -20270,15 +20554,44 @@ var REAL_PROBES = {
|
|
|
20270
20554
|
return void 0;
|
|
20271
20555
|
}
|
|
20272
20556
|
},
|
|
20273
|
-
websiteHost: (env) => {
|
|
20557
|
+
websiteHost: (env, dir) => {
|
|
20274
20558
|
try {
|
|
20275
|
-
|
|
20276
|
-
return resolved.source === "default" ? BRAND_URL : new URL(resolved.origin).host;
|
|
20559
|
+
return serviceHost4(env, dir);
|
|
20277
20560
|
} catch {
|
|
20278
20561
|
return void 0;
|
|
20279
20562
|
}
|
|
20280
20563
|
}
|
|
20281
20564
|
};
|
|
20565
|
+
function serviceHost4(env, projectDir, home) {
|
|
20566
|
+
const fromEnv = resolveControlOrigin(env);
|
|
20567
|
+
const origin = fromEnv.source === "environment" ? fromEnv.origin : storedServiceOrigin(projectDir, home) ?? fromEnv.origin;
|
|
20568
|
+
if (origin === DEFAULT_CONTROL_ORIGIN)
|
|
20569
|
+
return BRAND_URL;
|
|
20570
|
+
try {
|
|
20571
|
+
return new URL(origin).host || BRAND_URL;
|
|
20572
|
+
} catch {
|
|
20573
|
+
return BRAND_URL;
|
|
20574
|
+
}
|
|
20575
|
+
}
|
|
20576
|
+
function storedServiceOrigin(projectDir, home) {
|
|
20577
|
+
let engagementId;
|
|
20578
|
+
try {
|
|
20579
|
+
engagementId = readBundlePin(projectDir)?.engagementId;
|
|
20580
|
+
} catch {
|
|
20581
|
+
return void 0;
|
|
20582
|
+
}
|
|
20583
|
+
if (typeof engagementId !== "string" || engagementId === "")
|
|
20584
|
+
return void 0;
|
|
20585
|
+
try {
|
|
20586
|
+
const stored = readEngagementEnv(engagementId, home)?.["HALFCYCLE_SERVICE_URL"];
|
|
20587
|
+
if (stored === void 0)
|
|
20588
|
+
return void 0;
|
|
20589
|
+
const resolved = resolveControlOrigin({ HALFCYCLE_SERVICE_URL: stored });
|
|
20590
|
+
return resolved.source === "environment" ? resolved.origin : void 0;
|
|
20591
|
+
} catch {
|
|
20592
|
+
return void 0;
|
|
20593
|
+
}
|
|
20594
|
+
}
|
|
20282
20595
|
function fact(label, value, narrow) {
|
|
20283
20596
|
if (value === void 0 || value === "")
|
|
20284
20597
|
return void 0;
|
|
@@ -20304,14 +20617,14 @@ function openingBannerContent(projectDir, env = process.env, probes = REAL_PROBE
|
|
|
20304
20617
|
// `Website`, NOT `Plane` — see this function's docblock. (No issue number here:
|
|
20305
20618
|
// a comment beside a property survives into the published entry, unlike a
|
|
20306
20619
|
// leading docblock, which esbuild drops.)
|
|
20307
|
-
fact("Website", probes.websiteHost(env))
|
|
20620
|
+
fact("Website", probes.websiteHost(env, projectDir))
|
|
20308
20621
|
].filter((f) => f !== void 0);
|
|
20309
20622
|
return {
|
|
20310
20623
|
head: `halfcycle@${name}`,
|
|
20311
20624
|
facts,
|
|
20312
20625
|
tag: TAGLINE,
|
|
20313
20626
|
tagNarrow: TAGLINE_NARROW,
|
|
20314
|
-
url: probes.websiteHost(env) ?? BRAND_URL,
|
|
20627
|
+
url: probes.websiteHost(env, projectDir) ?? BRAND_URL,
|
|
20315
20628
|
// `''` when even this package's own manifest could not be read. The one-line cut
|
|
20316
20629
|
// drops the whole `vX.Y.Z` clause in that case rather than printing a bare `v`.
|
|
20317
20630
|
version: version ?? ""
|
|
@@ -20319,7 +20632,7 @@ function openingBannerContent(projectDir, env = process.env, probes = REAL_PROBE
|
|
|
20319
20632
|
}
|
|
20320
20633
|
function safeBasename(dir) {
|
|
20321
20634
|
try {
|
|
20322
|
-
const name = basename4(
|
|
20635
|
+
const name = basename4(resolve8(dir)).trim();
|
|
20323
20636
|
return name === "" ? "project" : name;
|
|
20324
20637
|
} catch {
|
|
20325
20638
|
return "project";
|
|
@@ -20661,7 +20974,7 @@ async function runCrew(repoRoot, deps) {
|
|
|
20661
20974
|
// dist/pace.js
|
|
20662
20975
|
import { spawn as spawn3 } from "node:child_process";
|
|
20663
20976
|
import { readFileSync as readFileSync24 } from "node:fs";
|
|
20664
|
-
import { join as
|
|
20977
|
+
import { join as join29 } from "node:path";
|
|
20665
20978
|
var PACE_USAGE = `${ownCommand()} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
|
|
20666
20979
|
function parsePaceArgs(argv) {
|
|
20667
20980
|
let repoRoot;
|
|
@@ -20726,13 +21039,13 @@ function runPaceChild(binary, credential, repoRoot, args2, deps = {}) {
|
|
|
20726
21039
|
if (binary instanceof Error)
|
|
20727
21040
|
return Promise.resolve({ code: 1, stdout: "" });
|
|
20728
21041
|
const err = deps.stderr ?? process.stderr;
|
|
20729
|
-
return new Promise((
|
|
21042
|
+
return new Promise((resolve10) => {
|
|
20730
21043
|
let settled = false;
|
|
20731
21044
|
const done = (output) => {
|
|
20732
21045
|
if (settled)
|
|
20733
21046
|
return;
|
|
20734
21047
|
settled = true;
|
|
20735
|
-
|
|
21048
|
+
resolve10(output);
|
|
20736
21049
|
};
|
|
20737
21050
|
try {
|
|
20738
21051
|
const child = spawn3(process.execPath, [binary, "pace", ...args2], {
|
|
@@ -20775,7 +21088,7 @@ function notPacedLine(reason) {
|
|
|
20775
21088
|
}
|
|
20776
21089
|
function readPromptScript(repoRoot) {
|
|
20777
21090
|
try {
|
|
20778
|
-
return readFileSync24(
|
|
21091
|
+
return readFileSync24(join29(repoRoot, ".claude", "hooks", "user-prompt-scope.sh"), "utf-8");
|
|
20779
21092
|
} catch {
|
|
20780
21093
|
return null;
|
|
20781
21094
|
}
|
|
@@ -20835,7 +21148,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
|
|
|
20835
21148
|
|
|
20836
21149
|
// dist/route.js
|
|
20837
21150
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
20838
|
-
import { join as
|
|
21151
|
+
import { join as join30 } from "node:path";
|
|
20839
21152
|
var ROUTE_USAGE = `${ownCommand()} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
|
|
20840
21153
|
var ROUTE_WORK_READ_DEADLINE_MS = 5e3;
|
|
20841
21154
|
function parseRouteArgs(argv) {
|
|
@@ -20878,7 +21191,7 @@ function parseRouteArgs(argv) {
|
|
|
20878
21191
|
return { ok: true, phase: positionals[0], task: positionals[1], agent: agent ?? "claude-code", repoRoot };
|
|
20879
21192
|
}
|
|
20880
21193
|
function routingPath(engagementId, home) {
|
|
20881
|
-
return
|
|
21194
|
+
return join30(engagementStateDir(engagementId, home), ROUTING_FILENAME);
|
|
20882
21195
|
}
|
|
20883
21196
|
function readCheapStarted(path, phase) {
|
|
20884
21197
|
try {
|
|
@@ -20901,8 +21214,8 @@ function lastTierOf(attempts) {
|
|
|
20901
21214
|
}
|
|
20902
21215
|
async function readWorkInTime(deps, credential, phase) {
|
|
20903
21216
|
let timer;
|
|
20904
|
-
const late = new Promise((
|
|
20905
|
-
timer = setTimeout(() =>
|
|
21217
|
+
const late = new Promise((resolve10) => {
|
|
21218
|
+
timer = setTimeout(() => resolve10({ kind: "unreachable" }), ROUTE_WORK_READ_DEADLINE_MS);
|
|
20906
21219
|
timer.unref?.();
|
|
20907
21220
|
});
|
|
20908
21221
|
try {
|
|
@@ -20968,10 +21281,10 @@ async function runRoute(repoRoot, phase, taskId, agent, deps) {
|
|
|
20968
21281
|
}
|
|
20969
21282
|
|
|
20970
21283
|
// dist/restore.js
|
|
20971
|
-
import { spawnSync as
|
|
20972
|
-
import { closeSync as closeSync3, fchmodSync, lstatSync as
|
|
21284
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21285
|
+
import { closeSync as closeSync3, fchmodSync, lstatSync as lstatSync6, mkdirSync as mkdirSync12, openSync as openSync3, readdirSync as readdirSync3, readFileSync as readFileSync26, readlinkSync, realpathSync as realpathSync8, rmSync as rmSync10, symlinkSync, writeSync as writeSync3 } from "node:fs";
|
|
20973
21286
|
import { homedir as homedir8 } from "node:os";
|
|
20974
|
-
import { join as
|
|
21287
|
+
import { join as join31, resolve as resolve9 } from "node:path";
|
|
20975
21288
|
var RESTORE_USAGE = `${ownCommand()} restore [<id>]`;
|
|
20976
21289
|
function parseRestoreArgs(rest) {
|
|
20977
21290
|
if (rest.length > 1)
|
|
@@ -20984,7 +21297,7 @@ function parseRestoreArgs(rest) {
|
|
|
20984
21297
|
var LIST_COMMAND = `${ownCommand()} restore`;
|
|
20985
21298
|
function runRestore(cwd, id, options = {}) {
|
|
20986
21299
|
const now = options.now ?? Date.now;
|
|
20987
|
-
const top =
|
|
21300
|
+
const top = git3(cwd, ["rev-parse", "--show-toplevel"]);
|
|
20988
21301
|
if (!top.ok || top.text.trim() === "") {
|
|
20989
21302
|
return refused2("This folder is not in a project kept with git, so Halfcycle has saved nothing here to bring back.");
|
|
20990
21303
|
}
|
|
@@ -20993,7 +21306,7 @@ function runRestore(cwd, id, options = {}) {
|
|
|
20993
21306
|
if (engagementId === null) {
|
|
20994
21307
|
return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${ownCommand()}`);
|
|
20995
21308
|
}
|
|
20996
|
-
const folder2 =
|
|
21309
|
+
const folder2 = join31(options.home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
|
|
20997
21310
|
const refs = savedRefs(root);
|
|
20998
21311
|
if (refs === null)
|
|
20999
21312
|
return refused2("Halfcycle could not read the saved copies in this project, so nothing was put back.");
|
|
@@ -21069,7 +21382,7 @@ function restoreOne(root, folder2, refs, rawId) {
|
|
|
21069
21382
|
if (p.keptIn === "commit")
|
|
21070
21383
|
fromCommit.push(p.path);
|
|
21071
21384
|
else
|
|
21072
|
-
restoreCopy(root,
|
|
21385
|
+
restoreCopy(root, join31(folder2, id), p.path, out);
|
|
21073
21386
|
}
|
|
21074
21387
|
if (fromCommit.length > 0)
|
|
21075
21388
|
restoreFromCommit(root, ref, fromCommit, out);
|
|
@@ -21097,7 +21410,7 @@ function makeFolders(root, rel) {
|
|
|
21097
21410
|
const segments = rel.split("/");
|
|
21098
21411
|
let at = root;
|
|
21099
21412
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
21100
|
-
at =
|
|
21413
|
+
at = join31(at, segments[i]);
|
|
21101
21414
|
const shown2 = segments.slice(0, i + 1).join("/");
|
|
21102
21415
|
let st = lstatOrNull3(at);
|
|
21103
21416
|
if (st === null) {
|
|
@@ -21117,7 +21430,7 @@ function makeFolders(root, rel) {
|
|
|
21117
21430
|
return null;
|
|
21118
21431
|
}
|
|
21119
21432
|
function place(root, rel, content, out) {
|
|
21120
|
-
const abs =
|
|
21433
|
+
const abs = join31(root, rel);
|
|
21121
21434
|
const there = lstatOrNull3(abs);
|
|
21122
21435
|
if (there !== null) {
|
|
21123
21436
|
if (sameAs(abs, there, content))
|
|
@@ -21165,14 +21478,14 @@ function restoreCopy(root, saveDir, rel, out) {
|
|
|
21165
21478
|
let at = saveDir;
|
|
21166
21479
|
const chain = [SAVED_COPIES_DIR_NAME, ...segments];
|
|
21167
21480
|
for (let i = 0; i < chain.length - 1; i++) {
|
|
21168
|
-
at =
|
|
21481
|
+
at = join31(at, chain[i]);
|
|
21169
21482
|
const st2 = lstatOrNull3(at);
|
|
21170
21483
|
if (st2 === null || st2.isSymbolicLink() || !st2.isDirectory()) {
|
|
21171
21484
|
out.left.push(`${rel}: the saved copy does not hold it`);
|
|
21172
21485
|
return;
|
|
21173
21486
|
}
|
|
21174
21487
|
}
|
|
21175
|
-
const src =
|
|
21488
|
+
const src = join31(at, chain[chain.length - 1]);
|
|
21176
21489
|
const st = lstatOrNull3(src);
|
|
21177
21490
|
try {
|
|
21178
21491
|
if (st !== null && st.isSymbolicLink())
|
|
@@ -21187,7 +21500,7 @@ function restoreFromCommit(root, ref, rels, out) {
|
|
|
21187
21500
|
const entries = /* @__PURE__ */ new Map();
|
|
21188
21501
|
for (let i = 0; i < rels.length; i += 200) {
|
|
21189
21502
|
const chunk = rels.slice(i, i + 200);
|
|
21190
|
-
const listed =
|
|
21503
|
+
const listed = git3(root, ["--literal-pathspecs", "ls-tree", "-z", "--full-tree", `${ref}^{commit}`, "--", ...chunk]);
|
|
21191
21504
|
if (!listed.ok) {
|
|
21192
21505
|
for (const rel of rels)
|
|
21193
21506
|
out.left.push(`${rel}: the saved copy could not be read`);
|
|
@@ -21218,7 +21531,7 @@ function restoreFromCommit(root, ref, rels, out) {
|
|
|
21218
21531
|
}
|
|
21219
21532
|
}
|
|
21220
21533
|
function readBlobs(root, objects) {
|
|
21221
|
-
const r =
|
|
21534
|
+
const r = spawnSync4("git", ["cat-file", "--batch"], {
|
|
21222
21535
|
cwd: root,
|
|
21223
21536
|
env: gitEnvForNamedRepo(),
|
|
21224
21537
|
input: `${objects.join("\n")}
|
|
@@ -21246,12 +21559,12 @@ function readBlobs(root, objects) {
|
|
|
21246
21559
|
function restoreLines(root, ref, saved, out) {
|
|
21247
21560
|
if (saved.branches.length === 0)
|
|
21248
21561
|
return;
|
|
21249
|
-
const parents =
|
|
21562
|
+
const parents = git3(root, ["rev-list", "--parents", "-n", "1", `${ref}^{commit}`]);
|
|
21250
21563
|
const held = new Set(parents.ok ? parents.text.trim().split(/\s+/).slice(1) : []);
|
|
21251
21564
|
for (const b of saved.branches) {
|
|
21252
21565
|
const shown2 = `the line of work "${b.name}"`;
|
|
21253
21566
|
const full = `refs/heads/${b.name}`;
|
|
21254
|
-
if (!
|
|
21567
|
+
if (!git3(root, ["check-ref-format", full]).ok) {
|
|
21255
21568
|
out.left.push(`${shown2}: its name cannot be used`);
|
|
21256
21569
|
continue;
|
|
21257
21570
|
}
|
|
@@ -21259,7 +21572,7 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21259
21572
|
out.left.push(`${shown2}: the saved copy does not hold its commit`);
|
|
21260
21573
|
continue;
|
|
21261
21574
|
}
|
|
21262
|
-
const now =
|
|
21575
|
+
const now = git3(root, ["rev-parse", "--verify", "--quiet", `${full}^{commit}`]);
|
|
21263
21576
|
if (now.ok && now.text.trim() !== "") {
|
|
21264
21577
|
if (now.text.trim() === b.commit)
|
|
21265
21578
|
out.already.push(`${shown2}, at its saved commit ${b.commit.slice(0, 12)}`);
|
|
@@ -21267,7 +21580,7 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21267
21580
|
out.left.push(`${shown2}: one with this name exists now; rename it and run this again to bring back the saved one`);
|
|
21268
21581
|
continue;
|
|
21269
21582
|
}
|
|
21270
|
-
const made =
|
|
21583
|
+
const made = git3(root, ["update-ref", "--no-deref", full, b.commit, ""]);
|
|
21271
21584
|
if (made.ok)
|
|
21272
21585
|
out.back.push(`${shown2}, at its saved commit ${b.commit.slice(0, 12)}`);
|
|
21273
21586
|
else
|
|
@@ -21276,14 +21589,14 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21276
21589
|
}
|
|
21277
21590
|
function pinnedEngagement(root) {
|
|
21278
21591
|
try {
|
|
21279
|
-
const pin = JSON.parse(readFileSync26(
|
|
21592
|
+
const pin = JSON.parse(readFileSync26(join31(root, BUNDLE_PIN_REL), "utf-8"));
|
|
21280
21593
|
return isEngagementId(pin.engagementId) ? pin.engagementId : null;
|
|
21281
21594
|
} catch {
|
|
21282
21595
|
return null;
|
|
21283
21596
|
}
|
|
21284
21597
|
}
|
|
21285
21598
|
function savedRefs(root) {
|
|
21286
|
-
const listed =
|
|
21599
|
+
const listed = git3(root, ["for-each-ref", "--format=%(refname)%00%(committerdate:unix)", SAVED_REF_PREFIX]);
|
|
21287
21600
|
if (!listed.ok)
|
|
21288
21601
|
return null;
|
|
21289
21602
|
const refs = /* @__PURE__ */ new Map();
|
|
@@ -21298,11 +21611,11 @@ function savedRefs(root) {
|
|
|
21298
21611
|
return refs;
|
|
21299
21612
|
}
|
|
21300
21613
|
function readSaved(folder2, id) {
|
|
21301
|
-
const dir =
|
|
21614
|
+
const dir = join31(folder2, id);
|
|
21302
21615
|
const st = lstatOrNull3(dir);
|
|
21303
21616
|
if (st === null || !st.isDirectory())
|
|
21304
21617
|
return null;
|
|
21305
|
-
const file =
|
|
21618
|
+
const file = join31(dir, SAVED_FILE_NAME);
|
|
21306
21619
|
const fst = lstatOrNull3(file);
|
|
21307
21620
|
if (fst === null || !fst.isFile())
|
|
21308
21621
|
return null;
|
|
@@ -21315,11 +21628,11 @@ function readSaved(folder2, id) {
|
|
|
21315
21628
|
}
|
|
21316
21629
|
function pruneAtRestore(root, folder2, refs, now) {
|
|
21317
21630
|
try {
|
|
21318
|
-
const common =
|
|
21319
|
-
const repository = common.ok && common.text.trim() !== "" ? realOrSelf(
|
|
21631
|
+
const common = git3(root, ["rev-parse", "--git-common-dir"]);
|
|
21632
|
+
const repository = common.ok && common.text.trim() !== "" ? realOrSelf(resolve9(root, common.text.trim())) : null;
|
|
21320
21633
|
let names = [];
|
|
21321
21634
|
try {
|
|
21322
|
-
names =
|
|
21635
|
+
names = readdirSync3(folder2);
|
|
21323
21636
|
} catch {
|
|
21324
21637
|
names = [];
|
|
21325
21638
|
}
|
|
@@ -21328,7 +21641,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21328
21641
|
if (!savedIdSchema.safeParse(id).success)
|
|
21329
21642
|
continue;
|
|
21330
21643
|
here.add(id);
|
|
21331
|
-
const dir =
|
|
21644
|
+
const dir = join31(folder2, id);
|
|
21332
21645
|
const st = lstatOrNull3(dir);
|
|
21333
21646
|
const answer = savedPruneAnswer({
|
|
21334
21647
|
refMadeAt: refs.get(id) ?? null,
|
|
@@ -21336,11 +21649,11 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21336
21649
|
folderRepository: markedRepository(dir)
|
|
21337
21650
|
}, { repository, now });
|
|
21338
21651
|
if (answer === "remove-folder")
|
|
21339
|
-
|
|
21340
|
-
else if (answer === "remove-ref-then-folder" &&
|
|
21652
|
+
rmSync10(dir, { recursive: true, force: true });
|
|
21653
|
+
else if (answer === "remove-ref-then-folder" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok) {
|
|
21341
21654
|
refs.delete(id);
|
|
21342
|
-
|
|
21343
|
-
} else if (answer === "remove-ref" &&
|
|
21655
|
+
rmSync10(dir, { recursive: true, force: true });
|
|
21656
|
+
} else if (answer === "remove-ref" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok) {
|
|
21344
21657
|
refs.delete(id);
|
|
21345
21658
|
}
|
|
21346
21659
|
}
|
|
@@ -21348,7 +21661,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21348
21661
|
if (here.has(id))
|
|
21349
21662
|
continue;
|
|
21350
21663
|
const answer = savedPruneAnswer({ refMadeAt: madeAt, folderChangedAt: null, folderRepository: null }, { repository, now });
|
|
21351
|
-
if (answer === "remove-ref" &&
|
|
21664
|
+
if (answer === "remove-ref" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok)
|
|
21352
21665
|
refs.delete(id);
|
|
21353
21666
|
}
|
|
21354
21667
|
} catch {
|
|
@@ -21356,15 +21669,15 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21356
21669
|
}
|
|
21357
21670
|
function markedRepository(dir) {
|
|
21358
21671
|
try {
|
|
21359
|
-
const text = readFileSync26(
|
|
21672
|
+
const text = readFileSync26(join31(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
|
|
21360
21673
|
return text === "" ? null : text;
|
|
21361
21674
|
} catch {
|
|
21362
21675
|
return null;
|
|
21363
21676
|
}
|
|
21364
21677
|
}
|
|
21365
|
-
function
|
|
21678
|
+
function git3(cwd, args2) {
|
|
21366
21679
|
try {
|
|
21367
|
-
const r =
|
|
21680
|
+
const r = spawnSync4("git", args2, { cwd, encoding: "utf-8", env: gitEnvForNamedRepo(), maxBuffer: 64 * 1024 * 1024 });
|
|
21368
21681
|
return r.status === 0 ? { ok: true, text: r.stdout ?? "" } : { ok: false };
|
|
21369
21682
|
} catch {
|
|
21370
21683
|
return { ok: false };
|
|
@@ -21372,23 +21685,23 @@ function git2(cwd, args2) {
|
|
|
21372
21685
|
}
|
|
21373
21686
|
function lstatOrNull3(p) {
|
|
21374
21687
|
try {
|
|
21375
|
-
return
|
|
21688
|
+
return lstatSync6(p);
|
|
21376
21689
|
} catch {
|
|
21377
21690
|
return null;
|
|
21378
21691
|
}
|
|
21379
21692
|
}
|
|
21380
21693
|
function realOrSelf(p) {
|
|
21381
21694
|
try {
|
|
21382
|
-
return
|
|
21695
|
+
return realpathSync8(p);
|
|
21383
21696
|
} catch {
|
|
21384
21697
|
return p;
|
|
21385
21698
|
}
|
|
21386
21699
|
}
|
|
21387
21700
|
|
|
21388
21701
|
// dist/request-shapes-verb.js
|
|
21389
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
21702
|
+
import { chmodSync as chmodSync4, existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync11, writeFileSync as writeFileSync10 } from "node:fs";
|
|
21390
21703
|
import { homedir as homedir9, platform as platform5 } from "node:os";
|
|
21391
|
-
import { join as
|
|
21704
|
+
import { join as join32 } from "node:path";
|
|
21392
21705
|
var REQUEST_SHAPES_USAGE = `${ownCommand()} request-shapes on|off|show|clear`;
|
|
21393
21706
|
var REQUEST_SHAPES_FORMS = ["on", "off", "show", "clear"];
|
|
21394
21707
|
var REQUEST_SHAPES_ON_LINE = "Halfcycle will write down the shape of every request this computer sends to it, never what is in them, until you run request-shapes off.";
|
|
@@ -21433,7 +21746,7 @@ function formatLine(line) {
|
|
|
21433
21746
|
function showLog(logPath2) {
|
|
21434
21747
|
let text = "";
|
|
21435
21748
|
try {
|
|
21436
|
-
text =
|
|
21749
|
+
text = existsSync18(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
|
|
21437
21750
|
} catch {
|
|
21438
21751
|
text = "";
|
|
21439
21752
|
}
|
|
@@ -21451,9 +21764,9 @@ function ownerOnly(path, mode) {
|
|
|
21451
21764
|
chmodSync4(path, mode);
|
|
21452
21765
|
}
|
|
21453
21766
|
function runRequestShapes(form, home = homedir9()) {
|
|
21454
|
-
const folder2 =
|
|
21455
|
-
const switchPath =
|
|
21456
|
-
const logPath2 =
|
|
21767
|
+
const folder2 = join32(home, HALFCYCLE_DIR_NAME, REQUEST_SHAPES_DIR_NAME);
|
|
21768
|
+
const switchPath = join32(folder2, REQUEST_SHAPES_SWITCH_FILENAME);
|
|
21769
|
+
const logPath2 = join32(folder2, REQUEST_SHAPES_LOG_FILENAME);
|
|
21457
21770
|
try {
|
|
21458
21771
|
switch (form) {
|
|
21459
21772
|
case "on":
|
|
@@ -21464,13 +21777,13 @@ function runRequestShapes(form, home = homedir9()) {
|
|
|
21464
21777
|
return { stdout: `${REQUEST_SHAPES_ON_LINE}
|
|
21465
21778
|
`, stderr: "", exitCode: 0 };
|
|
21466
21779
|
case "off":
|
|
21467
|
-
|
|
21780
|
+
rmSync11(switchPath, { force: true });
|
|
21468
21781
|
return { stdout: `${REQUEST_SHAPES_OFF_LINE}
|
|
21469
21782
|
`, stderr: "", exitCode: 0 };
|
|
21470
21783
|
case "show":
|
|
21471
21784
|
return { stdout: showLog(logPath2), stderr: "", exitCode: 0 };
|
|
21472
21785
|
case "clear":
|
|
21473
|
-
if (
|
|
21786
|
+
if (existsSync18(logPath2)) {
|
|
21474
21787
|
writeFileSync10(logPath2, "", { mode: 384 });
|
|
21475
21788
|
ownerOnly(logPath2, 384);
|
|
21476
21789
|
}
|
|
@@ -21779,7 +22092,7 @@ ${USAGE}`);
|
|
|
21779
22092
|
if (installArm) {
|
|
21780
22093
|
const engagementIdArg = positionals[1];
|
|
21781
22094
|
const engagementTypeRaw = positionals[2] ?? "client";
|
|
21782
|
-
if (!
|
|
22095
|
+
if (!existsSync19(targetRepo)) {
|
|
21783
22096
|
process.stderr.write(`${folderMissingRefusal(targetRepo)}
|
|
21784
22097
|
`);
|
|
21785
22098
|
process.exit(1);
|
|
@@ -22187,6 +22500,13 @@ ${USAGE}`);
|
|
|
22187
22500
|
const obtained = await obtainBuildRecord(credential, repoRoot, phaseArg);
|
|
22188
22501
|
if (obtained.kind === "written") {
|
|
22189
22502
|
process.stdout.write(writtenLines(obtained));
|
|
22503
|
+
const stamped = readEngagementEnv(credential.engagementId)?.[PHASE_STAMP_ENV_KEY]?.trim();
|
|
22504
|
+
if (stamped === void 0 || stamped === "" || stamped === phaseArg) {
|
|
22505
|
+
const tidied = tidyWorkerCopies(repoRoot);
|
|
22506
|
+
if (tidied.message !== void 0)
|
|
22507
|
+
process.stdout.write(`${tidied.message}
|
|
22508
|
+
`);
|
|
22509
|
+
}
|
|
22190
22510
|
if (obtained.stampFailure) {
|
|
22191
22511
|
process.stderr.write(`halfcycle build-record: the record was written, but this machine's record of the open phase could not be cleared: ${obtained.stampFailure.reason}
|
|
22192
22512
|
halfcycle build-record: until ${obtained.stampFailure.path} can be written, the checks run on this computer are still filed under the phase that closed.
|