halfcycle 0.12.0 → 0.13.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +583 -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 +50 -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,209 @@ 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
|
+
if (tree.locked || holdsWork(at)) {
|
|
11853
|
+
kept.push(shown2);
|
|
11854
|
+
continue;
|
|
11855
|
+
}
|
|
11856
|
+
clearCaches(at, at);
|
|
11857
|
+
if (!git2(root, ["worktree", "remove", at]).ok) {
|
|
11858
|
+
kept.push(shown2);
|
|
11859
|
+
continue;
|
|
11860
|
+
}
|
|
11861
|
+
removed += 1;
|
|
11862
|
+
if (tree.branch !== void 0)
|
|
11863
|
+
git2(root, ["branch", "-d", tree.branch]);
|
|
11864
|
+
}
|
|
11865
|
+
return { removed, kept, message: lineFor(removed, kept) };
|
|
11866
|
+
}
|
|
11867
|
+
function lineFor(removed, kept) {
|
|
11868
|
+
if (removed === 0 && kept.length === 0)
|
|
11869
|
+
return void 0;
|
|
11870
|
+
const head = `[halfcycle] Removed ${removed} worker ${removed === 1 ? "copy" : "copies"} under ${COPIES_SHOWN}.`;
|
|
11871
|
+
return kept.length === 0 ? head : `${head} Kept ${kept.length} that may hold work: ${kept.join(", ")}.`;
|
|
11872
|
+
}
|
|
11873
|
+
function git2(cwd, args2) {
|
|
11874
|
+
const r = spawnSync("git", [...args2], {
|
|
11875
|
+
cwd,
|
|
11876
|
+
encoding: "utf-8",
|
|
11877
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11878
|
+
windowsHide: true,
|
|
11879
|
+
timeout: 12e4,
|
|
11880
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
11881
|
+
});
|
|
11882
|
+
if (r.error !== void 0)
|
|
11883
|
+
return { ran: false, ok: false, out: "" };
|
|
11884
|
+
return { ran: true, ok: r.status === 0, out: r.stdout ?? "" };
|
|
11885
|
+
}
|
|
11886
|
+
function parseWorktrees(porcelain) {
|
|
11887
|
+
const trees = [];
|
|
11888
|
+
let current;
|
|
11889
|
+
for (const line of porcelain.split(/\r?\n/)) {
|
|
11890
|
+
if (line.startsWith("worktree ")) {
|
|
11891
|
+
current = { path: line.slice("worktree ".length), branch: void 0, locked: false, bare: false };
|
|
11892
|
+
trees.push(current);
|
|
11893
|
+
} else if (current === void 0) {
|
|
11894
|
+
continue;
|
|
11895
|
+
} else if (line.startsWith("branch refs/heads/")) {
|
|
11896
|
+
current.branch = line.slice("branch refs/heads/".length);
|
|
11897
|
+
} else if (line === "locked" || line.startsWith("locked ")) {
|
|
11898
|
+
current.locked = true;
|
|
11899
|
+
} else if (line === "bare") {
|
|
11900
|
+
current.bare = true;
|
|
11901
|
+
}
|
|
11902
|
+
}
|
|
11903
|
+
return trees;
|
|
11904
|
+
}
|
|
11905
|
+
function holdsWork(copyDir) {
|
|
11906
|
+
const status = git2(copyDir, ["status", "--porcelain", "-z", "--untracked-files=all"]);
|
|
11907
|
+
if (!status.ok)
|
|
11908
|
+
return true;
|
|
11909
|
+
const fields = status.out.split("\0");
|
|
11910
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
11911
|
+
const field = fields[i];
|
|
11912
|
+
if (field === "")
|
|
11913
|
+
continue;
|
|
11914
|
+
const code = field.slice(0, 2);
|
|
11915
|
+
if (code !== "??")
|
|
11916
|
+
return true;
|
|
11917
|
+
const path = field.slice(3);
|
|
11918
|
+
if (!path.split("/").some((segment) => CACHE_NAMES.has(segment)))
|
|
11919
|
+
return true;
|
|
11920
|
+
}
|
|
11921
|
+
return false;
|
|
11922
|
+
}
|
|
11923
|
+
function clearCaches(copyDir, dir) {
|
|
11924
|
+
let names;
|
|
11925
|
+
try {
|
|
11926
|
+
names = readdirSync(dir);
|
|
11927
|
+
} catch {
|
|
11928
|
+
return;
|
|
11929
|
+
}
|
|
11930
|
+
for (const name of names) {
|
|
11931
|
+
if (name === ".git")
|
|
11932
|
+
continue;
|
|
11933
|
+
const at = join9(dir, name);
|
|
11934
|
+
let entry;
|
|
11935
|
+
try {
|
|
11936
|
+
entry = lstatSync2(at);
|
|
11937
|
+
} catch {
|
|
11938
|
+
continue;
|
|
11939
|
+
}
|
|
11940
|
+
if (entry.isSymbolicLink()) {
|
|
11941
|
+
if (CACHE_NAMES.has(name) && !holdsTracked(copyDir, at))
|
|
11942
|
+
removeLink(at);
|
|
11943
|
+
continue;
|
|
11944
|
+
}
|
|
11945
|
+
if (!entry.isDirectory())
|
|
11946
|
+
continue;
|
|
11947
|
+
if (CACHE_NAMES.has(name)) {
|
|
11948
|
+
if (holdsTracked(copyDir, at))
|
|
11949
|
+
continue;
|
|
11950
|
+
try {
|
|
11951
|
+
rmSync3(at, { recursive: true, force: true });
|
|
11952
|
+
} catch {
|
|
11953
|
+
}
|
|
11954
|
+
continue;
|
|
11955
|
+
}
|
|
11956
|
+
clearCaches(copyDir, at);
|
|
11957
|
+
}
|
|
11958
|
+
}
|
|
11959
|
+
function holdsTracked(copyDir, path) {
|
|
11960
|
+
const listed = git2(copyDir, ["ls-files", "-z", "--", relative2(copyDir, path).split(sep).join("/")]);
|
|
11961
|
+
return !listed.ok || listed.out !== "";
|
|
11962
|
+
}
|
|
11963
|
+
function removeLink(at) {
|
|
11964
|
+
try {
|
|
11965
|
+
rmSync3(at, { force: true });
|
|
11966
|
+
} catch {
|
|
11967
|
+
try {
|
|
11968
|
+
rmdirSync(at);
|
|
11969
|
+
} catch {
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
11972
|
+
}
|
|
11973
|
+
function canonical(path) {
|
|
11974
|
+
try {
|
|
11975
|
+
return realpathSync(path);
|
|
11976
|
+
} catch {
|
|
11977
|
+
return resolve2(path);
|
|
11978
|
+
}
|
|
11979
|
+
}
|
|
11980
|
+
function fold(path) {
|
|
11981
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
11982
|
+
}
|
|
11983
|
+
function samePath(a, b) {
|
|
11984
|
+
return fold(resolve2(a)) === fold(resolve2(b));
|
|
11985
|
+
}
|
|
11986
|
+
function isInside(parent, child) {
|
|
11987
|
+
const rel = relative2(fold(parent), fold(child));
|
|
11988
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
|
|
11989
|
+
}
|
|
11990
|
+
function isInsideCopiesFolder(path) {
|
|
11991
|
+
const segments = fold(path).split(sep);
|
|
11992
|
+
for (let i = 0; i + 2 < segments.length; i += 1) {
|
|
11993
|
+
if (segments[i] === ".claude" && segments[i + 1] === "worktrees")
|
|
11994
|
+
return true;
|
|
11995
|
+
}
|
|
11996
|
+
return false;
|
|
11997
|
+
}
|
|
11998
|
+
|
|
11762
11999
|
// dist/control-origin.js
|
|
11763
12000
|
var DEFAULT_CONTROL_ORIGIN = "https://control.halfcycle.ai";
|
|
11764
12001
|
function resolveControlOrigin(env = process.env) {
|
|
@@ -11870,14 +12107,14 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11870
12107
|
else
|
|
11871
12108
|
pending = callback;
|
|
11872
12109
|
}, engagementId, serviceUrl));
|
|
11873
|
-
const bindProblem = await new Promise((
|
|
12110
|
+
const bindProblem = await new Promise((resolve10) => {
|
|
11874
12111
|
const onError = (err) => {
|
|
11875
|
-
|
|
12112
|
+
resolve10(`${err.code ?? "bind failed"} on ${LOOPBACK_REDIRECT_HOST}:${wanted.port}`);
|
|
11876
12113
|
};
|
|
11877
12114
|
server.once("error", onError);
|
|
11878
12115
|
server.listen({ host: LOOPBACK_REDIRECT_HOST, port: wanted.port }, () => {
|
|
11879
12116
|
server.removeListener("error", onError);
|
|
11880
|
-
|
|
12117
|
+
resolve10(null);
|
|
11881
12118
|
});
|
|
11882
12119
|
});
|
|
11883
12120
|
if (bindProblem !== null) {
|
|
@@ -11893,9 +12130,9 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11893
12130
|
bound: true,
|
|
11894
12131
|
target: { port: address.port, state },
|
|
11895
12132
|
boundAddress: address.address,
|
|
11896
|
-
waitForCallback: ({ timeoutMs, onStillWaiting, stillWaitingMs = DEFAULT_STILL_WAITING_MS }) => new Promise((
|
|
12133
|
+
waitForCallback: ({ timeoutMs, onStillWaiting, stillWaitingMs = DEFAULT_STILL_WAITING_MS }) => new Promise((resolve10) => {
|
|
11897
12134
|
if (pending !== null) {
|
|
11898
|
-
|
|
12135
|
+
resolve10(pending);
|
|
11899
12136
|
return;
|
|
11900
12137
|
}
|
|
11901
12138
|
const startedAt = Date.now();
|
|
@@ -11906,7 +12143,7 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11906
12143
|
clearTimeout(timer);
|
|
11907
12144
|
deliver = null;
|
|
11908
12145
|
abandon = null;
|
|
11909
|
-
|
|
12146
|
+
resolve10(value);
|
|
11910
12147
|
};
|
|
11911
12148
|
const timer = setTimeout(() => finish(null), Math.max(0, timeoutMs));
|
|
11912
12149
|
deliver = finish;
|
|
@@ -11920,23 +12157,23 @@ async function bindLoopback(env = process.env, engagementId, serviceUrl) {
|
|
|
11920
12157
|
};
|
|
11921
12158
|
}
|
|
11922
12159
|
function closeServer(server) {
|
|
11923
|
-
return new Promise((
|
|
12160
|
+
return new Promise((resolve10) => {
|
|
11924
12161
|
server.closeAllConnections();
|
|
11925
|
-
server.close(() =>
|
|
12162
|
+
server.close(() => resolve10());
|
|
11926
12163
|
});
|
|
11927
12164
|
}
|
|
11928
12165
|
|
|
11929
12166
|
// dist/sign-in-attempt.js
|
|
11930
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync, rmSync as
|
|
12167
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync2, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
11931
12168
|
import { homedir as homedir4 } from "node:os";
|
|
11932
|
-
import { join as
|
|
12169
|
+
import { join as join10 } from "node:path";
|
|
11933
12170
|
var ATTEMPT_FILENAME = "sign-in-attempt.json";
|
|
11934
12171
|
function attemptPath(home) {
|
|
11935
|
-
return
|
|
12172
|
+
return join10(home ?? homedir4(), HALFCYCLE_DIR_NAME, ATTEMPT_FILENAME);
|
|
11936
12173
|
}
|
|
11937
12174
|
function realFolder(folder2) {
|
|
11938
12175
|
try {
|
|
11939
|
-
return
|
|
12176
|
+
return realpathSync2(folder2);
|
|
11940
12177
|
} catch {
|
|
11941
12178
|
return folder2;
|
|
11942
12179
|
}
|
|
@@ -11944,7 +12181,7 @@ function realFolder(folder2) {
|
|
|
11944
12181
|
function recordSignInAttempt(folder2, expiresAt, home) {
|
|
11945
12182
|
try {
|
|
11946
12183
|
const path = attemptPath(home);
|
|
11947
|
-
mkdirSync6(
|
|
12184
|
+
mkdirSync6(join10(home ?? homedir4(), HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
|
|
11948
12185
|
const record2 = { folder: realFolder(folder2), expiresAt };
|
|
11949
12186
|
writeFileSync6(path, `${JSON.stringify(record2)}
|
|
11950
12187
|
`, { mode: 384 });
|
|
@@ -11954,7 +12191,7 @@ function recordSignInAttempt(folder2, expiresAt, home) {
|
|
|
11954
12191
|
}
|
|
11955
12192
|
function clearSignInAttempt(home) {
|
|
11956
12193
|
try {
|
|
11957
|
-
|
|
12194
|
+
rmSync4(attemptPath(home), { force: true });
|
|
11958
12195
|
} catch {
|
|
11959
12196
|
return;
|
|
11960
12197
|
}
|
|
@@ -11997,7 +12234,7 @@ function defaultWrite(text) {
|
|
|
11997
12234
|
process.stdout.write(text);
|
|
11998
12235
|
}
|
|
11999
12236
|
function defaultSleep(ms) {
|
|
12000
|
-
return new Promise((
|
|
12237
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
12001
12238
|
}
|
|
12002
12239
|
function browserOpenCommand(os, url) {
|
|
12003
12240
|
if (os === "darwin")
|
|
@@ -12044,12 +12281,12 @@ async function openBrowser(url, env = process.env, os = platform3(), spawnImpl =
|
|
|
12044
12281
|
};
|
|
12045
12282
|
}
|
|
12046
12283
|
const { command, args: args2, windowsVerbatimArguments } = browserOpenCommand(os, url);
|
|
12047
|
-
return new Promise((
|
|
12284
|
+
return new Promise((resolve10) => {
|
|
12048
12285
|
let child;
|
|
12049
12286
|
try {
|
|
12050
12287
|
child = spawnImpl(command, args2, { stdio: "ignore", env, windowsVerbatimArguments });
|
|
12051
12288
|
} catch {
|
|
12052
|
-
|
|
12289
|
+
resolve10({ opened: false, reason: `${command} could not be run` });
|
|
12053
12290
|
return;
|
|
12054
12291
|
}
|
|
12055
12292
|
let settled = false;
|
|
@@ -12058,7 +12295,7 @@ async function openBrowser(url, env = process.env, os = platform3(), spawnImpl =
|
|
|
12058
12295
|
return;
|
|
12059
12296
|
settled = true;
|
|
12060
12297
|
clearTimeout(timer);
|
|
12061
|
-
|
|
12298
|
+
resolve10(attempt2);
|
|
12062
12299
|
};
|
|
12063
12300
|
const timer = setTimeout(() => {
|
|
12064
12301
|
child.unref();
|
|
@@ -12203,17 +12440,17 @@ async function pollBesideLoopback(serviceUrl, started, wake, deps) {
|
|
|
12203
12440
|
continue;
|
|
12204
12441
|
}
|
|
12205
12442
|
if (sleep === void 0) {
|
|
12206
|
-
await new Promise((
|
|
12207
|
-
const timer = setTimeout(
|
|
12443
|
+
await new Promise((resolve10) => {
|
|
12444
|
+
const timer = setTimeout(resolve10, started.pollIntervalMs);
|
|
12208
12445
|
interrupt = () => {
|
|
12209
12446
|
clearTimeout(timer);
|
|
12210
|
-
|
|
12447
|
+
resolve10();
|
|
12211
12448
|
};
|
|
12212
12449
|
});
|
|
12213
12450
|
} else {
|
|
12214
|
-
await new Promise((
|
|
12215
|
-
interrupt =
|
|
12216
|
-
void sleep(started.pollIntervalMs).then(
|
|
12451
|
+
await new Promise((resolve10) => {
|
|
12452
|
+
interrupt = resolve10;
|
|
12453
|
+
void sleep(started.pollIntervalMs).then(resolve10);
|
|
12217
12454
|
});
|
|
12218
12455
|
}
|
|
12219
12456
|
interrupt = null;
|
|
@@ -12819,7 +13056,7 @@ function worthAnotherTry(value) {
|
|
|
12819
13056
|
return { tryAgain: true, value };
|
|
12820
13057
|
}
|
|
12821
13058
|
function realSleep(ms) {
|
|
12822
|
-
return new Promise((
|
|
13059
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
12823
13060
|
}
|
|
12824
13061
|
async function attemptInTurn(attempt2, options = {}) {
|
|
12825
13062
|
const timeouts = options.timeouts ?? ATTEMPT_TIMEOUTS_MS;
|
|
@@ -13017,9 +13254,9 @@ function wrongAccountVisibility(act) {
|
|
|
13017
13254
|
async function askLine(question2) {
|
|
13018
13255
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13019
13256
|
try {
|
|
13020
|
-
return await new Promise((
|
|
13021
|
-
rl.once("close", () =>
|
|
13022
|
-
rl.question(question2).then(
|
|
13257
|
+
return await new Promise((resolve10) => {
|
|
13258
|
+
rl.once("close", () => resolve10(""));
|
|
13259
|
+
rl.question(question2).then(resolve10, () => resolve10(""));
|
|
13023
13260
|
});
|
|
13024
13261
|
} finally {
|
|
13025
13262
|
rl.close();
|
|
@@ -13152,7 +13389,7 @@ async function confirmActingIdentity(serviceUrl, credential, act, deps = {}) {
|
|
|
13152
13389
|
}
|
|
13153
13390
|
|
|
13154
13391
|
// dist/project-name.js
|
|
13155
|
-
import { basename, resolve as
|
|
13392
|
+
import { basename, resolve as resolve3 } from "node:path";
|
|
13156
13393
|
var MAX_DERIVED_NAME_LEN = 100;
|
|
13157
13394
|
var EMPTY_SEGMENTS = /* @__PURE__ */ new Set(["", ".", "..", "/", "\\"]);
|
|
13158
13395
|
function repositoryNameFromRemote(remote) {
|
|
@@ -13172,7 +13409,7 @@ function isLocalRemote(remote) {
|
|
|
13172
13409
|
function deriveProjectName(targetRepoRoot) {
|
|
13173
13410
|
const remote = readRemote(targetRepoRoot);
|
|
13174
13411
|
const fromRemote = remote && !isLocalRemote(remote) ? repositoryNameFromRemote(remote) : void 0;
|
|
13175
|
-
const candidate = fromRemote ?? basename(
|
|
13412
|
+
const candidate = fromRemote ?? basename(resolve3(targetRepoRoot));
|
|
13176
13413
|
const name = candidate.trim();
|
|
13177
13414
|
if (EMPTY_SEGMENTS.has(name))
|
|
13178
13415
|
return void 0;
|
|
@@ -13261,9 +13498,9 @@ async function joinPinnedEngagement(serviceUrl, engagementId, targetRepo, deps =
|
|
|
13261
13498
|
}
|
|
13262
13499
|
|
|
13263
13500
|
// dist/pre-create-checks.js
|
|
13264
|
-
import { spawnSync } from "node:child_process";
|
|
13265
|
-
import { existsSync as
|
|
13266
|
-
import { delimiter, dirname as dirname5, join as
|
|
13501
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13502
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, realpathSync as realpathSync3 } from "node:fs";
|
|
13503
|
+
import { delimiter, dirname as dirname5, join as join11 } from "node:path";
|
|
13267
13504
|
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
13505
|
var PreCreateRefused = class extends Error {
|
|
13269
13506
|
constructor(message) {
|
|
@@ -13306,14 +13543,14 @@ function refuseSystemFolder(targetRepo, platform6 = process.platform) {
|
|
|
13306
13543
|
function refuseInsideProject(targetRepo) {
|
|
13307
13544
|
let folder2 = realOrLiteral(targetRepo);
|
|
13308
13545
|
for (let parent = dirname5(folder2); parent !== folder2; folder2 = parent, parent = dirname5(parent)) {
|
|
13309
|
-
if (
|
|
13546
|
+
if (existsSync8(join11(parent, BUNDLE_PIN_REL))) {
|
|
13310
13547
|
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
13548
|
}
|
|
13312
13549
|
}
|
|
13313
13550
|
}
|
|
13314
13551
|
function realOrLiteral(folder2) {
|
|
13315
13552
|
try {
|
|
13316
|
-
return
|
|
13553
|
+
return realpathSync3(folder2);
|
|
13317
13554
|
} catch {
|
|
13318
13555
|
return folder2;
|
|
13319
13556
|
}
|
|
@@ -13330,22 +13567,22 @@ async function offerGitForWindows() {
|
|
|
13330
13567
|
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
13568
|
}
|
|
13332
13569
|
function gitBashPath(env) {
|
|
13333
|
-
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" &&
|
|
13570
|
+
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" && existsSync8(join11(dir, "git.exe"))).map((dir) => join11(dir, "..", "bin", "bash.exe"));
|
|
13334
13571
|
const candidates = [
|
|
13335
13572
|
env["CLAUDE_CODE_GIT_BASH_PATH"],
|
|
13336
13573
|
...onPath,
|
|
13337
|
-
env["ProgramFiles"] === void 0 ? void 0 :
|
|
13574
|
+
env["ProgramFiles"] === void 0 ? void 0 : join11(env["ProgramFiles"], "Git", "bin", "bash.exe")
|
|
13338
13575
|
];
|
|
13339
|
-
return candidates.find((file) => file !== void 0 && file !== "" &&
|
|
13576
|
+
return candidates.find((file) => file !== void 0 && file !== "" && existsSync8(file));
|
|
13340
13577
|
}
|
|
13341
13578
|
function hasGitBash(env) {
|
|
13342
13579
|
return gitBashPath(env) !== void 0;
|
|
13343
13580
|
}
|
|
13344
13581
|
function runWingetInstall() {
|
|
13345
|
-
return
|
|
13582
|
+
return spawnSync2("winget", [...GIT_INSTALL_ARGS], { stdio: "inherit" }).status === 0;
|
|
13346
13583
|
}
|
|
13347
13584
|
function unreadableProjectRefusal(targetRepo) {
|
|
13348
|
-
return `${
|
|
13585
|
+
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
13586
|
}
|
|
13350
13587
|
function cannotWriteRefusal(targetRepo) {
|
|
13351
13588
|
return `cannot write to ${targetRepo} \u2014 run "${ownBareCommand()}" in a folder you can write to.`;
|
|
@@ -13365,7 +13602,7 @@ function claimFolderId(targetRepo) {
|
|
|
13365
13602
|
function readFolderId(targetRepo) {
|
|
13366
13603
|
let folderId;
|
|
13367
13604
|
try {
|
|
13368
|
-
const text = readFileSync9(
|
|
13605
|
+
const text = readFileSync9(join11(targetRepo, PROJECT_IDENTITY_REL), "utf-8");
|
|
13369
13606
|
folderId = JSON.parse(text)?.projectId;
|
|
13370
13607
|
} catch {
|
|
13371
13608
|
return void 0;
|
|
@@ -13382,7 +13619,7 @@ function checkSettingsParse(targetRepo) {
|
|
|
13382
13619
|
}
|
|
13383
13620
|
|
|
13384
13621
|
// dist/folder-refusals.js
|
|
13385
|
-
import { realpathSync as
|
|
13622
|
+
import { realpathSync as realpathSync4 } from "node:fs";
|
|
13386
13623
|
import { homedir as homedir5 } from "node:os";
|
|
13387
13624
|
function folderMissingRefusal(targetRepo) {
|
|
13388
13625
|
return `halfcycle: no such directory: ${targetRepo}`;
|
|
@@ -13402,8 +13639,8 @@ function homeFolderRefusal(targetRepo) {
|
|
|
13402
13639
|
}
|
|
13403
13640
|
function isHomeFolder(dir) {
|
|
13404
13641
|
try {
|
|
13405
|
-
const target =
|
|
13406
|
-
const home =
|
|
13642
|
+
const target = realpathSync4(dir);
|
|
13643
|
+
const home = realpathSync4(homedir5());
|
|
13407
13644
|
return process.platform === "win32" ? target.toLowerCase() === home.toLowerCase() : target === home;
|
|
13408
13645
|
} catch {
|
|
13409
13646
|
return false;
|
|
@@ -13411,16 +13648,16 @@ function isHomeFolder(dir) {
|
|
|
13411
13648
|
}
|
|
13412
13649
|
|
|
13413
13650
|
// dist/preview.js
|
|
13414
|
-
import { accessSync, constants, existsSync as
|
|
13415
|
-
import { join as
|
|
13651
|
+
import { accessSync, constants, existsSync as existsSync10, readFileSync as readFileSync12, statSync } from "node:fs";
|
|
13652
|
+
import { join as join14 } from "node:path";
|
|
13416
13653
|
|
|
13417
13654
|
// dist/halfcycle-monorepo.js
|
|
13418
13655
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
13419
|
-
import { join as
|
|
13656
|
+
import { join as join12 } from "node:path";
|
|
13420
13657
|
var MONOREPO_PACKAGE_NAME = "halfcycle-monorepo";
|
|
13421
13658
|
function isHalfcycleMonorepo(dir) {
|
|
13422
13659
|
try {
|
|
13423
|
-
const pkg = JSON.parse(readFileSync10(
|
|
13660
|
+
const pkg = JSON.parse(readFileSync10(join12(dir, "package.json"), "utf-8"));
|
|
13424
13661
|
return pkg.name === MONOREPO_PACKAGE_NAME;
|
|
13425
13662
|
} catch {
|
|
13426
13663
|
return false;
|
|
@@ -13432,8 +13669,8 @@ function isSelfBuildInstallForm(engagementIdArg, engagementTypeArg) {
|
|
|
13432
13669
|
|
|
13433
13670
|
// dist/local-allow.js
|
|
13434
13671
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
13435
|
-
import { existsSync as
|
|
13436
|
-
import { dirname as dirname6, join as
|
|
13672
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
13673
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
13437
13674
|
var LOCAL_SETTINGS_REL2 = ".claude/settings.local.json";
|
|
13438
13675
|
var LOCAL_ALLOW_RECORD_REL = ".halfcycle/local-allow.json";
|
|
13439
13676
|
var LOCAL_ALLOW_EDIT_MODE = "acceptEdits";
|
|
@@ -13441,7 +13678,6 @@ var SCRIPTS = ["test", "build", "lint", "typecheck"];
|
|
|
13441
13678
|
var SCRIPT_RUNNERS = ["npm run", "pnpm", "pnpm run", "yarn", "bun run"];
|
|
13442
13679
|
var HALFCYCLE_TOOLS_RULE = `mcp__${MCP_SERVER_KEY}`;
|
|
13443
13680
|
var bash = (command) => `Bash(${command}:*)`;
|
|
13444
|
-
var CONTEXT_BUDGET_IGNORE_CHECK = "git check-ignore -q .halfcycle/context-budget.json";
|
|
13445
13681
|
var COMMIT_FILE_LISTING = "git ls-tree -r --name-only";
|
|
13446
13682
|
var INSTALLED_HOOK_SCRIPTS = Object.keys(OWNED_GENERATED_HEADERS).filter((path) => path.startsWith(".claude/hooks/"));
|
|
13447
13683
|
var LOCAL_ALLOW_RULES = [
|
|
@@ -13477,7 +13713,6 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13477
13713
|
bash("git merge"),
|
|
13478
13714
|
bash("git worktree add"),
|
|
13479
13715
|
bash("git worktree list"),
|
|
13480
|
-
`Bash(${CONTEXT_BUDGET_IGNORE_CHECK})`,
|
|
13481
13716
|
bash("git pull --ff-only"),
|
|
13482
13717
|
bash("git pull --rebase"),
|
|
13483
13718
|
bash("npm install"),
|
|
@@ -13489,6 +13724,8 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13489
13724
|
bash("yarn add"),
|
|
13490
13725
|
bash("bun install"),
|
|
13491
13726
|
bash("bun add"),
|
|
13727
|
+
bash("gh repo view"),
|
|
13728
|
+
bash("gh issue create"),
|
|
13492
13729
|
bash("go mod tidy"),
|
|
13493
13730
|
bash("go get"),
|
|
13494
13731
|
bash("cargo add"),
|
|
@@ -13528,7 +13765,7 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13528
13765
|
bash("tr"),
|
|
13529
13766
|
HALFCYCLE_TOOLS_RULE
|
|
13530
13767
|
];
|
|
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
|
|
13768
|
+
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
13769
|
var LOCAL_ALLOW_ON_LINE = `Your coding agent can now work without asking on this computer. To undo it, run: ${ownCommand()} allow --remove`;
|
|
13533
13770
|
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
13771
|
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 +13787,8 @@ function trackedByGit(repo) {
|
|
|
13550
13787
|
}
|
|
13551
13788
|
}
|
|
13552
13789
|
function readRecord2(repo) {
|
|
13553
|
-
const path =
|
|
13554
|
-
if (!
|
|
13790
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13791
|
+
if (!existsSync9(path))
|
|
13555
13792
|
return null;
|
|
13556
13793
|
try {
|
|
13557
13794
|
const raw = JSON.parse(readFileSync11(path, "utf8"));
|
|
@@ -13567,12 +13804,12 @@ function readRecord2(repo) {
|
|
|
13567
13804
|
}
|
|
13568
13805
|
}
|
|
13569
13806
|
function writeRecord(repo, record2) {
|
|
13570
|
-
const path =
|
|
13807
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13571
13808
|
mkdirSync7(dirname6(path), { recursive: true });
|
|
13572
13809
|
writeFileSync7(path, JSON.stringify({ rules: record2.rules, defaultMode: record2.defaultMode, before: record2.before }, null, 2) + "\n");
|
|
13573
13810
|
}
|
|
13574
13811
|
function holdsLocalAllowRecord(repo) {
|
|
13575
|
-
return
|
|
13812
|
+
return existsSync9(join13(repo, LOCAL_ALLOW_RECORD_REL));
|
|
13576
13813
|
}
|
|
13577
13814
|
function layoutOf(text) {
|
|
13578
13815
|
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -13587,8 +13824,8 @@ function render(value, like) {
|
|
|
13587
13824
|
function writeLocalAllow(repo) {
|
|
13588
13825
|
if (trackedByGit(repo))
|
|
13589
13826
|
return { kind: "tracked", line: LOCAL_ALLOW_TRACKED_LINE };
|
|
13590
|
-
const path =
|
|
13591
|
-
const existed =
|
|
13827
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13828
|
+
const existed = existsSync9(path);
|
|
13592
13829
|
const text = existed ? readFileSync11(path, "utf8") : null;
|
|
13593
13830
|
let settings = {};
|
|
13594
13831
|
if (text !== null) {
|
|
@@ -13631,14 +13868,14 @@ function writeLocalAllow(repo) {
|
|
|
13631
13868
|
}
|
|
13632
13869
|
function removeLocalAllow(repo) {
|
|
13633
13870
|
const record2 = readRecord2(repo);
|
|
13634
|
-
const recordPath =
|
|
13871
|
+
const recordPath = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13635
13872
|
if (record2 === null) {
|
|
13636
|
-
if (
|
|
13637
|
-
|
|
13873
|
+
if (existsSync9(recordPath))
|
|
13874
|
+
rmSync5(recordPath, { force: true });
|
|
13638
13875
|
return { kind: "nothing", line: LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE };
|
|
13639
13876
|
}
|
|
13640
|
-
const path =
|
|
13641
|
-
if (
|
|
13877
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13878
|
+
if (existsSync9(path)) {
|
|
13642
13879
|
const text = readFileSync11(path, "utf8");
|
|
13643
13880
|
let parsed = null;
|
|
13644
13881
|
try {
|
|
@@ -13673,13 +13910,13 @@ function removeLocalAllow(repo) {
|
|
|
13673
13910
|
if (record2.before !== null && original !== null && sameJson(next, original)) {
|
|
13674
13911
|
writeFileSync7(path, record2.before);
|
|
13675
13912
|
} else if (record2.before === null && Object.keys(next).length === 0) {
|
|
13676
|
-
|
|
13913
|
+
rmSync5(path, { force: true });
|
|
13677
13914
|
} else {
|
|
13678
13915
|
writeFileSync7(path, render(next, text));
|
|
13679
13916
|
}
|
|
13680
13917
|
}
|
|
13681
13918
|
}
|
|
13682
|
-
|
|
13919
|
+
rmSync5(recordPath, { force: true });
|
|
13683
13920
|
return { kind: "removed", line: LOCAL_ALLOW_REMOVED_LINE };
|
|
13684
13921
|
}
|
|
13685
13922
|
function parseObject(text) {
|
|
@@ -13693,21 +13930,21 @@ function parseObject(text) {
|
|
|
13693
13930
|
}
|
|
13694
13931
|
}
|
|
13695
13932
|
function sameJson(a, b) {
|
|
13696
|
-
return JSON.stringify(
|
|
13933
|
+
return JSON.stringify(canonical2(a)) === JSON.stringify(canonical2(b));
|
|
13697
13934
|
}
|
|
13698
|
-
function
|
|
13935
|
+
function canonical2(value) {
|
|
13699
13936
|
if (Array.isArray(value))
|
|
13700
|
-
return value.map(
|
|
13937
|
+
return value.map(canonical2);
|
|
13701
13938
|
if (isObject2(value)) {
|
|
13702
|
-
return Object.fromEntries(Object.keys(value).sort().map((key) => [key,
|
|
13939
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical2(value[key])]));
|
|
13703
13940
|
}
|
|
13704
13941
|
return value;
|
|
13705
13942
|
}
|
|
13706
13943
|
function shouldAskLocalAllow(repo, interactive) {
|
|
13707
13944
|
if (!interactive || holdsLocalAllowRecord(repo))
|
|
13708
13945
|
return false;
|
|
13709
|
-
const path =
|
|
13710
|
-
if (!
|
|
13946
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13947
|
+
if (!existsSync9(path))
|
|
13711
13948
|
return true;
|
|
13712
13949
|
const parsed = parseObject(readFileSync11(path, "utf8"));
|
|
13713
13950
|
const allow = isObject2(parsed?.["permissions"]) ? parsed["permissions"]["allow"] : void 0;
|
|
@@ -13728,8 +13965,8 @@ function localAllowClosingLine(repo, said) {
|
|
|
13728
13965
|
var ENABLED_SERVERS_KEY = "enabledMcpjsonServers";
|
|
13729
13966
|
var DISABLED_SERVERS_KEY = "disabledMcpjsonServers";
|
|
13730
13967
|
function registersOwnServer(repo) {
|
|
13731
|
-
const path =
|
|
13732
|
-
const registration = parseObject(
|
|
13968
|
+
const path = join13(repo, MCP_REGISTRATION_REL);
|
|
13969
|
+
const registration = parseObject(existsSync9(path) ? readFileSync11(path, "utf8") : null);
|
|
13733
13970
|
const servers = registration?.["mcpServers"];
|
|
13734
13971
|
return isObject2(servers) && isObject2(servers[MCP_SERVER_KEY]);
|
|
13735
13972
|
}
|
|
@@ -13739,15 +13976,15 @@ function stringsIn(value) {
|
|
|
13739
13976
|
function approveOwnServer(repo) {
|
|
13740
13977
|
const plan = planOwnServerApproval(repo, registersOwnServer(repo));
|
|
13741
13978
|
if (plan.after !== null) {
|
|
13742
|
-
const path =
|
|
13979
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13743
13980
|
mkdirSync7(dirname6(path), { recursive: true });
|
|
13744
13981
|
writeFileSync7(path, plan.after);
|
|
13745
13982
|
}
|
|
13746
13983
|
return plan.outcome;
|
|
13747
13984
|
}
|
|
13748
13985
|
function planOwnServerApproval(repo, registered) {
|
|
13749
|
-
const path =
|
|
13750
|
-
const text =
|
|
13986
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13987
|
+
const text = existsSync9(path) ? readFileSync11(path, "utf8") : null;
|
|
13751
13988
|
const unchanged = (outcome) => ({ outcome, before: text, after: null });
|
|
13752
13989
|
if (!registered)
|
|
13753
13990
|
return unchanged("no-server");
|
|
@@ -13783,8 +14020,8 @@ function planOwnServerApproval(repo, registered) {
|
|
|
13783
14020
|
return { outcome: "approved", before: text, after: after === text ? null : after };
|
|
13784
14021
|
}
|
|
13785
14022
|
function removeOwnServerApproval(repo) {
|
|
13786
|
-
const path =
|
|
13787
|
-
if (!
|
|
14023
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
14024
|
+
if (!existsSync9(path) || trackedByGit(repo))
|
|
13788
14025
|
return false;
|
|
13789
14026
|
const text = readFileSync11(path, "utf8");
|
|
13790
14027
|
const settings = parseObject(text);
|
|
@@ -13818,7 +14055,7 @@ function removeOwnServerApproval(repo) {
|
|
|
13818
14055
|
if (!changed)
|
|
13819
14056
|
return false;
|
|
13820
14057
|
if (Object.keys(next).length === 0)
|
|
13821
|
-
|
|
14058
|
+
rmSync5(path, { force: true });
|
|
13822
14059
|
else
|
|
13823
14060
|
writeFileSync7(path, render(next, text));
|
|
13824
14061
|
return true;
|
|
@@ -13879,8 +14116,8 @@ function ownServerRow(targetRepo, plan) {
|
|
|
13879
14116
|
}
|
|
13880
14117
|
}
|
|
13881
14118
|
function claudeMdLine(targetRepo) {
|
|
13882
|
-
const path =
|
|
13883
|
-
if (!
|
|
14119
|
+
const path = join14(targetRepo, CLAUDE_MD_REL);
|
|
14120
|
+
if (!existsSync10(path))
|
|
13884
14121
|
return void 0;
|
|
13885
14122
|
let text;
|
|
13886
14123
|
try {
|
|
@@ -13891,7 +14128,7 @@ function claudeMdLine(targetRepo) {
|
|
|
13891
14128
|
return text.includes(AGENTS_IMPORT_LINE) ? WORDS.claudeMdAlreadyPoints : WORDS.claudeMdWillAdd;
|
|
13892
14129
|
}
|
|
13893
14130
|
function previewRefusal(targetRepo, options) {
|
|
13894
|
-
if (!
|
|
14131
|
+
if (!existsSync10(targetRepo))
|
|
13895
14132
|
return folderMissingRefusal(targetRepo);
|
|
13896
14133
|
if (!statSync(targetRepo).isDirectory())
|
|
13897
14134
|
return folderIsFileRefusal(targetRepo);
|
|
@@ -13912,7 +14149,7 @@ function previewRefusal(targetRepo, options) {
|
|
|
13912
14149
|
} catch {
|
|
13913
14150
|
return cannotWriteRefusal(targetRepo);
|
|
13914
14151
|
}
|
|
13915
|
-
if (
|
|
14152
|
+
if (existsSync10(join14(targetRepo, PROJECT_IDENTITY_REL)) && readFolderId(targetRepo) === void 0) {
|
|
13916
14153
|
return unreadableProjectRefusal(targetRepo);
|
|
13917
14154
|
}
|
|
13918
14155
|
try {
|
|
@@ -13972,10 +14209,10 @@ function renderPreview(plan, missingGitForWindows, claudeMdWords, ownServer) {
|
|
|
13972
14209
|
}
|
|
13973
14210
|
|
|
13974
14211
|
// dist/doctor.js
|
|
13975
|
-
import { spawnSync as
|
|
13976
|
-
import { existsSync as
|
|
14212
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
14213
|
+
import { existsSync as existsSync11, lstatSync as lstatSync3, readFileSync as readFileSync13, realpathSync as realpathSync5, statSync as statSync2 } from "node:fs";
|
|
13977
14214
|
import { homedir as homedir6 } from "node:os";
|
|
13978
|
-
import { join as
|
|
14215
|
+
import { join as join15 } from "node:path";
|
|
13979
14216
|
var GUARD_RUNNER_REL2 = ".claude/hooks/guard-runner.sh";
|
|
13980
14217
|
var CONNECTION_VERB = "check-connection";
|
|
13981
14218
|
var WRAPPER_TIMEOUT_MS = 3e4;
|
|
@@ -14014,7 +14251,7 @@ function runDoctor(root, options) {
|
|
|
14014
14251
|
};
|
|
14015
14252
|
}
|
|
14016
14253
|
function setUpLine(root, upToDate) {
|
|
14017
|
-
if (!
|
|
14254
|
+
if (!existsSync11(join15(root, BUNDLE_PIN_REL))) {
|
|
14018
14255
|
return {
|
|
14019
14256
|
state: "no",
|
|
14020
14257
|
hooksMissing: false,
|
|
@@ -14028,9 +14265,9 @@ function setUpLine(root, upToDate) {
|
|
|
14028
14265
|
const missing = [];
|
|
14029
14266
|
if (!pinNamesProject(root))
|
|
14030
14267
|
missing.push("Halfcycle's project record");
|
|
14031
|
-
if (!
|
|
14268
|
+
if (!existsSync11(join15(root, INSTALL_MANIFEST_REL)))
|
|
14032
14269
|
missing.push("the install record");
|
|
14033
|
-
if (!
|
|
14270
|
+
if (!existsSync11(join15(root, VENDORED_BIN_REL)))
|
|
14034
14271
|
missing.push("Halfcycle's check program");
|
|
14035
14272
|
if (!mcpNamesServer(root))
|
|
14036
14273
|
missing.push("Halfcycle's server entry");
|
|
@@ -14056,14 +14293,14 @@ function pinNamesProject(root) {
|
|
|
14056
14293
|
}
|
|
14057
14294
|
}
|
|
14058
14295
|
function mcpNamesServer(root) {
|
|
14059
|
-
const parsed = readJson(
|
|
14296
|
+
const parsed = readJson(join15(root, MCP_REGISTRATION_REL));
|
|
14060
14297
|
if (!isObject3(parsed) || !isObject3(parsed["mcpServers"]))
|
|
14061
14298
|
return false;
|
|
14062
14299
|
const entry = parsed["mcpServers"][MCP_SERVER_KEY];
|
|
14063
14300
|
return isObject3(entry) && typeof entry["url"] === "string";
|
|
14064
14301
|
}
|
|
14065
14302
|
function settingsRegisterEveryCommand(root) {
|
|
14066
|
-
const parsed = readJson(
|
|
14303
|
+
const parsed = readJson(join15(root, SETTINGS_REL));
|
|
14067
14304
|
if (!isObject3(parsed) || !isObject3(parsed["hooks"]))
|
|
14068
14305
|
return false;
|
|
14069
14306
|
const registered = /* @__PURE__ */ new Set();
|
|
@@ -14094,10 +14331,10 @@ function startWrapper(root, options) {
|
|
|
14094
14331
|
}
|
|
14095
14332
|
shell = gitBash;
|
|
14096
14333
|
}
|
|
14097
|
-
const wrapper =
|
|
14098
|
-
if (!
|
|
14334
|
+
const wrapper = join15(root, GUARD_RUNNER_REL2);
|
|
14335
|
+
if (!existsSync11(wrapper))
|
|
14099
14336
|
return failed(repairSentence());
|
|
14100
|
-
const run =
|
|
14337
|
+
const run = spawnSync3(shell, [wrapper, CONNECTION_VERB], {
|
|
14101
14338
|
cwd: root,
|
|
14102
14339
|
env: { ...options.env, CLAUDE_PROJECT_DIR: root },
|
|
14103
14340
|
encoding: "utf-8",
|
|
@@ -14122,7 +14359,7 @@ function startWrapper(root, options) {
|
|
|
14122
14359
|
}
|
|
14123
14360
|
function settingsDisablingHooks(root) {
|
|
14124
14361
|
for (const rel of [SETTINGS_REL, LOCAL_SETTINGS_REL]) {
|
|
14125
|
-
const parsed = readJson(
|
|
14362
|
+
const parsed = readJson(join15(root, rel));
|
|
14126
14363
|
if (isObject3(parsed) && parsed["disableAllHooks"] === true)
|
|
14127
14364
|
return rel;
|
|
14128
14365
|
}
|
|
@@ -14212,8 +14449,8 @@ function folderTrustLine(root, options) {
|
|
|
14212
14449
|
if (!mcpNamesServer(root))
|
|
14213
14450
|
return void 0;
|
|
14214
14451
|
const configFolder = options.env["CLAUDE_CONFIG_DIR"];
|
|
14215
|
-
const stateFile =
|
|
14216
|
-
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync:
|
|
14452
|
+
const stateFile = join15(configFolder !== void 0 && configFolder !== "" ? configFolder : options.home ?? homedir6(), CLAUDE_STATE_FILENAME);
|
|
14453
|
+
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync3, readFileSync: readFileSync13, realpathSync: realpathSync5 });
|
|
14217
14454
|
return trust === "not-trusted" ? { ok: true, text: `Folder trust: ${FOLDER_NOT_TRUSTED_SENTENCE}` } : void 0;
|
|
14218
14455
|
}
|
|
14219
14456
|
function signInCodeLine(root, running, now, options) {
|
|
@@ -14287,7 +14524,7 @@ var CODEX_EVENT_SNAKE = {
|
|
|
14287
14524
|
};
|
|
14288
14525
|
function codexHomeDir(options) {
|
|
14289
14526
|
const codexHome = options.env["CODEX_HOME"];
|
|
14290
|
-
return codexHome !== void 0 && codexHome !== "" ? codexHome :
|
|
14527
|
+
return codexHome !== void 0 && codexHome !== "" ? codexHome : join15(homedir6(), ".codex");
|
|
14291
14528
|
}
|
|
14292
14529
|
function isDirectory(path) {
|
|
14293
14530
|
try {
|
|
@@ -14297,8 +14534,8 @@ function isDirectory(path) {
|
|
|
14297
14534
|
}
|
|
14298
14535
|
}
|
|
14299
14536
|
function codexLine(root, options) {
|
|
14300
|
-
const hooksPath =
|
|
14301
|
-
if (!
|
|
14537
|
+
const hooksPath = join15(root, CODEX_HOOKS_REL2);
|
|
14538
|
+
if (!existsSync11(hooksPath))
|
|
14302
14539
|
return void 0;
|
|
14303
14540
|
if (!isDirectory(codexHomeDir(options)))
|
|
14304
14541
|
return void 0;
|
|
@@ -14340,7 +14577,7 @@ function codexEntryPositions(hooksPath) {
|
|
|
14340
14577
|
return positions;
|
|
14341
14578
|
}
|
|
14342
14579
|
function codexApprovalStatus(root, positions, options) {
|
|
14343
|
-
const configPath =
|
|
14580
|
+
const configPath = join15(codexHomeDir(options), "config.toml");
|
|
14344
14581
|
let text;
|
|
14345
14582
|
try {
|
|
14346
14583
|
text = readFileSync13(configPath, "utf-8");
|
|
@@ -14349,7 +14586,7 @@ function codexApprovalStatus(root, positions, options) {
|
|
|
14349
14586
|
}
|
|
14350
14587
|
let hooksPath;
|
|
14351
14588
|
try {
|
|
14352
|
-
hooksPath =
|
|
14589
|
+
hooksPath = join15(realpathSync5(root), CODEX_HOOKS_REL2);
|
|
14353
14590
|
} catch {
|
|
14354
14591
|
return "not-approved";
|
|
14355
14592
|
}
|
|
@@ -14379,7 +14616,7 @@ function naturalList(items) {
|
|
|
14379
14616
|
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
14380
14617
|
}
|
|
14381
14618
|
function readJson(path) {
|
|
14382
|
-
if (!
|
|
14619
|
+
if (!existsSync11(path))
|
|
14383
14620
|
return void 0;
|
|
14384
14621
|
try {
|
|
14385
14622
|
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
@@ -14393,8 +14630,8 @@ function isObject3(value) {
|
|
|
14393
14630
|
|
|
14394
14631
|
// dist/claude-version-probe.js
|
|
14395
14632
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
14396
|
-
import { existsSync as
|
|
14397
|
-
import { delimiter as delimiter2, join as
|
|
14633
|
+
import { existsSync as existsSync12, realpathSync as realpathSync6 } from "node:fs";
|
|
14634
|
+
import { delimiter as delimiter2, join as join16 } from "node:path";
|
|
14398
14635
|
function execVersionCommand(command, timeoutMs, deps) {
|
|
14399
14636
|
const platform6 = deps.platform ?? process.platform;
|
|
14400
14637
|
const exec = deps.exec ?? execFileSync4;
|
|
@@ -14437,10 +14674,10 @@ var INSTALL_METHOD_RULES = [
|
|
|
14437
14674
|
function probeClaudeCodeInstallMethod(deps = {}) {
|
|
14438
14675
|
const env = deps.env ?? process.env;
|
|
14439
14676
|
const platform6 = deps.platform ?? process.platform;
|
|
14440
|
-
const exists = deps.exists ??
|
|
14677
|
+
const exists = deps.exists ?? existsSync12;
|
|
14441
14678
|
const realpath = deps.realpath ?? ((file) => {
|
|
14442
14679
|
try {
|
|
14443
|
-
return
|
|
14680
|
+
return realpathSync6(file);
|
|
14444
14681
|
} catch {
|
|
14445
14682
|
return file;
|
|
14446
14683
|
}
|
|
@@ -14449,7 +14686,7 @@ function probeClaudeCodeInstallMethod(deps = {}) {
|
|
|
14449
14686
|
const dirs = (env["PATH"] ?? "").split(delimiter2).filter((dir) => dir !== "");
|
|
14450
14687
|
for (const dir of dirs) {
|
|
14451
14688
|
for (const name of names) {
|
|
14452
|
-
const file =
|
|
14689
|
+
const file = join16(dir, name);
|
|
14453
14690
|
if (!exists(file))
|
|
14454
14691
|
continue;
|
|
14455
14692
|
const real = realpath(file);
|
|
@@ -14484,8 +14721,8 @@ function codexInstallLine(codex) {
|
|
|
14484
14721
|
}
|
|
14485
14722
|
|
|
14486
14723
|
// dist/build-record/write.js
|
|
14487
|
-
import { existsSync as
|
|
14488
|
-
import { basename as basename2, join as
|
|
14724
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, rmSync as rmSync7 } from "node:fs";
|
|
14725
|
+
import { basename as basename2, join as join17, resolve as resolve4, relative as relative3, isAbsolute as isAbsolute4 } from "node:path";
|
|
14489
14726
|
|
|
14490
14727
|
// dist/build-record/render-v2.js
|
|
14491
14728
|
var HIDDEN = /[\p{Cc}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/u;
|
|
@@ -14831,6 +15068,20 @@ function leftForYouLines(left, p) {
|
|
|
14831
15068
|
}
|
|
14832
15069
|
return lines;
|
|
14833
15070
|
}
|
|
15071
|
+
var TASKS_FILE_LINK = "../tasks.md";
|
|
15072
|
+
function ticketLines(tickets, p) {
|
|
15073
|
+
if (tickets === void 0)
|
|
15074
|
+
return [];
|
|
15075
|
+
const lines = ["- Tickets:"];
|
|
15076
|
+
for (const entry of tickets.entries) {
|
|
15077
|
+
lines.push(` - ${entry.ref} ${p.text(entry.title)}`);
|
|
15078
|
+
if (entry.url !== null)
|
|
15079
|
+
lines.push(` - Link: [${entry.ref}](${entry.url})`);
|
|
15080
|
+
else if (entry.ref === TASKS_FILE_PATH)
|
|
15081
|
+
lines.push(` - Link: [${entry.ref}](${TASKS_FILE_LINK})`);
|
|
15082
|
+
}
|
|
15083
|
+
return lines;
|
|
15084
|
+
}
|
|
14834
15085
|
function shownBy(steps) {
|
|
14835
15086
|
const named2 = steps.filter((s) => s.name !== void 0).length;
|
|
14836
15087
|
if (named2 === 0)
|
|
@@ -14904,6 +15155,7 @@ function renderClose(heading, observed, attested, checks, p, closingRound = fals
|
|
|
14904
15155
|
`- Signed in as: ${signedInAs(observed.closedBy, p)}`,
|
|
14905
15156
|
...outstanding(observed.crossed, p),
|
|
14906
15157
|
...leftForYouLines(observed.leftForOwner, p),
|
|
15158
|
+
...ticketLines(observed.tickets, p),
|
|
14907
15159
|
machines(observed.seatCoverage, checks)
|
|
14908
15160
|
].join("\n"),
|
|
14909
15161
|
STATED_LABEL,
|
|
@@ -15076,7 +15328,7 @@ function renderBuildRecordV2Markdown(record2) {
|
|
|
15076
15328
|
}
|
|
15077
15329
|
|
|
15078
15330
|
// dist/build-record/pair-io.js
|
|
15079
|
-
import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as
|
|
15331
|
+
import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync6, writeSync as writeSync2 } from "node:fs";
|
|
15080
15332
|
function createExclusiveOnDisk(path, data) {
|
|
15081
15333
|
const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
15082
15334
|
const fd = openSync(path, "wx");
|
|
@@ -15087,7 +15339,7 @@ function createExclusiveOnDisk(path, data) {
|
|
|
15087
15339
|
}
|
|
15088
15340
|
} catch (err) {
|
|
15089
15341
|
closeSync(fd);
|
|
15090
|
-
|
|
15342
|
+
rmSync6(path, { force: true });
|
|
15091
15343
|
throw err;
|
|
15092
15344
|
}
|
|
15093
15345
|
closeSync(fd);
|
|
@@ -15121,28 +15373,28 @@ function createPairExclusive(io, jsonPath, jsonText, mdPath, mdText) {
|
|
|
15121
15373
|
return "created";
|
|
15122
15374
|
} catch (err) {
|
|
15123
15375
|
if (createdJson)
|
|
15124
|
-
|
|
15376
|
+
rmSync6(jsonPath, { force: true });
|
|
15125
15377
|
if (!isEexist(err))
|
|
15126
15378
|
throw err;
|
|
15127
15379
|
return "exists";
|
|
15128
15380
|
}
|
|
15129
15381
|
}
|
|
15130
15382
|
function replaceOne(io, tmp, finalPath, text) {
|
|
15131
|
-
|
|
15383
|
+
rmSync6(tmp, { force: true });
|
|
15132
15384
|
try {
|
|
15133
15385
|
io.createExclusive(tmp, text);
|
|
15134
15386
|
io.rename(tmp, finalPath);
|
|
15135
15387
|
} catch (err) {
|
|
15136
|
-
|
|
15388
|
+
rmSync6(tmp, { force: true });
|
|
15137
15389
|
throw err;
|
|
15138
15390
|
}
|
|
15139
15391
|
}
|
|
15140
15392
|
|
|
15141
15393
|
// dist/build-record/write.js
|
|
15142
|
-
var BUILD_RECORD_DIR =
|
|
15394
|
+
var BUILD_RECORD_DIR = join17("docs", "build-records");
|
|
15143
15395
|
function assertInsideOutDir(outDir, candidate, identity) {
|
|
15144
|
-
const rel =
|
|
15145
|
-
const isPlainFilename = rel !== "" && rel !== ".." && !
|
|
15396
|
+
const rel = relative3(outDir, resolve4(candidate));
|
|
15397
|
+
const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute4(rel) && rel === basename2(rel);
|
|
15146
15398
|
if (!isPlainFilename) {
|
|
15147
15399
|
throw new InvalidPhaseIdentityError(identity, BUILD_RECORD_DIR);
|
|
15148
15400
|
}
|
|
@@ -15175,10 +15427,10 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15175
15427
|
if (!isValidPhaseIdentity(phase)) {
|
|
15176
15428
|
throw new InvalidPhaseIdentityError(phase, BUILD_RECORD_DIR);
|
|
15177
15429
|
}
|
|
15178
|
-
const outDir =
|
|
15430
|
+
const outDir = resolve4(repoRoot, BUILD_RECORD_DIR);
|
|
15179
15431
|
const segment = renderPhaseSegment(phase);
|
|
15180
|
-
const jsonPath =
|
|
15181
|
-
const mdPath =
|
|
15432
|
+
const jsonPath = join17(outDir, `${segment}.json`);
|
|
15433
|
+
const mdPath = join17(outDir, `${segment}.md`);
|
|
15182
15434
|
const jsonTmp = `${jsonPath}.tmp`;
|
|
15183
15435
|
const mdTmp = `${mdPath}.tmp`;
|
|
15184
15436
|
for (const candidate of [jsonPath, mdPath, jsonTmp, mdTmp]) {
|
|
@@ -15192,7 +15444,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15192
15444
|
}
|
|
15193
15445
|
const existingJson = readIfPresent(jsonPath);
|
|
15194
15446
|
if (existingJson === null) {
|
|
15195
|
-
return { kind: "in-the-way", path:
|
|
15447
|
+
return { kind: "in-the-way", path: existsSync13(jsonPath) ? jsonPath : mdPath };
|
|
15196
15448
|
}
|
|
15197
15449
|
if (existingJson === jsonText) {
|
|
15198
15450
|
replaceOne(io, mdTmp, mdPath, mdText);
|
|
@@ -15208,7 +15460,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15208
15460
|
try {
|
|
15209
15461
|
replaceOne(io, jsonTmp, jsonPath, existingJson);
|
|
15210
15462
|
} catch {
|
|
15211
|
-
|
|
15463
|
+
rmSync7(jsonPath, { force: true });
|
|
15212
15464
|
}
|
|
15213
15465
|
throw err;
|
|
15214
15466
|
}
|
|
@@ -15216,24 +15468,24 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
15216
15468
|
}
|
|
15217
15469
|
|
|
15218
15470
|
// dist/build-record/obtain.js
|
|
15219
|
-
import { relative as
|
|
15471
|
+
import { relative as relative6 } from "node:path";
|
|
15220
15472
|
|
|
15221
15473
|
// dist/open-phase.js
|
|
15222
15474
|
import { readFileSync as readFileSync20 } from "node:fs";
|
|
15223
|
-
import { join as
|
|
15475
|
+
import { join as join25 } from "node:path";
|
|
15224
15476
|
|
|
15225
15477
|
// dist/close-phase.js
|
|
15226
15478
|
import { execFile, execFileSync as execFileSync6 } from "node:child_process";
|
|
15227
15479
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
15228
|
-
import { join as
|
|
15480
|
+
import { join as join24 } from "node:path";
|
|
15229
15481
|
|
|
15230
15482
|
// dist/plan.js
|
|
15231
|
-
import { existsSync as
|
|
15232
|
-
import { join as
|
|
15483
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18 } from "node:fs";
|
|
15484
|
+
import { join as join23 } from "node:path";
|
|
15233
15485
|
|
|
15234
15486
|
// dist/draw/write.js
|
|
15235
|
-
import { closeSync as closeSync2, lstatSync as
|
|
15236
|
-
import { dirname as dirname7, isAbsolute as
|
|
15487
|
+
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";
|
|
15488
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join18, relative as relative4, resolve as resolve5, sep as sep2 } from "node:path";
|
|
15237
15489
|
var DOCS_DIR = "docs";
|
|
15238
15490
|
var DIAGRAMS_DIR = "diagrams";
|
|
15239
15491
|
var DIAGRAM_DIR_REL = `${DOCS_DIR}/${DIAGRAMS_DIR}`;
|
|
@@ -15288,10 +15540,10 @@ function embedImageLink(base, doc) {
|
|
|
15288
15540
|
return `${up}${DIAGRAMS_DIR}/${markdownLinkName(embedImageName(base))}`;
|
|
15289
15541
|
}
|
|
15290
15542
|
function diagramPaths(repoRoot, base) {
|
|
15291
|
-
const root =
|
|
15292
|
-
const dir =
|
|
15543
|
+
const root = resolve5(repoRoot);
|
|
15544
|
+
const dir = join18(root, DOCS_DIR, DIAGRAMS_DIR);
|
|
15293
15545
|
const names = fileNames(base);
|
|
15294
|
-
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which,
|
|
15546
|
+
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which, join18(dir, names[which])]));
|
|
15295
15547
|
return { repoRoot: root, dir, file };
|
|
15296
15548
|
}
|
|
15297
15549
|
function diagramRel(base, which) {
|
|
@@ -15308,19 +15560,19 @@ function spokenList(items) {
|
|
|
15308
15560
|
}
|
|
15309
15561
|
function lstatOrNull2(path) {
|
|
15310
15562
|
try {
|
|
15311
|
-
return
|
|
15563
|
+
return lstatSync4(path);
|
|
15312
15564
|
} catch {
|
|
15313
15565
|
return null;
|
|
15314
15566
|
}
|
|
15315
15567
|
}
|
|
15316
|
-
function
|
|
15317
|
-
const rel =
|
|
15318
|
-
return rel === "" || !
|
|
15568
|
+
function isInside2(outer, inner) {
|
|
15569
|
+
const rel = relative4(outer, inner);
|
|
15570
|
+
return rel === "" || !isAbsolute5(rel) && rel !== ".." && !rel.startsWith(`..${sep2}`);
|
|
15319
15571
|
}
|
|
15320
15572
|
function checkDiagramPlace(paths) {
|
|
15321
15573
|
let root;
|
|
15322
15574
|
try {
|
|
15323
|
-
root =
|
|
15575
|
+
root = realpathSync7(paths.repoRoot);
|
|
15324
15576
|
} catch {
|
|
15325
15577
|
return { ok: false, why: `the folder ${paths.repoRoot} could not be opened. Check the path, then run this again.` };
|
|
15326
15578
|
}
|
|
@@ -15333,14 +15585,14 @@ function checkDiagramPlace(paths) {
|
|
|
15333
15585
|
}
|
|
15334
15586
|
let real;
|
|
15335
15587
|
try {
|
|
15336
|
-
real =
|
|
15588
|
+
real = realpathSync7(ancestor);
|
|
15337
15589
|
} catch {
|
|
15338
15590
|
return {
|
|
15339
15591
|
ok: false,
|
|
15340
15592
|
why: `${DIAGRAM_DIR_REL} leads to a place that does not exist. Remove the link in the way, then run this again.`
|
|
15341
15593
|
};
|
|
15342
15594
|
}
|
|
15343
|
-
if (!
|
|
15595
|
+
if (!isInside2(root, real)) {
|
|
15344
15596
|
return {
|
|
15345
15597
|
ok: false,
|
|
15346
15598
|
why: `${DIAGRAM_DIR_REL} leads outside this repository, so nothing was written. Remove the link in the way, then run this again.`
|
|
@@ -15348,12 +15600,12 @@ function checkDiagramPlace(paths) {
|
|
|
15348
15600
|
}
|
|
15349
15601
|
let isFolder = false;
|
|
15350
15602
|
try {
|
|
15351
|
-
isFolder =
|
|
15603
|
+
isFolder = lstatSync4(real).isDirectory();
|
|
15352
15604
|
} catch {
|
|
15353
15605
|
isFolder = false;
|
|
15354
15606
|
}
|
|
15355
15607
|
if (!isFolder) {
|
|
15356
|
-
const name =
|
|
15608
|
+
const name = relative4(root, real).split("\\").join("/");
|
|
15357
15609
|
return {
|
|
15358
15610
|
ok: false,
|
|
15359
15611
|
why: `${name === "" ? "this repository" : name} is not a folder, so ${DIAGRAM_DIR_REL} cannot be made. Move it, then run this again.`
|
|
@@ -15468,7 +15720,7 @@ function checkPictures(drawn, base) {
|
|
|
15468
15720
|
}
|
|
15469
15721
|
function removeQuietly(path) {
|
|
15470
15722
|
try {
|
|
15471
|
-
|
|
15723
|
+
rmSync8(path, { force: true });
|
|
15472
15724
|
} catch {
|
|
15473
15725
|
return;
|
|
15474
15726
|
}
|
|
@@ -15534,10 +15786,10 @@ function restore(io, paths, renamed, existing) {
|
|
|
15534
15786
|
const tmp = tempPath(paths, which);
|
|
15535
15787
|
try {
|
|
15536
15788
|
if (previous === null) {
|
|
15537
|
-
|
|
15789
|
+
rmSync8(finalPath, { force: true });
|
|
15538
15790
|
continue;
|
|
15539
15791
|
}
|
|
15540
|
-
|
|
15792
|
+
rmSync8(tmp, { force: true });
|
|
15541
15793
|
io.createExclusive(tmp, previous);
|
|
15542
15794
|
io.rename(tmp, finalPath);
|
|
15543
15795
|
} catch {
|
|
@@ -15564,7 +15816,7 @@ function isMissingRoute(method, status, body) {
|
|
|
15564
15816
|
}
|
|
15565
15817
|
|
|
15566
15818
|
// dist/change.js
|
|
15567
|
-
import { join as
|
|
15819
|
+
import { join as join21 } from "node:path";
|
|
15568
15820
|
|
|
15569
15821
|
// dist/cli-contract.js
|
|
15570
15822
|
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 +16061,11 @@ function placeholderRefusal(verb, tokens) {
|
|
|
15809
16061
|
// dist/system-report.js
|
|
15810
16062
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
15811
16063
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
15812
|
-
import { join as
|
|
16064
|
+
import { join as join19 } from "node:path";
|
|
15813
16065
|
function readDescriptionFile(repoRoot) {
|
|
15814
16066
|
let text;
|
|
15815
16067
|
try {
|
|
15816
|
-
text = readFileSync16(
|
|
16068
|
+
text = readFileSync16(join19(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8");
|
|
15817
16069
|
} catch (err) {
|
|
15818
16070
|
const code = err.code;
|
|
15819
16071
|
return code === "ENOENT" || code === "ENOTDIR" ? { kind: "missing" } : { kind: "unreadable" };
|
|
@@ -16037,8 +16289,8 @@ async function runSystem(repoRoot, deps) {
|
|
|
16037
16289
|
}
|
|
16038
16290
|
|
|
16039
16291
|
// dist/build-record/write-change.js
|
|
16040
|
-
import { existsSync as
|
|
16041
|
-
import { basename as basename3, isAbsolute as
|
|
16292
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10 } from "node:fs";
|
|
16293
|
+
import { basename as basename3, isAbsolute as isAbsolute6, join as join20, relative as relative5, resolve as resolve6 } from "node:path";
|
|
16042
16294
|
|
|
16043
16295
|
// dist/build-record/render-change.js
|
|
16044
16296
|
function counted2(n, one, many) {
|
|
@@ -16183,7 +16435,7 @@ function renderChangeRecordMarkdown(record2) {
|
|
|
16183
16435
|
}
|
|
16184
16436
|
|
|
16185
16437
|
// dist/build-record/write-change.js
|
|
16186
|
-
var CHANGE_RECORD_DIR =
|
|
16438
|
+
var CHANGE_RECORD_DIR = join20("docs", "build-records", "changes");
|
|
16187
16439
|
var InvalidChangeIdError = class extends Error {
|
|
16188
16440
|
id;
|
|
16189
16441
|
constructor(id) {
|
|
@@ -16193,8 +16445,8 @@ var InvalidChangeIdError = class extends Error {
|
|
|
16193
16445
|
}
|
|
16194
16446
|
};
|
|
16195
16447
|
function assertInsideChangeDir(outDir, candidate, id) {
|
|
16196
|
-
const rel =
|
|
16197
|
-
const isPlainFilename = rel !== "" && rel !== ".." && !
|
|
16448
|
+
const rel = relative5(outDir, resolve6(candidate));
|
|
16449
|
+
const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute6(rel) && rel === basename3(rel);
|
|
16198
16450
|
if (!isPlainFilename)
|
|
16199
16451
|
throw new InvalidChangeIdError(id);
|
|
16200
16452
|
}
|
|
@@ -16202,9 +16454,9 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
16202
16454
|
const checked = sortingIdSchema.safeParse(sortingId);
|
|
16203
16455
|
if (!checked.success)
|
|
16204
16456
|
throw new InvalidChangeIdError(sortingId);
|
|
16205
|
-
const outDir =
|
|
16206
|
-
const jsonPath =
|
|
16207
|
-
const mdPath =
|
|
16457
|
+
const outDir = resolve6(repoRoot, CHANGE_RECORD_DIR);
|
|
16458
|
+
const jsonPath = join20(outDir, `${checked.data}.json`);
|
|
16459
|
+
const mdPath = join20(outDir, `${checked.data}.md`);
|
|
16208
16460
|
const mdTmp = `${mdPath}.tmp`;
|
|
16209
16461
|
for (const candidate of [jsonPath, mdPath, mdTmp]) {
|
|
16210
16462
|
assertInsideChangeDir(outDir, candidate, sortingId);
|
|
@@ -16217,7 +16469,7 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
16217
16469
|
}
|
|
16218
16470
|
const existingJson = readIfPresent(jsonPath);
|
|
16219
16471
|
if (existingJson === null) {
|
|
16220
|
-
return { kind: "in-the-way", path:
|
|
16472
|
+
return { kind: "in-the-way", path: existsSync14(jsonPath) ? jsonPath : mdPath };
|
|
16221
16473
|
}
|
|
16222
16474
|
if (existingJson !== jsonText)
|
|
16223
16475
|
return { kind: "in-the-way", path: jsonPath };
|
|
@@ -16480,7 +16732,7 @@ function writePairLines(deps, repoRoot, sortingId, found) {
|
|
|
16480
16732
|
};
|
|
16481
16733
|
}
|
|
16482
16734
|
return {
|
|
16483
|
-
stdout: `[halfcycle] Its record is in ${
|
|
16735
|
+
stdout: `[halfcycle] Its record is in ${join21(CHANGE_RECORD_DIR, `${sortingId}.md`)}.
|
|
16484
16736
|
`,
|
|
16485
16737
|
stderr: "",
|
|
16486
16738
|
ok: true
|
|
@@ -16721,12 +16973,12 @@ function runChange(repoRoot, args2, deps) {
|
|
|
16721
16973
|
// dist/shown-record.js
|
|
16722
16974
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16723
16975
|
import { platform as platform4 } from "node:os";
|
|
16724
|
-
import { join as
|
|
16976
|
+
import { join as join22 } from "node:path";
|
|
16725
16977
|
function updateShownRecord(engagementId, update, home) {
|
|
16726
16978
|
let temp = null;
|
|
16727
16979
|
try {
|
|
16728
16980
|
const dir = engagementStateDir(engagementId, home);
|
|
16729
|
-
const path =
|
|
16981
|
+
const path = join22(dir, SHOWN_PLANS_FILENAME);
|
|
16730
16982
|
let current;
|
|
16731
16983
|
try {
|
|
16732
16984
|
current = parseShownRecord(readFileSync17(path, "utf-8"));
|
|
@@ -16736,7 +16988,7 @@ function updateShownRecord(engagementId, update, home) {
|
|
|
16736
16988
|
const next = update(current);
|
|
16737
16989
|
const body = serialiseShownRecord(next);
|
|
16738
16990
|
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
16739
|
-
temp =
|
|
16991
|
+
temp = join22(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
|
|
16740
16992
|
writeFileSync8(temp, body, { mode: 384 });
|
|
16741
16993
|
if (platform4() !== "win32")
|
|
16742
16994
|
chmodSync3(temp, 384);
|
|
@@ -16813,7 +17065,7 @@ function readAndCheckPlan(repoRoot, phase, description, verb) {
|
|
|
16813
17065
|
const fileRel = planFileRel(phase);
|
|
16814
17066
|
let text;
|
|
16815
17067
|
try {
|
|
16816
|
-
text = readFileSync18(
|
|
17068
|
+
text = readFileSync18(join23(repoRoot, fileRel), "utf-8");
|
|
16817
17069
|
} catch (err) {
|
|
16818
17070
|
const code = err.code;
|
|
16819
17071
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -16969,7 +17221,7 @@ function removalOwedLines(verb, parts) {
|
|
|
16969
17221
|
function redrawLine(repoRoot, phase) {
|
|
16970
17222
|
let exists;
|
|
16971
17223
|
try {
|
|
16972
|
-
exists =
|
|
17224
|
+
exists = existsSync15(join23(repoRoot, DIAGRAM_DIR_REL, embedImageName(phaseDiagramBase(phase))));
|
|
16973
17225
|
} catch {
|
|
16974
17226
|
return null;
|
|
16975
17227
|
}
|
|
@@ -17553,7 +17805,7 @@ function readObserveLine(stdout) {
|
|
|
17553
17805
|
function runRunnerChild(binary, args2, credential, repoRoot, readLine, fallback) {
|
|
17554
17806
|
if (binary instanceof Error)
|
|
17555
17807
|
return Promise.resolve(fallback);
|
|
17556
|
-
return new Promise((
|
|
17808
|
+
return new Promise((resolve10) => {
|
|
17557
17809
|
try {
|
|
17558
17810
|
execFile(process.execPath, [binary, ...args2], {
|
|
17559
17811
|
cwd: repoRoot,
|
|
@@ -17563,10 +17815,10 @@ function runRunnerChild(binary, args2, credential, repoRoot, readLine, fallback)
|
|
|
17563
17815
|
windowsHide: true
|
|
17564
17816
|
}, (err, stdout) => {
|
|
17565
17817
|
const stopped = err !== null && err.killed === true;
|
|
17566
|
-
|
|
17818
|
+
resolve10(stopped || typeof stdout !== "string" ? fallback : readLine(stdout));
|
|
17567
17819
|
});
|
|
17568
17820
|
} catch {
|
|
17569
|
-
|
|
17821
|
+
resolve10(fallback);
|
|
17570
17822
|
}
|
|
17571
17823
|
});
|
|
17572
17824
|
}
|
|
@@ -17929,7 +18181,7 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
|
|
|
17929
18181
|
var PACING_FOLDER = "pacing";
|
|
17930
18182
|
function pacingFile(engagementId, name, home) {
|
|
17931
18183
|
try {
|
|
17932
|
-
return
|
|
18184
|
+
return join24(engagementStateDir(engagementId, home), PACING_FOLDER, name);
|
|
17933
18185
|
} catch {
|
|
17934
18186
|
return null;
|
|
17935
18187
|
}
|
|
@@ -18101,7 +18353,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
18101
18353
|
if (pinned !== null)
|
|
18102
18354
|
assertEngagementId(pinned);
|
|
18103
18355
|
const fromStore = pinned === null ? null : readEngagementEnv(pinned, home);
|
|
18104
|
-
const looked = pinned === null ? `${
|
|
18356
|
+
const looked = pinned === null ? `${join25(repoRoot, ".halfcycle", "bundle.json")} (which names no engagement)` : engagementEnvPath(pinned, home);
|
|
18105
18357
|
const values = {};
|
|
18106
18358
|
for (const key of OPEN_PHASE_ENV_KEYS) {
|
|
18107
18359
|
const value = (env[key] ?? fromStore?.[key] ?? "").trim();
|
|
@@ -18118,7 +18370,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
18118
18370
|
}
|
|
18119
18371
|
function readPinnedEngagementId(repoRoot) {
|
|
18120
18372
|
try {
|
|
18121
|
-
const pin = JSON.parse(readFileSync20(
|
|
18373
|
+
const pin = JSON.parse(readFileSync20(join25(repoRoot, ".halfcycle", "bundle.json"), "utf-8"));
|
|
18122
18374
|
const id = pin[PIN_ENGAGEMENT_ID_FIELD];
|
|
18123
18375
|
return typeof id === "string" && id !== "" ? id : null;
|
|
18124
18376
|
} catch {
|
|
@@ -18268,12 +18520,12 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
|
|
|
18268
18520
|
};
|
|
18269
18521
|
}
|
|
18270
18522
|
if (result.kind === "in-the-way") {
|
|
18271
|
-
return { kind: "exists", path:
|
|
18523
|
+
return { kind: "exists", path: relative6(repoRoot, result.path) };
|
|
18272
18524
|
}
|
|
18273
18525
|
return {
|
|
18274
18526
|
kind: "written",
|
|
18275
|
-
jsonPath:
|
|
18276
|
-
mdPath:
|
|
18527
|
+
jsonPath: relative6(repoRoot, result.jsonPath),
|
|
18528
|
+
mdPath: relative6(repoRoot, result.mdPath),
|
|
18277
18529
|
headline: latestCloseHeadline(answer.record),
|
|
18278
18530
|
stampFailure: clearStampFor(credential.engagementId, phase, home)
|
|
18279
18531
|
};
|
|
@@ -18606,7 +18858,7 @@ async function writeDrawOutcome(repoRoot, deps, outcome, target, warnings, canno
|
|
|
18606
18858
|
|
|
18607
18859
|
// dist/check-tasks.js
|
|
18608
18860
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
18609
|
-
import { join as
|
|
18861
|
+
import { join as join26, resolve as resolve7 } from "node:path";
|
|
18610
18862
|
var CHECK_TASKS_USAGE = `${ownCommand()} check-tasks <phase> [--with-task-list] [--repo <root>]`;
|
|
18611
18863
|
function parseCheckTasksArgs(argv) {
|
|
18612
18864
|
const positionals = [];
|
|
@@ -18647,7 +18899,7 @@ function issuePlace(rel, path) {
|
|
|
18647
18899
|
function readJsonFile(repoRoot, rel, parse) {
|
|
18648
18900
|
let text;
|
|
18649
18901
|
try {
|
|
18650
|
-
text = readFileSync21(
|
|
18902
|
+
text = readFileSync21(resolve7(repoRoot, rel), "utf-8");
|
|
18651
18903
|
} catch (err) {
|
|
18652
18904
|
const code = err.code;
|
|
18653
18905
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18705,7 +18957,7 @@ function readPhasePlanParts(repoRoot, phase, description) {
|
|
|
18705
18957
|
const file = planFileRel(phase);
|
|
18706
18958
|
let text;
|
|
18707
18959
|
try {
|
|
18708
|
-
text = readFileSync21(
|
|
18960
|
+
text = readFileSync21(join26(repoRoot, file), "utf-8");
|
|
18709
18961
|
} catch (err) {
|
|
18710
18962
|
const code = err.code;
|
|
18711
18963
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18738,7 +18990,7 @@ function readPlanSpecFiles(repoRoot, plan) {
|
|
|
18738
18990
|
const rel = `${SPEC_FOLDER}/${slug}.md`;
|
|
18739
18991
|
let text;
|
|
18740
18992
|
try {
|
|
18741
|
-
text = readFileSync21(
|
|
18993
|
+
text = readFileSync21(join26(repoRoot, rel), "utf-8");
|
|
18742
18994
|
} catch (err) {
|
|
18743
18995
|
const code = err.code;
|
|
18744
18996
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -18994,8 +19246,8 @@ function runCheckReport(source, head, deps = {}) {
|
|
|
18994
19246
|
|
|
18995
19247
|
// dist/setup-committed.js
|
|
18996
19248
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
18997
|
-
import { existsSync as
|
|
18998
|
-
import { join as
|
|
19249
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
19250
|
+
import { join as join27 } from "node:path";
|
|
18999
19251
|
var SETUP_COMMIT_MESSAGE = "Add Halfcycle setup files";
|
|
19000
19252
|
var SETUP_UNCOMMITTED_LINES = {
|
|
19001
19253
|
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 +19258,7 @@ function shellWord(path) {
|
|
|
19006
19258
|
return /^[A-Za-z0-9._/@+=-]+$/.test(path) ? path : `'${path.replace(/'/g, `'\\''`)}'`;
|
|
19007
19259
|
}
|
|
19008
19260
|
function uncommittedSetupFiles(repoRoot) {
|
|
19009
|
-
if (!
|
|
19261
|
+
if (!existsSync16(join27(repoRoot, INSTALL_MANIFEST_REL)))
|
|
19010
19262
|
return [];
|
|
19011
19263
|
const manifest = readPreviousManifest(repoRoot);
|
|
19012
19264
|
if (manifest === null)
|
|
@@ -19250,13 +19502,13 @@ function proveCouldNotStart(err) {
|
|
|
19250
19502
|
function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
19251
19503
|
const out = deps.stdout ?? process.stdout;
|
|
19252
19504
|
const err = deps.stderr ?? process.stderr;
|
|
19253
|
-
return new Promise((
|
|
19505
|
+
return new Promise((resolve10) => {
|
|
19254
19506
|
let settled = false;
|
|
19255
19507
|
const finish = (code) => {
|
|
19256
19508
|
if (settled)
|
|
19257
19509
|
return;
|
|
19258
19510
|
settled = true;
|
|
19259
|
-
|
|
19511
|
+
resolve10(code);
|
|
19260
19512
|
};
|
|
19261
19513
|
let child;
|
|
19262
19514
|
try {
|
|
@@ -19282,9 +19534,9 @@ function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
|
19282
19534
|
}
|
|
19283
19535
|
|
|
19284
19536
|
// dist/uninstall.js
|
|
19285
|
-
import { existsSync as
|
|
19537
|
+
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
19538
|
import { homedir as homedir7 } from "node:os";
|
|
19287
|
-
import { dirname as dirname8, join as
|
|
19539
|
+
import { dirname as dirname8, join as join28, relative as relative7, sep as sep3 } from "node:path";
|
|
19288
19540
|
var REMOVE_CREDENTIAL_FLAG = "--remove-credential";
|
|
19289
19541
|
function parseUninstallArgs(args2) {
|
|
19290
19542
|
let removeCredential = false;
|
|
@@ -19301,7 +19553,7 @@ function parseUninstallArgs(args2) {
|
|
|
19301
19553
|
}
|
|
19302
19554
|
function lexists(path) {
|
|
19303
19555
|
try {
|
|
19304
|
-
|
|
19556
|
+
lstatSync5(path);
|
|
19305
19557
|
return true;
|
|
19306
19558
|
} catch {
|
|
19307
19559
|
return false;
|
|
@@ -19319,7 +19571,7 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19319
19571
|
const { rel } = rule;
|
|
19320
19572
|
if (symlinkOnPath(targetRepo, rel))
|
|
19321
19573
|
return { state: "symlink" };
|
|
19322
|
-
const path =
|
|
19574
|
+
const path = join28(targetRepo, rel);
|
|
19323
19575
|
if (!lexists(path))
|
|
19324
19576
|
return { state: "absent" };
|
|
19325
19577
|
let text;
|
|
@@ -19342,8 +19594,8 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19342
19594
|
}
|
|
19343
19595
|
function validate(options, rules) {
|
|
19344
19596
|
const { targetRepo, removeCredential, home } = options;
|
|
19345
|
-
const manifestPath =
|
|
19346
|
-
const pinPath =
|
|
19597
|
+
const manifestPath = join28(targetRepo, INSTALL_MANIFEST_REL);
|
|
19598
|
+
const pinPath = join28(targetRepo, BUNDLE_PIN_REL);
|
|
19347
19599
|
if (symlinkOnPath(targetRepo, INSTALL_MANIFEST_REL) || symlinkOnPath(targetRepo, BUNDLE_PIN_REL)) {
|
|
19348
19600
|
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
19601
|
}
|
|
@@ -19384,7 +19636,7 @@ function validate(options, rules) {
|
|
|
19384
19636
|
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
19637
|
}
|
|
19386
19638
|
if (engagementId !== void 0) {
|
|
19387
|
-
const expectedParent =
|
|
19639
|
+
const expectedParent = join28(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
|
|
19388
19640
|
if (dirname8(engagementStateDir(engagementId, home)) !== expectedParent) {
|
|
19389
19641
|
throw new Refusal(`the project id in ${BUNDLE_PIN_REL} does not name a folder under ~/.halfcycle/engagements. Nothing was removed.`);
|
|
19390
19642
|
}
|
|
@@ -19410,10 +19662,10 @@ function mergedFileRules(writeSet) {
|
|
|
19410
19662
|
function writeBack(targetRepo, rel, current, next, lists, name) {
|
|
19411
19663
|
if (next === current)
|
|
19412
19664
|
return;
|
|
19413
|
-
const path =
|
|
19665
|
+
const path = join28(targetRepo, rel);
|
|
19414
19666
|
try {
|
|
19415
19667
|
if (next === null) {
|
|
19416
|
-
|
|
19668
|
+
rmSync9(path);
|
|
19417
19669
|
name(lists.removed, rel, rel);
|
|
19418
19670
|
} else {
|
|
19419
19671
|
writeFileSync9(path, next, "utf-8");
|
|
@@ -19427,7 +19679,7 @@ function statusLineClaimedFromTree(targetRepo, writeSet) {
|
|
|
19427
19679
|
try {
|
|
19428
19680
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19429
19681
|
return {};
|
|
19430
|
-
const value = JSON.parse(readFileSync23(
|
|
19682
|
+
const value = JSON.parse(readFileSync23(join28(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19431
19683
|
if (!isPlainObject4(value) || !("statusLine" in value))
|
|
19432
19684
|
return {};
|
|
19433
19685
|
const hash = canonicalSha256(value["statusLine"]);
|
|
@@ -19440,7 +19692,7 @@ function attributionClaimedFromTree(targetRepo, writeSet) {
|
|
|
19440
19692
|
try {
|
|
19441
19693
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19442
19694
|
return {};
|
|
19443
|
-
const value = JSON.parse(readFileSync23(
|
|
19695
|
+
const value = JSON.parse(readFileSync23(join28(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19444
19696
|
if (!isPlainObject4(value) || !("attribution" in value))
|
|
19445
19697
|
return {};
|
|
19446
19698
|
const hash = canonicalSha256(value["attribution"]);
|
|
@@ -19454,7 +19706,7 @@ function noRecord(targetRepo, writeSet) {
|
|
|
19454
19706
|
const claim = (rel, header, lines) => {
|
|
19455
19707
|
let text;
|
|
19456
19708
|
try {
|
|
19457
|
-
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(
|
|
19709
|
+
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(join28(targetRepo, rel), "utf-8");
|
|
19458
19710
|
} catch {
|
|
19459
19711
|
text = "";
|
|
19460
19712
|
}
|
|
@@ -19700,7 +19952,7 @@ function lastOccurrence(text, block) {
|
|
|
19700
19952
|
}
|
|
19701
19953
|
function coveredPathExists(targetRepo, line) {
|
|
19702
19954
|
const path = line.trim().replace(/^\//, "").replace(/\/$/, "");
|
|
19703
|
-
return path !== "" && lexists(
|
|
19955
|
+
return path !== "" && lexists(join28(targetRepo, path));
|
|
19704
19956
|
}
|
|
19705
19957
|
function selfBuildMergedFileRules() {
|
|
19706
19958
|
return {
|
|
@@ -19714,10 +19966,10 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19714
19966
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19715
19967
|
return;
|
|
19716
19968
|
}
|
|
19717
|
-
const abs =
|
|
19969
|
+
const abs = join28(targetRepo, rel);
|
|
19718
19970
|
let isFile;
|
|
19719
19971
|
try {
|
|
19720
|
-
isFile =
|
|
19972
|
+
isFile = lstatSync5(abs).isFile();
|
|
19721
19973
|
} catch {
|
|
19722
19974
|
return;
|
|
19723
19975
|
}
|
|
@@ -19726,7 +19978,7 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19726
19978
|
return;
|
|
19727
19979
|
}
|
|
19728
19980
|
try {
|
|
19729
|
-
|
|
19981
|
+
rmSync9(abs);
|
|
19730
19982
|
name(lists.removed, rel, rel);
|
|
19731
19983
|
} catch {
|
|
19732
19984
|
name(lists.failed, rel, rel);
|
|
@@ -19738,14 +19990,14 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19738
19990
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19739
19991
|
return;
|
|
19740
19992
|
}
|
|
19741
|
-
const abs =
|
|
19993
|
+
const abs = join28(targetRepo, rel);
|
|
19742
19994
|
let bytes;
|
|
19743
19995
|
try {
|
|
19744
19996
|
bytes = readFileSync23(abs);
|
|
19745
19997
|
} catch {
|
|
19746
19998
|
return;
|
|
19747
19999
|
}
|
|
19748
|
-
if (!
|
|
20000
|
+
if (!lstatSync5(abs).isFile()) {
|
|
19749
20001
|
name(lists.leftAlone, `${rel} (not a file)`, rel);
|
|
19750
20002
|
return;
|
|
19751
20003
|
}
|
|
@@ -19754,7 +20006,7 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19754
20006
|
return;
|
|
19755
20007
|
}
|
|
19756
20008
|
try {
|
|
19757
|
-
|
|
20009
|
+
rmSync9(abs);
|
|
19758
20010
|
name(lists.removed, rel, rel);
|
|
19759
20011
|
} catch {
|
|
19760
20012
|
name(lists.failed, rel, rel);
|
|
@@ -19765,14 +20017,14 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19765
20017
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19766
20018
|
return;
|
|
19767
20019
|
}
|
|
19768
|
-
const abs =
|
|
20020
|
+
const abs = join28(targetRepo, rel);
|
|
19769
20021
|
let bytes;
|
|
19770
20022
|
try {
|
|
19771
20023
|
bytes = readFileSync23(abs);
|
|
19772
20024
|
} catch {
|
|
19773
20025
|
return;
|
|
19774
20026
|
}
|
|
19775
|
-
if (!
|
|
20027
|
+
if (!lstatSync5(abs).isFile()) {
|
|
19776
20028
|
name(lists.leftAlone, `${rel} (not a file)`, rel);
|
|
19777
20029
|
return;
|
|
19778
20030
|
}
|
|
@@ -19781,7 +20033,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19781
20033
|
return;
|
|
19782
20034
|
}
|
|
19783
20035
|
try {
|
|
19784
|
-
|
|
20036
|
+
rmSync9(abs);
|
|
19785
20037
|
name(lists.removed, rel, rel);
|
|
19786
20038
|
} catch {
|
|
19787
20039
|
name(lists.failed, rel, rel);
|
|
@@ -19790,15 +20042,15 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19790
20042
|
function removeSelfBuildEmptyDir(targetRepo, dir, lists, name) {
|
|
19791
20043
|
if (symlinkOnPath(targetRepo, dir))
|
|
19792
20044
|
return;
|
|
19793
|
-
const abs =
|
|
20045
|
+
const abs = join28(targetRepo, dir);
|
|
19794
20046
|
try {
|
|
19795
|
-
if (!
|
|
20047
|
+
if (!lstatSync5(abs).isDirectory() || readdirSync2(abs).length > 0)
|
|
19796
20048
|
return;
|
|
19797
20049
|
} catch {
|
|
19798
20050
|
return;
|
|
19799
20051
|
}
|
|
19800
20052
|
try {
|
|
19801
|
-
|
|
20053
|
+
rmdirSync2(abs);
|
|
19802
20054
|
} catch {
|
|
19803
20055
|
name(lists.failed, `${dir}/`, dir);
|
|
19804
20056
|
}
|
|
@@ -19824,7 +20076,7 @@ function uninstallSelfBuild(options) {
|
|
|
19824
20076
|
throw err;
|
|
19825
20077
|
}
|
|
19826
20078
|
const { targetRepo } = options;
|
|
19827
|
-
const pinExisted = lexists(
|
|
20079
|
+
const pinExisted = lexists(join28(targetRepo, BUNDLE_PIN_REL));
|
|
19828
20080
|
const name = (list2, text, _path) => {
|
|
19829
20081
|
list2.push(text);
|
|
19830
20082
|
};
|
|
@@ -19911,7 +20163,7 @@ function uninstall(options) {
|
|
|
19911
20163
|
}
|
|
19912
20164
|
const serverRemoved = removeOwnServerApproval(targetRepo);
|
|
19913
20165
|
if (listRemoved || serverRemoved) {
|
|
19914
|
-
if (
|
|
20166
|
+
if (existsSync17(join28(targetRepo, LOCAL_SETTINGS_REL2))) {
|
|
19915
20167
|
name(result.restored, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
19916
20168
|
} else {
|
|
19917
20169
|
name(result.removed, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
@@ -19953,7 +20205,7 @@ function uninstall(options) {
|
|
|
19953
20205
|
const gitattributes = manifest.gitattributes ?? { created: false, appended: [] };
|
|
19954
20206
|
trimAppendedFile(targetRepo, gitattributes, deleteWhenEmpty(gitattributes.created), gitattributesRules(writeSet), result, name);
|
|
19955
20207
|
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(
|
|
20208
|
+
if (checked.manifest === null && !lexists(join28(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
|
|
19957
20209
|
return { outcome: "not-installed", exitCode: 0, ...result, credential: nothingToSay };
|
|
19958
20210
|
}
|
|
19959
20211
|
const credential = handleCredential(options, checked);
|
|
@@ -19961,7 +20213,7 @@ function uninstall(options) {
|
|
|
19961
20213
|
if (result.failed.length === 0 && !credentialFailed) {
|
|
19962
20214
|
removeInstalledFile(targetRepo, BUNDLE_PIN_REL, recorded.get(BUNDLE_PIN_REL), writeSet, result, name);
|
|
19963
20215
|
try {
|
|
19964
|
-
|
|
20216
|
+
rmSync9(join28(targetRepo, INSTALL_MANIFEST_REL), { force: true });
|
|
19965
20217
|
named2.add(INSTALL_MANIFEST_REL);
|
|
19966
20218
|
} catch {
|
|
19967
20219
|
name(result.failed, INSTALL_MANIFEST_REL, INSTALL_MANIFEST_REL);
|
|
@@ -19996,13 +20248,13 @@ function applyMergedJson(targetRepo, rule, file, deleteWhenEmpty, lists, name, e
|
|
|
19996
20248
|
return;
|
|
19997
20249
|
const empty = Object.keys(value).length === 0 && deleteWhenEmpty;
|
|
19998
20250
|
writeBack(targetRepo, rel, file.text, empty ? null : inLineEndingOf(file.text, serializeJson(value)), lists, name);
|
|
19999
|
-
if (adopted && lexists(
|
|
20251
|
+
if (adopted && lexists(join28(targetRepo, rel))) {
|
|
20000
20252
|
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
20253
|
}
|
|
20002
20254
|
}
|
|
20003
20255
|
function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, name) {
|
|
20004
20256
|
const rel = CODEX_CONFIG_REL2;
|
|
20005
|
-
const path =
|
|
20257
|
+
const path = join28(targetRepo, rel);
|
|
20006
20258
|
if (symlinkOnPath(targetRepo, rel)) {
|
|
20007
20259
|
if (lexists(path))
|
|
20008
20260
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
@@ -20010,7 +20262,7 @@ function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, nam
|
|
|
20010
20262
|
}
|
|
20011
20263
|
let text;
|
|
20012
20264
|
try {
|
|
20013
|
-
if (!
|
|
20265
|
+
if (!lstatSync5(path).isFile())
|
|
20014
20266
|
return;
|
|
20015
20267
|
text = readFileSync23(path, "utf-8");
|
|
20016
20268
|
} catch {
|
|
@@ -20047,10 +20299,10 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20047
20299
|
name(lists.leftAlone, `${path} (a symbolic link, or inside one \u2014 not followed)`, path);
|
|
20048
20300
|
return;
|
|
20049
20301
|
}
|
|
20050
|
-
const abs =
|
|
20302
|
+
const abs = join28(targetRepo, path);
|
|
20051
20303
|
let isFile;
|
|
20052
20304
|
try {
|
|
20053
|
-
isFile =
|
|
20305
|
+
isFile = lstatSync5(abs).isFile();
|
|
20054
20306
|
} catch {
|
|
20055
20307
|
return;
|
|
20056
20308
|
}
|
|
@@ -20072,7 +20324,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20072
20324
|
}
|
|
20073
20325
|
}
|
|
20074
20326
|
try {
|
|
20075
|
-
|
|
20327
|
+
rmSync9(abs);
|
|
20076
20328
|
name(lists.removed, path, path);
|
|
20077
20329
|
} catch {
|
|
20078
20330
|
name(lists.failed, path, path);
|
|
@@ -20081,15 +20333,15 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20081
20333
|
function removeEmptyDir(targetRepo, dir, writeSet, lists, name) {
|
|
20082
20334
|
if (!writeSet.dirs.has(dir) || symlinkOnPath(targetRepo, dir))
|
|
20083
20335
|
return;
|
|
20084
|
-
const abs =
|
|
20336
|
+
const abs = join28(targetRepo, dir);
|
|
20085
20337
|
try {
|
|
20086
|
-
if (!
|
|
20338
|
+
if (!lstatSync5(abs).isDirectory() || readdirSync2(abs).length > 0)
|
|
20087
20339
|
return;
|
|
20088
20340
|
} catch {
|
|
20089
20341
|
return;
|
|
20090
20342
|
}
|
|
20091
20343
|
try {
|
|
20092
|
-
|
|
20344
|
+
rmdirSync2(abs);
|
|
20093
20345
|
} catch {
|
|
20094
20346
|
name(lists.failed, `${dir}/`, dir);
|
|
20095
20347
|
}
|
|
@@ -20100,7 +20352,7 @@ function trimAppendedFile(targetRepo, rec, deleteWhenEmpty, rules, lists, name)
|
|
|
20100
20352
|
name(lists.leftAlone, `${rel} (a symbolic link \u2014 not followed)`, rel);
|
|
20101
20353
|
return [];
|
|
20102
20354
|
}
|
|
20103
|
-
const path =
|
|
20355
|
+
const path = join28(targetRepo, rel);
|
|
20104
20356
|
let before;
|
|
20105
20357
|
try {
|
|
20106
20358
|
before = readFileSync23(path, "utf-8");
|
|
@@ -20131,12 +20383,12 @@ function handleCredential(options, checked) {
|
|
|
20131
20383
|
if (!lexists(dir))
|
|
20132
20384
|
return { kind: "not-held", dir };
|
|
20133
20385
|
try {
|
|
20134
|
-
if (
|
|
20386
|
+
if (lstatSync5(dir).isSymbolicLink())
|
|
20135
20387
|
return { kind: "failed", dir, reason: "it is a symbolic link" };
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
if (
|
|
20139
|
-
|
|
20388
|
+
rmSync9(engagementEnvPath(engagementId, home), { force: true });
|
|
20389
|
+
rmSync9(notThisAccountMarkerPath(engagementId, home), { force: true });
|
|
20390
|
+
if (readdirSync2(dir).length === 0)
|
|
20391
|
+
rmdirSync2(dir);
|
|
20140
20392
|
} catch (err) {
|
|
20141
20393
|
return { kind: "failed", dir, reason: err instanceof Error ? err.message : String(err) };
|
|
20142
20394
|
}
|
|
@@ -20147,16 +20399,16 @@ function filesUnder(root, rel) {
|
|
|
20147
20399
|
const walk = (dirRel) => {
|
|
20148
20400
|
let names;
|
|
20149
20401
|
try {
|
|
20150
|
-
if (!
|
|
20402
|
+
if (!lstatSync5(join28(root, dirRel)).isDirectory())
|
|
20151
20403
|
return;
|
|
20152
|
-
names =
|
|
20404
|
+
names = readdirSync2(join28(root, dirRel));
|
|
20153
20405
|
} catch {
|
|
20154
20406
|
return;
|
|
20155
20407
|
}
|
|
20156
20408
|
for (const entry of names.sort()) {
|
|
20157
20409
|
const childRel = `${dirRel}/${entry}`;
|
|
20158
20410
|
try {
|
|
20159
|
-
if (
|
|
20411
|
+
if (lstatSync5(join28(root, childRel)).isDirectory())
|
|
20160
20412
|
walk(childRel);
|
|
20161
20413
|
else
|
|
20162
20414
|
out.push(childRel);
|
|
@@ -20170,9 +20422,9 @@ function filesUnder(root, rel) {
|
|
|
20170
20422
|
}
|
|
20171
20423
|
function shownPath(path, home) {
|
|
20172
20424
|
const base = home ?? homedir7();
|
|
20173
|
-
const rel =
|
|
20174
|
-
const under = rel !== "" && !rel.startsWith("..") && !rel.startsWith(
|
|
20175
|
-
return `${under ? `~/${rel.split(
|
|
20425
|
+
const rel = relative7(base, path);
|
|
20426
|
+
const under = rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep3);
|
|
20427
|
+
return `${under ? `~/${rel.split(sep3).join("/")}` : path}/`;
|
|
20176
20428
|
}
|
|
20177
20429
|
function renderUninstallReport(result, home) {
|
|
20178
20430
|
if (result.outcome === "refused") {
|
|
@@ -20228,7 +20480,7 @@ function renderUninstallReport(result, home) {
|
|
|
20228
20480
|
}
|
|
20229
20481
|
|
|
20230
20482
|
// dist/banner-facts.js
|
|
20231
|
-
import { basename as basename4, resolve as
|
|
20483
|
+
import { basename as basename4, resolve as resolve8 } from "node:path";
|
|
20232
20484
|
var BRAND_URL = "halfcycle.ai";
|
|
20233
20485
|
var TAGLINE = [
|
|
20234
20486
|
"Fewer cycles.",
|
|
@@ -20270,15 +20522,44 @@ var REAL_PROBES = {
|
|
|
20270
20522
|
return void 0;
|
|
20271
20523
|
}
|
|
20272
20524
|
},
|
|
20273
|
-
websiteHost: (env) => {
|
|
20525
|
+
websiteHost: (env, dir) => {
|
|
20274
20526
|
try {
|
|
20275
|
-
|
|
20276
|
-
return resolved.source === "default" ? BRAND_URL : new URL(resolved.origin).host;
|
|
20527
|
+
return serviceHost4(env, dir);
|
|
20277
20528
|
} catch {
|
|
20278
20529
|
return void 0;
|
|
20279
20530
|
}
|
|
20280
20531
|
}
|
|
20281
20532
|
};
|
|
20533
|
+
function serviceHost4(env, projectDir, home) {
|
|
20534
|
+
const fromEnv = resolveControlOrigin(env);
|
|
20535
|
+
const origin = fromEnv.source === "environment" ? fromEnv.origin : storedServiceOrigin(projectDir, home) ?? fromEnv.origin;
|
|
20536
|
+
if (origin === DEFAULT_CONTROL_ORIGIN)
|
|
20537
|
+
return BRAND_URL;
|
|
20538
|
+
try {
|
|
20539
|
+
return new URL(origin).host || BRAND_URL;
|
|
20540
|
+
} catch {
|
|
20541
|
+
return BRAND_URL;
|
|
20542
|
+
}
|
|
20543
|
+
}
|
|
20544
|
+
function storedServiceOrigin(projectDir, home) {
|
|
20545
|
+
let engagementId;
|
|
20546
|
+
try {
|
|
20547
|
+
engagementId = readBundlePin(projectDir)?.engagementId;
|
|
20548
|
+
} catch {
|
|
20549
|
+
return void 0;
|
|
20550
|
+
}
|
|
20551
|
+
if (typeof engagementId !== "string" || engagementId === "")
|
|
20552
|
+
return void 0;
|
|
20553
|
+
try {
|
|
20554
|
+
const stored = readEngagementEnv(engagementId, home)?.["HALFCYCLE_SERVICE_URL"];
|
|
20555
|
+
if (stored === void 0)
|
|
20556
|
+
return void 0;
|
|
20557
|
+
const resolved = resolveControlOrigin({ HALFCYCLE_SERVICE_URL: stored });
|
|
20558
|
+
return resolved.source === "environment" ? resolved.origin : void 0;
|
|
20559
|
+
} catch {
|
|
20560
|
+
return void 0;
|
|
20561
|
+
}
|
|
20562
|
+
}
|
|
20282
20563
|
function fact(label, value, narrow) {
|
|
20283
20564
|
if (value === void 0 || value === "")
|
|
20284
20565
|
return void 0;
|
|
@@ -20304,14 +20585,14 @@ function openingBannerContent(projectDir, env = process.env, probes = REAL_PROBE
|
|
|
20304
20585
|
// `Website`, NOT `Plane` — see this function's docblock. (No issue number here:
|
|
20305
20586
|
// a comment beside a property survives into the published entry, unlike a
|
|
20306
20587
|
// leading docblock, which esbuild drops.)
|
|
20307
|
-
fact("Website", probes.websiteHost(env))
|
|
20588
|
+
fact("Website", probes.websiteHost(env, projectDir))
|
|
20308
20589
|
].filter((f) => f !== void 0);
|
|
20309
20590
|
return {
|
|
20310
20591
|
head: `halfcycle@${name}`,
|
|
20311
20592
|
facts,
|
|
20312
20593
|
tag: TAGLINE,
|
|
20313
20594
|
tagNarrow: TAGLINE_NARROW,
|
|
20314
|
-
url: probes.websiteHost(env) ?? BRAND_URL,
|
|
20595
|
+
url: probes.websiteHost(env, projectDir) ?? BRAND_URL,
|
|
20315
20596
|
// `''` when even this package's own manifest could not be read. The one-line cut
|
|
20316
20597
|
// drops the whole `vX.Y.Z` clause in that case rather than printing a bare `v`.
|
|
20317
20598
|
version: version ?? ""
|
|
@@ -20319,7 +20600,7 @@ function openingBannerContent(projectDir, env = process.env, probes = REAL_PROBE
|
|
|
20319
20600
|
}
|
|
20320
20601
|
function safeBasename(dir) {
|
|
20321
20602
|
try {
|
|
20322
|
-
const name = basename4(
|
|
20603
|
+
const name = basename4(resolve8(dir)).trim();
|
|
20323
20604
|
return name === "" ? "project" : name;
|
|
20324
20605
|
} catch {
|
|
20325
20606
|
return "project";
|
|
@@ -20661,7 +20942,7 @@ async function runCrew(repoRoot, deps) {
|
|
|
20661
20942
|
// dist/pace.js
|
|
20662
20943
|
import { spawn as spawn3 } from "node:child_process";
|
|
20663
20944
|
import { readFileSync as readFileSync24 } from "node:fs";
|
|
20664
|
-
import { join as
|
|
20945
|
+
import { join as join29 } from "node:path";
|
|
20665
20946
|
var PACE_USAGE = `${ownCommand()} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
|
|
20666
20947
|
function parsePaceArgs(argv) {
|
|
20667
20948
|
let repoRoot;
|
|
@@ -20726,13 +21007,13 @@ function runPaceChild(binary, credential, repoRoot, args2, deps = {}) {
|
|
|
20726
21007
|
if (binary instanceof Error)
|
|
20727
21008
|
return Promise.resolve({ code: 1, stdout: "" });
|
|
20728
21009
|
const err = deps.stderr ?? process.stderr;
|
|
20729
|
-
return new Promise((
|
|
21010
|
+
return new Promise((resolve10) => {
|
|
20730
21011
|
let settled = false;
|
|
20731
21012
|
const done = (output) => {
|
|
20732
21013
|
if (settled)
|
|
20733
21014
|
return;
|
|
20734
21015
|
settled = true;
|
|
20735
|
-
|
|
21016
|
+
resolve10(output);
|
|
20736
21017
|
};
|
|
20737
21018
|
try {
|
|
20738
21019
|
const child = spawn3(process.execPath, [binary, "pace", ...args2], {
|
|
@@ -20775,7 +21056,7 @@ function notPacedLine(reason) {
|
|
|
20775
21056
|
}
|
|
20776
21057
|
function readPromptScript(repoRoot) {
|
|
20777
21058
|
try {
|
|
20778
|
-
return readFileSync24(
|
|
21059
|
+
return readFileSync24(join29(repoRoot, ".claude", "hooks", "user-prompt-scope.sh"), "utf-8");
|
|
20779
21060
|
} catch {
|
|
20780
21061
|
return null;
|
|
20781
21062
|
}
|
|
@@ -20835,7 +21116,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
|
|
|
20835
21116
|
|
|
20836
21117
|
// dist/route.js
|
|
20837
21118
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
20838
|
-
import { join as
|
|
21119
|
+
import { join as join30 } from "node:path";
|
|
20839
21120
|
var ROUTE_USAGE = `${ownCommand()} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
|
|
20840
21121
|
var ROUTE_WORK_READ_DEADLINE_MS = 5e3;
|
|
20841
21122
|
function parseRouteArgs(argv) {
|
|
@@ -20878,7 +21159,7 @@ function parseRouteArgs(argv) {
|
|
|
20878
21159
|
return { ok: true, phase: positionals[0], task: positionals[1], agent: agent ?? "claude-code", repoRoot };
|
|
20879
21160
|
}
|
|
20880
21161
|
function routingPath(engagementId, home) {
|
|
20881
|
-
return
|
|
21162
|
+
return join30(engagementStateDir(engagementId, home), ROUTING_FILENAME);
|
|
20882
21163
|
}
|
|
20883
21164
|
function readCheapStarted(path, phase) {
|
|
20884
21165
|
try {
|
|
@@ -20901,8 +21182,8 @@ function lastTierOf(attempts) {
|
|
|
20901
21182
|
}
|
|
20902
21183
|
async function readWorkInTime(deps, credential, phase) {
|
|
20903
21184
|
let timer;
|
|
20904
|
-
const late = new Promise((
|
|
20905
|
-
timer = setTimeout(() =>
|
|
21185
|
+
const late = new Promise((resolve10) => {
|
|
21186
|
+
timer = setTimeout(() => resolve10({ kind: "unreachable" }), ROUTE_WORK_READ_DEADLINE_MS);
|
|
20906
21187
|
timer.unref?.();
|
|
20907
21188
|
});
|
|
20908
21189
|
try {
|
|
@@ -20968,10 +21249,10 @@ async function runRoute(repoRoot, phase, taskId, agent, deps) {
|
|
|
20968
21249
|
}
|
|
20969
21250
|
|
|
20970
21251
|
// dist/restore.js
|
|
20971
|
-
import { spawnSync as
|
|
20972
|
-
import { closeSync as closeSync3, fchmodSync, lstatSync as
|
|
21252
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21253
|
+
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
21254
|
import { homedir as homedir8 } from "node:os";
|
|
20974
|
-
import { join as
|
|
21255
|
+
import { join as join31, resolve as resolve9 } from "node:path";
|
|
20975
21256
|
var RESTORE_USAGE = `${ownCommand()} restore [<id>]`;
|
|
20976
21257
|
function parseRestoreArgs(rest) {
|
|
20977
21258
|
if (rest.length > 1)
|
|
@@ -20984,7 +21265,7 @@ function parseRestoreArgs(rest) {
|
|
|
20984
21265
|
var LIST_COMMAND = `${ownCommand()} restore`;
|
|
20985
21266
|
function runRestore(cwd, id, options = {}) {
|
|
20986
21267
|
const now = options.now ?? Date.now;
|
|
20987
|
-
const top =
|
|
21268
|
+
const top = git3(cwd, ["rev-parse", "--show-toplevel"]);
|
|
20988
21269
|
if (!top.ok || top.text.trim() === "") {
|
|
20989
21270
|
return refused2("This folder is not in a project kept with git, so Halfcycle has saved nothing here to bring back.");
|
|
20990
21271
|
}
|
|
@@ -20993,7 +21274,7 @@ function runRestore(cwd, id, options = {}) {
|
|
|
20993
21274
|
if (engagementId === null) {
|
|
20994
21275
|
return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${ownCommand()}`);
|
|
20995
21276
|
}
|
|
20996
|
-
const folder2 =
|
|
21277
|
+
const folder2 = join31(options.home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
|
|
20997
21278
|
const refs = savedRefs(root);
|
|
20998
21279
|
if (refs === null)
|
|
20999
21280
|
return refused2("Halfcycle could not read the saved copies in this project, so nothing was put back.");
|
|
@@ -21069,7 +21350,7 @@ function restoreOne(root, folder2, refs, rawId) {
|
|
|
21069
21350
|
if (p.keptIn === "commit")
|
|
21070
21351
|
fromCommit.push(p.path);
|
|
21071
21352
|
else
|
|
21072
|
-
restoreCopy(root,
|
|
21353
|
+
restoreCopy(root, join31(folder2, id), p.path, out);
|
|
21073
21354
|
}
|
|
21074
21355
|
if (fromCommit.length > 0)
|
|
21075
21356
|
restoreFromCommit(root, ref, fromCommit, out);
|
|
@@ -21097,7 +21378,7 @@ function makeFolders(root, rel) {
|
|
|
21097
21378
|
const segments = rel.split("/");
|
|
21098
21379
|
let at = root;
|
|
21099
21380
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
21100
|
-
at =
|
|
21381
|
+
at = join31(at, segments[i]);
|
|
21101
21382
|
const shown2 = segments.slice(0, i + 1).join("/");
|
|
21102
21383
|
let st = lstatOrNull3(at);
|
|
21103
21384
|
if (st === null) {
|
|
@@ -21117,7 +21398,7 @@ function makeFolders(root, rel) {
|
|
|
21117
21398
|
return null;
|
|
21118
21399
|
}
|
|
21119
21400
|
function place(root, rel, content, out) {
|
|
21120
|
-
const abs =
|
|
21401
|
+
const abs = join31(root, rel);
|
|
21121
21402
|
const there = lstatOrNull3(abs);
|
|
21122
21403
|
if (there !== null) {
|
|
21123
21404
|
if (sameAs(abs, there, content))
|
|
@@ -21165,14 +21446,14 @@ function restoreCopy(root, saveDir, rel, out) {
|
|
|
21165
21446
|
let at = saveDir;
|
|
21166
21447
|
const chain = [SAVED_COPIES_DIR_NAME, ...segments];
|
|
21167
21448
|
for (let i = 0; i < chain.length - 1; i++) {
|
|
21168
|
-
at =
|
|
21449
|
+
at = join31(at, chain[i]);
|
|
21169
21450
|
const st2 = lstatOrNull3(at);
|
|
21170
21451
|
if (st2 === null || st2.isSymbolicLink() || !st2.isDirectory()) {
|
|
21171
21452
|
out.left.push(`${rel}: the saved copy does not hold it`);
|
|
21172
21453
|
return;
|
|
21173
21454
|
}
|
|
21174
21455
|
}
|
|
21175
|
-
const src =
|
|
21456
|
+
const src = join31(at, chain[chain.length - 1]);
|
|
21176
21457
|
const st = lstatOrNull3(src);
|
|
21177
21458
|
try {
|
|
21178
21459
|
if (st !== null && st.isSymbolicLink())
|
|
@@ -21187,7 +21468,7 @@ function restoreFromCommit(root, ref, rels, out) {
|
|
|
21187
21468
|
const entries = /* @__PURE__ */ new Map();
|
|
21188
21469
|
for (let i = 0; i < rels.length; i += 200) {
|
|
21189
21470
|
const chunk = rels.slice(i, i + 200);
|
|
21190
|
-
const listed =
|
|
21471
|
+
const listed = git3(root, ["--literal-pathspecs", "ls-tree", "-z", "--full-tree", `${ref}^{commit}`, "--", ...chunk]);
|
|
21191
21472
|
if (!listed.ok) {
|
|
21192
21473
|
for (const rel of rels)
|
|
21193
21474
|
out.left.push(`${rel}: the saved copy could not be read`);
|
|
@@ -21218,7 +21499,7 @@ function restoreFromCommit(root, ref, rels, out) {
|
|
|
21218
21499
|
}
|
|
21219
21500
|
}
|
|
21220
21501
|
function readBlobs(root, objects) {
|
|
21221
|
-
const r =
|
|
21502
|
+
const r = spawnSync4("git", ["cat-file", "--batch"], {
|
|
21222
21503
|
cwd: root,
|
|
21223
21504
|
env: gitEnvForNamedRepo(),
|
|
21224
21505
|
input: `${objects.join("\n")}
|
|
@@ -21246,12 +21527,12 @@ function readBlobs(root, objects) {
|
|
|
21246
21527
|
function restoreLines(root, ref, saved, out) {
|
|
21247
21528
|
if (saved.branches.length === 0)
|
|
21248
21529
|
return;
|
|
21249
|
-
const parents =
|
|
21530
|
+
const parents = git3(root, ["rev-list", "--parents", "-n", "1", `${ref}^{commit}`]);
|
|
21250
21531
|
const held = new Set(parents.ok ? parents.text.trim().split(/\s+/).slice(1) : []);
|
|
21251
21532
|
for (const b of saved.branches) {
|
|
21252
21533
|
const shown2 = `the line of work "${b.name}"`;
|
|
21253
21534
|
const full = `refs/heads/${b.name}`;
|
|
21254
|
-
if (!
|
|
21535
|
+
if (!git3(root, ["check-ref-format", full]).ok) {
|
|
21255
21536
|
out.left.push(`${shown2}: its name cannot be used`);
|
|
21256
21537
|
continue;
|
|
21257
21538
|
}
|
|
@@ -21259,7 +21540,7 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21259
21540
|
out.left.push(`${shown2}: the saved copy does not hold its commit`);
|
|
21260
21541
|
continue;
|
|
21261
21542
|
}
|
|
21262
|
-
const now =
|
|
21543
|
+
const now = git3(root, ["rev-parse", "--verify", "--quiet", `${full}^{commit}`]);
|
|
21263
21544
|
if (now.ok && now.text.trim() !== "") {
|
|
21264
21545
|
if (now.text.trim() === b.commit)
|
|
21265
21546
|
out.already.push(`${shown2}, at its saved commit ${b.commit.slice(0, 12)}`);
|
|
@@ -21267,7 +21548,7 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21267
21548
|
out.left.push(`${shown2}: one with this name exists now; rename it and run this again to bring back the saved one`);
|
|
21268
21549
|
continue;
|
|
21269
21550
|
}
|
|
21270
|
-
const made =
|
|
21551
|
+
const made = git3(root, ["update-ref", "--no-deref", full, b.commit, ""]);
|
|
21271
21552
|
if (made.ok)
|
|
21272
21553
|
out.back.push(`${shown2}, at its saved commit ${b.commit.slice(0, 12)}`);
|
|
21273
21554
|
else
|
|
@@ -21276,14 +21557,14 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21276
21557
|
}
|
|
21277
21558
|
function pinnedEngagement(root) {
|
|
21278
21559
|
try {
|
|
21279
|
-
const pin = JSON.parse(readFileSync26(
|
|
21560
|
+
const pin = JSON.parse(readFileSync26(join31(root, BUNDLE_PIN_REL), "utf-8"));
|
|
21280
21561
|
return isEngagementId(pin.engagementId) ? pin.engagementId : null;
|
|
21281
21562
|
} catch {
|
|
21282
21563
|
return null;
|
|
21283
21564
|
}
|
|
21284
21565
|
}
|
|
21285
21566
|
function savedRefs(root) {
|
|
21286
|
-
const listed =
|
|
21567
|
+
const listed = git3(root, ["for-each-ref", "--format=%(refname)%00%(committerdate:unix)", SAVED_REF_PREFIX]);
|
|
21287
21568
|
if (!listed.ok)
|
|
21288
21569
|
return null;
|
|
21289
21570
|
const refs = /* @__PURE__ */ new Map();
|
|
@@ -21298,11 +21579,11 @@ function savedRefs(root) {
|
|
|
21298
21579
|
return refs;
|
|
21299
21580
|
}
|
|
21300
21581
|
function readSaved(folder2, id) {
|
|
21301
|
-
const dir =
|
|
21582
|
+
const dir = join31(folder2, id);
|
|
21302
21583
|
const st = lstatOrNull3(dir);
|
|
21303
21584
|
if (st === null || !st.isDirectory())
|
|
21304
21585
|
return null;
|
|
21305
|
-
const file =
|
|
21586
|
+
const file = join31(dir, SAVED_FILE_NAME);
|
|
21306
21587
|
const fst = lstatOrNull3(file);
|
|
21307
21588
|
if (fst === null || !fst.isFile())
|
|
21308
21589
|
return null;
|
|
@@ -21315,11 +21596,11 @@ function readSaved(folder2, id) {
|
|
|
21315
21596
|
}
|
|
21316
21597
|
function pruneAtRestore(root, folder2, refs, now) {
|
|
21317
21598
|
try {
|
|
21318
|
-
const common =
|
|
21319
|
-
const repository = common.ok && common.text.trim() !== "" ? realOrSelf(
|
|
21599
|
+
const common = git3(root, ["rev-parse", "--git-common-dir"]);
|
|
21600
|
+
const repository = common.ok && common.text.trim() !== "" ? realOrSelf(resolve9(root, common.text.trim())) : null;
|
|
21320
21601
|
let names = [];
|
|
21321
21602
|
try {
|
|
21322
|
-
names =
|
|
21603
|
+
names = readdirSync3(folder2);
|
|
21323
21604
|
} catch {
|
|
21324
21605
|
names = [];
|
|
21325
21606
|
}
|
|
@@ -21328,7 +21609,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21328
21609
|
if (!savedIdSchema.safeParse(id).success)
|
|
21329
21610
|
continue;
|
|
21330
21611
|
here.add(id);
|
|
21331
|
-
const dir =
|
|
21612
|
+
const dir = join31(folder2, id);
|
|
21332
21613
|
const st = lstatOrNull3(dir);
|
|
21333
21614
|
const answer = savedPruneAnswer({
|
|
21334
21615
|
refMadeAt: refs.get(id) ?? null,
|
|
@@ -21336,11 +21617,11 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21336
21617
|
folderRepository: markedRepository(dir)
|
|
21337
21618
|
}, { repository, now });
|
|
21338
21619
|
if (answer === "remove-folder")
|
|
21339
|
-
|
|
21340
|
-
else if (answer === "remove-ref-then-folder" &&
|
|
21620
|
+
rmSync10(dir, { recursive: true, force: true });
|
|
21621
|
+
else if (answer === "remove-ref-then-folder" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok) {
|
|
21341
21622
|
refs.delete(id);
|
|
21342
|
-
|
|
21343
|
-
} else if (answer === "remove-ref" &&
|
|
21623
|
+
rmSync10(dir, { recursive: true, force: true });
|
|
21624
|
+
} else if (answer === "remove-ref" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok) {
|
|
21344
21625
|
refs.delete(id);
|
|
21345
21626
|
}
|
|
21346
21627
|
}
|
|
@@ -21348,7 +21629,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21348
21629
|
if (here.has(id))
|
|
21349
21630
|
continue;
|
|
21350
21631
|
const answer = savedPruneAnswer({ refMadeAt: madeAt, folderChangedAt: null, folderRepository: null }, { repository, now });
|
|
21351
|
-
if (answer === "remove-ref" &&
|
|
21632
|
+
if (answer === "remove-ref" && git3(root, ["update-ref", "-d", savedRefOf(id)]).ok)
|
|
21352
21633
|
refs.delete(id);
|
|
21353
21634
|
}
|
|
21354
21635
|
} catch {
|
|
@@ -21356,15 +21637,15 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21356
21637
|
}
|
|
21357
21638
|
function markedRepository(dir) {
|
|
21358
21639
|
try {
|
|
21359
|
-
const text = readFileSync26(
|
|
21640
|
+
const text = readFileSync26(join31(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
|
|
21360
21641
|
return text === "" ? null : text;
|
|
21361
21642
|
} catch {
|
|
21362
21643
|
return null;
|
|
21363
21644
|
}
|
|
21364
21645
|
}
|
|
21365
|
-
function
|
|
21646
|
+
function git3(cwd, args2) {
|
|
21366
21647
|
try {
|
|
21367
|
-
const r =
|
|
21648
|
+
const r = spawnSync4("git", args2, { cwd, encoding: "utf-8", env: gitEnvForNamedRepo(), maxBuffer: 64 * 1024 * 1024 });
|
|
21368
21649
|
return r.status === 0 ? { ok: true, text: r.stdout ?? "" } : { ok: false };
|
|
21369
21650
|
} catch {
|
|
21370
21651
|
return { ok: false };
|
|
@@ -21372,23 +21653,23 @@ function git2(cwd, args2) {
|
|
|
21372
21653
|
}
|
|
21373
21654
|
function lstatOrNull3(p) {
|
|
21374
21655
|
try {
|
|
21375
|
-
return
|
|
21656
|
+
return lstatSync6(p);
|
|
21376
21657
|
} catch {
|
|
21377
21658
|
return null;
|
|
21378
21659
|
}
|
|
21379
21660
|
}
|
|
21380
21661
|
function realOrSelf(p) {
|
|
21381
21662
|
try {
|
|
21382
|
-
return
|
|
21663
|
+
return realpathSync8(p);
|
|
21383
21664
|
} catch {
|
|
21384
21665
|
return p;
|
|
21385
21666
|
}
|
|
21386
21667
|
}
|
|
21387
21668
|
|
|
21388
21669
|
// dist/request-shapes-verb.js
|
|
21389
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
21670
|
+
import { chmodSync as chmodSync4, existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync11, writeFileSync as writeFileSync10 } from "node:fs";
|
|
21390
21671
|
import { homedir as homedir9, platform as platform5 } from "node:os";
|
|
21391
|
-
import { join as
|
|
21672
|
+
import { join as join32 } from "node:path";
|
|
21392
21673
|
var REQUEST_SHAPES_USAGE = `${ownCommand()} request-shapes on|off|show|clear`;
|
|
21393
21674
|
var REQUEST_SHAPES_FORMS = ["on", "off", "show", "clear"];
|
|
21394
21675
|
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 +21714,7 @@ function formatLine(line) {
|
|
|
21433
21714
|
function showLog(logPath2) {
|
|
21434
21715
|
let text = "";
|
|
21435
21716
|
try {
|
|
21436
|
-
text =
|
|
21717
|
+
text = existsSync18(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
|
|
21437
21718
|
} catch {
|
|
21438
21719
|
text = "";
|
|
21439
21720
|
}
|
|
@@ -21451,9 +21732,9 @@ function ownerOnly(path, mode) {
|
|
|
21451
21732
|
chmodSync4(path, mode);
|
|
21452
21733
|
}
|
|
21453
21734
|
function runRequestShapes(form, home = homedir9()) {
|
|
21454
|
-
const folder2 =
|
|
21455
|
-
const switchPath =
|
|
21456
|
-
const logPath2 =
|
|
21735
|
+
const folder2 = join32(home, HALFCYCLE_DIR_NAME, REQUEST_SHAPES_DIR_NAME);
|
|
21736
|
+
const switchPath = join32(folder2, REQUEST_SHAPES_SWITCH_FILENAME);
|
|
21737
|
+
const logPath2 = join32(folder2, REQUEST_SHAPES_LOG_FILENAME);
|
|
21457
21738
|
try {
|
|
21458
21739
|
switch (form) {
|
|
21459
21740
|
case "on":
|
|
@@ -21464,13 +21745,13 @@ function runRequestShapes(form, home = homedir9()) {
|
|
|
21464
21745
|
return { stdout: `${REQUEST_SHAPES_ON_LINE}
|
|
21465
21746
|
`, stderr: "", exitCode: 0 };
|
|
21466
21747
|
case "off":
|
|
21467
|
-
|
|
21748
|
+
rmSync11(switchPath, { force: true });
|
|
21468
21749
|
return { stdout: `${REQUEST_SHAPES_OFF_LINE}
|
|
21469
21750
|
`, stderr: "", exitCode: 0 };
|
|
21470
21751
|
case "show":
|
|
21471
21752
|
return { stdout: showLog(logPath2), stderr: "", exitCode: 0 };
|
|
21472
21753
|
case "clear":
|
|
21473
|
-
if (
|
|
21754
|
+
if (existsSync18(logPath2)) {
|
|
21474
21755
|
writeFileSync10(logPath2, "", { mode: 384 });
|
|
21475
21756
|
ownerOnly(logPath2, 384);
|
|
21476
21757
|
}
|
|
@@ -21779,7 +22060,7 @@ ${USAGE}`);
|
|
|
21779
22060
|
if (installArm) {
|
|
21780
22061
|
const engagementIdArg = positionals[1];
|
|
21781
22062
|
const engagementTypeRaw = positionals[2] ?? "client";
|
|
21782
|
-
if (!
|
|
22063
|
+
if (!existsSync19(targetRepo)) {
|
|
21783
22064
|
process.stderr.write(`${folderMissingRefusal(targetRepo)}
|
|
21784
22065
|
`);
|
|
21785
22066
|
process.exit(1);
|
|
@@ -22187,6 +22468,13 @@ ${USAGE}`);
|
|
|
22187
22468
|
const obtained = await obtainBuildRecord(credential, repoRoot, phaseArg);
|
|
22188
22469
|
if (obtained.kind === "written") {
|
|
22189
22470
|
process.stdout.write(writtenLines(obtained));
|
|
22471
|
+
const stamped = readEngagementEnv(credential.engagementId)?.[PHASE_STAMP_ENV_KEY]?.trim();
|
|
22472
|
+
if (stamped === void 0 || stamped === "" || stamped === phaseArg) {
|
|
22473
|
+
const tidied = tidyWorkerCopies(repoRoot);
|
|
22474
|
+
if (tidied.message !== void 0)
|
|
22475
|
+
process.stdout.write(`${tidied.message}
|
|
22476
|
+
`);
|
|
22477
|
+
}
|
|
22190
22478
|
if (obtained.stampFailure) {
|
|
22191
22479
|
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
22480
|
halfcycle build-record: until ${obtained.stampFailure.path} can be written, the checks run on this computer are still filed under the phase that closed.
|