halfcycle 0.12.0-rc.2 → 0.12.0-rc.4
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/dist/cli.js +136 -64
- package/dist/cli.js.map +3 -3
- package/dist/index.js +16 -12
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +9 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/local-allow.d.ts +16 -0
- package/dist/local-allow.d.ts.map +1 -1
- package/dist/ready.d.ts +9 -2
- package/dist/ready.d.ts.map +1 -1
- package/dist/setup-committed.d.ts +38 -0
- package/dist/setup-committed.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halfcycle",
|
|
3
|
-
"version": "0.12.0-rc.
|
|
3
|
+
"version": "0.12.0-rc.4",
|
|
4
4
|
"description": "Halfcycle Method bundle — resolution-stub slash commands, remote method-delivery registration, and governance-hook wiring for a Halfcycle engagement repo. It ships NO worker role files: a project's roles are authored from that project's own recorded decisions at orchestration kickoff (FX-2, W3-F-27).",
|
|
5
5
|
"commands": [
|
|
6
6
|
{
|
package/dist/cli.js
CHANGED
|
@@ -2552,7 +2552,7 @@ if (switchedOn) {
|
|
|
2552
2552
|
}
|
|
2553
2553
|
|
|
2554
2554
|
// dist/bin.js
|
|
2555
|
-
import { existsSync as
|
|
2555
|
+
import { existsSync as existsSync18, statSync as statSync3, writeSync as writeSync4 } from "node:fs";
|
|
2556
2556
|
|
|
2557
2557
|
// dist/install.js
|
|
2558
2558
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -9879,6 +9879,21 @@ function assertManifestInWriteSet(manifest, writeSet) {
|
|
|
9879
9879
|
}
|
|
9880
9880
|
}
|
|
9881
9881
|
function findNotShared(targetRepo, manifest, collidedPaths) {
|
|
9882
|
+
const paths = setupFilesToShare(targetRepo, manifest, collidedPaths);
|
|
9883
|
+
const ignored = readIgnoredPaths(targetRepo, paths);
|
|
9884
|
+
if (ignored === null)
|
|
9885
|
+
return null;
|
|
9886
|
+
const sorted = [...ignored].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
9887
|
+
const named2 = sorted.find((entry) => entry.path === SETTINGS_REL) ?? sorted[0];
|
|
9888
|
+
return {
|
|
9889
|
+
path: named2.path,
|
|
9890
|
+
pattern: named2.pattern,
|
|
9891
|
+
source: named2.source,
|
|
9892
|
+
line: named2.line,
|
|
9893
|
+
others: sorted.length - 1
|
|
9894
|
+
};
|
|
9895
|
+
}
|
|
9896
|
+
function setupFilesToShare(targetRepo, manifest, collidedPaths) {
|
|
9882
9897
|
const merged = [
|
|
9883
9898
|
SETTINGS_REL,
|
|
9884
9899
|
...manifest.mcp !== void 0 ? [MCP_REGISTRATION_REL] : [],
|
|
@@ -9909,18 +9924,7 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
|
|
|
9909
9924
|
INSTALL_MANIFEST_REL
|
|
9910
9925
|
])
|
|
9911
9926
|
].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join8(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
|
|
9912
|
-
|
|
9913
|
-
if (ignored === null)
|
|
9914
|
-
return null;
|
|
9915
|
-
const sorted = [...ignored].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
9916
|
-
const named2 = sorted.find((entry) => entry.path === SETTINGS_REL) ?? sorted[0];
|
|
9917
|
-
return {
|
|
9918
|
-
path: named2.path,
|
|
9919
|
-
pattern: named2.pattern,
|
|
9920
|
-
source: named2.source,
|
|
9921
|
-
line: named2.line,
|
|
9922
|
-
others: sorted.length - 1
|
|
9923
|
-
};
|
|
9927
|
+
return paths;
|
|
9924
9928
|
}
|
|
9925
9929
|
function lstatOrNull(path) {
|
|
9926
9930
|
try {
|
|
@@ -13537,6 +13541,8 @@ var SCRIPTS = ["test", "build", "lint", "typecheck"];
|
|
|
13537
13541
|
var SCRIPT_RUNNERS = ["npm run", "pnpm", "pnpm run", "yarn", "bun run"];
|
|
13538
13542
|
var HALFCYCLE_TOOLS_RULE = `mcp__${MCP_SERVER_KEY}`;
|
|
13539
13543
|
var bash = (command) => `Bash(${command}:*)`;
|
|
13544
|
+
var CONTEXT_BUDGET_IGNORE_CHECK = "git check-ignore -q .halfcycle/context-budget.json";
|
|
13545
|
+
var COMMIT_FILE_LISTING = "git ls-tree -r --name-only";
|
|
13540
13546
|
var INSTALLED_HOOK_SCRIPTS = Object.keys(OWNED_GENERATED_HEADERS).filter((path) => path.startsWith(".claude/hooks/"));
|
|
13541
13547
|
var LOCAL_ALLOW_RULES = [
|
|
13542
13548
|
...SCRIPT_RUNNERS.flatMap((runner) => SCRIPTS.map((script) => bash(`${runner} ${script}`))),
|
|
@@ -13561,6 +13567,7 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13561
13567
|
bash("git diff"),
|
|
13562
13568
|
bash("git log"),
|
|
13563
13569
|
bash("git show"),
|
|
13570
|
+
bash(COMMIT_FILE_LISTING),
|
|
13564
13571
|
bash("git add"),
|
|
13565
13572
|
bash("git commit"),
|
|
13566
13573
|
bash("git switch"),
|
|
@@ -13570,6 +13577,7 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13570
13577
|
bash("git merge"),
|
|
13571
13578
|
bash("git worktree add"),
|
|
13572
13579
|
bash("git worktree list"),
|
|
13580
|
+
`Bash(${CONTEXT_BUDGET_IGNORE_CHECK})`,
|
|
13573
13581
|
bash("git pull --ff-only"),
|
|
13574
13582
|
bash("git pull --rebase"),
|
|
13575
13583
|
bash("npm install"),
|
|
@@ -19070,6 +19078,65 @@ function runCheckReport(source, head, deps = {}) {
|
|
|
19070
19078
|
`).join(""), stderr: "", exitCode: 1 };
|
|
19071
19079
|
}
|
|
19072
19080
|
|
|
19081
|
+
// dist/setup-committed.js
|
|
19082
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
19083
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
19084
|
+
import { join as join26 } from "node:path";
|
|
19085
|
+
var SETUP_COMMIT_MESSAGE = "Add Halfcycle setup files";
|
|
19086
|
+
var SETUP_UNCOMMITTED_LINES = {
|
|
19087
|
+
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:",
|
|
19088
|
+
add: (paths) => `git add -- ${paths.map(shellWord).join(" ")}`,
|
|
19089
|
+
commit: (paths) => `git commit -m "${SETUP_COMMIT_MESSAGE}" -- ${paths.map(shellWord).join(" ")}`
|
|
19090
|
+
};
|
|
19091
|
+
function shellWord(path) {
|
|
19092
|
+
return /^[A-Za-z0-9._/@+=-]+$/.test(path) ? path : `'${path.replace(/'/g, `'\\''`)}'`;
|
|
19093
|
+
}
|
|
19094
|
+
function uncommittedSetupFiles(repoRoot) {
|
|
19095
|
+
if (!existsSync15(join26(repoRoot, INSTALL_MANIFEST_REL)))
|
|
19096
|
+
return [];
|
|
19097
|
+
const manifest = readPreviousManifest(repoRoot);
|
|
19098
|
+
if (manifest === null)
|
|
19099
|
+
return [];
|
|
19100
|
+
const paths = setupFilesToShare(repoRoot, manifest, manifest.leftAlone);
|
|
19101
|
+
if (paths.length === 0)
|
|
19102
|
+
return [];
|
|
19103
|
+
let out;
|
|
19104
|
+
let prefix;
|
|
19105
|
+
try {
|
|
19106
|
+
const run = (args2) => execFileSync7("git", ["-C", repoRoot, ...args2], {
|
|
19107
|
+
encoding: "utf-8",
|
|
19108
|
+
env: gitEnvForNamedRepo(),
|
|
19109
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
19110
|
+
maxBuffer: 64 * 1024 * 1024
|
|
19111
|
+
});
|
|
19112
|
+
prefix = run(["rev-parse", "--show-prefix"]).replace(/\r?\n$/, "");
|
|
19113
|
+
out = run(["--literal-pathspecs", "status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...paths]);
|
|
19114
|
+
} catch {
|
|
19115
|
+
return [];
|
|
19116
|
+
}
|
|
19117
|
+
const wanted = new Set(paths);
|
|
19118
|
+
const found = /* @__PURE__ */ new Set();
|
|
19119
|
+
const fields = out.split("\0");
|
|
19120
|
+
for (let i = 0; i < fields.length; i++) {
|
|
19121
|
+
const entry = fields[i];
|
|
19122
|
+
if (entry.length < 4)
|
|
19123
|
+
continue;
|
|
19124
|
+
const xy = entry.slice(0, 2);
|
|
19125
|
+
const fromTop = entry.slice(3);
|
|
19126
|
+
if (xy.includes("R") || xy.includes("C"))
|
|
19127
|
+
i++;
|
|
19128
|
+
const rel = prefix !== "" && fromTop.startsWith(prefix) ? fromTop.slice(prefix.length) : fromTop;
|
|
19129
|
+
if (wanted.has(rel))
|
|
19130
|
+
found.add(rel);
|
|
19131
|
+
}
|
|
19132
|
+
return [...found].sort();
|
|
19133
|
+
}
|
|
19134
|
+
function setupUncommittedLines(paths) {
|
|
19135
|
+
if (paths.length === 0)
|
|
19136
|
+
return [];
|
|
19137
|
+
return [SETUP_UNCOMMITTED_LINES.intro(), SETUP_UNCOMMITTED_LINES.add(paths), SETUP_UNCOMMITTED_LINES.commit(paths)];
|
|
19138
|
+
}
|
|
19139
|
+
|
|
19073
19140
|
// dist/ready.js
|
|
19074
19141
|
var READY_USAGE = `${ownCommand()} ready <phase> [--cap <n>] [--repo <root>]`;
|
|
19075
19142
|
var STATES_TEXT = buildTaskStateSchema.options.join(", ");
|
|
@@ -19202,6 +19269,11 @@ function runReady(repoRoot, phase, cap) {
|
|
|
19202
19269
|
if (!read.ok)
|
|
19203
19270
|
return failed2(read.lines.map((line) => `${line}
|
|
19204
19271
|
`).join(""));
|
|
19272
|
+
const uncommitted = setupUncommittedLines(uncommittedSetupFiles(repoRoot));
|
|
19273
|
+
if (uncommitted.length > 0) {
|
|
19274
|
+
return { stdout: uncommitted.map((line) => `${line}
|
|
19275
|
+
`).join(""), stderr: "", exitCode: 1 };
|
|
19276
|
+
}
|
|
19205
19277
|
const titles = new Map(read.rows.map((row) => [row.id, row.title]));
|
|
19206
19278
|
const set = readyTasks(read.rows);
|
|
19207
19279
|
const out = [];
|
|
@@ -19296,9 +19368,9 @@ function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
|
19296
19368
|
}
|
|
19297
19369
|
|
|
19298
19370
|
// dist/uninstall.js
|
|
19299
|
-
import { existsSync as
|
|
19371
|
+
import { existsSync as existsSync16, lstatSync as lstatSync4, readFileSync as readFileSync23, readdirSync, rmSync as rmSync8, rmdirSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
19300
19372
|
import { homedir as homedir7 } from "node:os";
|
|
19301
|
-
import { dirname as dirname8, join as
|
|
19373
|
+
import { dirname as dirname8, join as join27, relative as relative6, sep as sep2 } from "node:path";
|
|
19302
19374
|
var REMOVE_CREDENTIAL_FLAG = "--remove-credential";
|
|
19303
19375
|
function parseUninstallArgs(args2) {
|
|
19304
19376
|
let removeCredential = false;
|
|
@@ -19333,7 +19405,7 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19333
19405
|
const { rel } = rule;
|
|
19334
19406
|
if (symlinkOnPath(targetRepo, rel))
|
|
19335
19407
|
return { state: "symlink" };
|
|
19336
|
-
const path =
|
|
19408
|
+
const path = join27(targetRepo, rel);
|
|
19337
19409
|
if (!lexists(path))
|
|
19338
19410
|
return { state: "absent" };
|
|
19339
19411
|
let text;
|
|
@@ -19356,8 +19428,8 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19356
19428
|
}
|
|
19357
19429
|
function validate(options, rules) {
|
|
19358
19430
|
const { targetRepo, removeCredential, home } = options;
|
|
19359
|
-
const manifestPath =
|
|
19360
|
-
const pinPath =
|
|
19431
|
+
const manifestPath = join27(targetRepo, INSTALL_MANIFEST_REL);
|
|
19432
|
+
const pinPath = join27(targetRepo, BUNDLE_PIN_REL);
|
|
19361
19433
|
if (symlinkOnPath(targetRepo, INSTALL_MANIFEST_REL) || symlinkOnPath(targetRepo, BUNDLE_PIN_REL)) {
|
|
19362
19434
|
throw new Refusal(`.halfcycle, or a file Halfcycle keeps in it, is a symbolic link here, so nothing was followed and nothing was removed.`);
|
|
19363
19435
|
}
|
|
@@ -19398,7 +19470,7 @@ function validate(options, rules) {
|
|
|
19398
19470
|
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.`);
|
|
19399
19471
|
}
|
|
19400
19472
|
if (engagementId !== void 0) {
|
|
19401
|
-
const expectedParent =
|
|
19473
|
+
const expectedParent = join27(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
|
|
19402
19474
|
if (dirname8(engagementStateDir(engagementId, home)) !== expectedParent) {
|
|
19403
19475
|
throw new Refusal(`the project id in ${BUNDLE_PIN_REL} does not name a folder under ~/.halfcycle/engagements. Nothing was removed.`);
|
|
19404
19476
|
}
|
|
@@ -19424,7 +19496,7 @@ function mergedFileRules(writeSet) {
|
|
|
19424
19496
|
function writeBack(targetRepo, rel, current, next, lists, name) {
|
|
19425
19497
|
if (next === current)
|
|
19426
19498
|
return;
|
|
19427
|
-
const path =
|
|
19499
|
+
const path = join27(targetRepo, rel);
|
|
19428
19500
|
try {
|
|
19429
19501
|
if (next === null) {
|
|
19430
19502
|
rmSync8(path);
|
|
@@ -19441,7 +19513,7 @@ function statusLineClaimedFromTree(targetRepo, writeSet) {
|
|
|
19441
19513
|
try {
|
|
19442
19514
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19443
19515
|
return {};
|
|
19444
|
-
const value = JSON.parse(readFileSync23(
|
|
19516
|
+
const value = JSON.parse(readFileSync23(join27(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19445
19517
|
if (!isPlainObject4(value) || !("statusLine" in value))
|
|
19446
19518
|
return {};
|
|
19447
19519
|
const hash = canonicalSha256(value["statusLine"]);
|
|
@@ -19454,7 +19526,7 @@ function attributionClaimedFromTree(targetRepo, writeSet) {
|
|
|
19454
19526
|
try {
|
|
19455
19527
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19456
19528
|
return {};
|
|
19457
|
-
const value = JSON.parse(readFileSync23(
|
|
19529
|
+
const value = JSON.parse(readFileSync23(join27(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19458
19530
|
if (!isPlainObject4(value) || !("attribution" in value))
|
|
19459
19531
|
return {};
|
|
19460
19532
|
const hash = canonicalSha256(value["attribution"]);
|
|
@@ -19468,7 +19540,7 @@ function noRecord(targetRepo, writeSet) {
|
|
|
19468
19540
|
const claim = (rel, header, lines) => {
|
|
19469
19541
|
let text;
|
|
19470
19542
|
try {
|
|
19471
|
-
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(
|
|
19543
|
+
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(join27(targetRepo, rel), "utf-8");
|
|
19472
19544
|
} catch {
|
|
19473
19545
|
text = "";
|
|
19474
19546
|
}
|
|
@@ -19714,7 +19786,7 @@ function lastOccurrence(text, block) {
|
|
|
19714
19786
|
}
|
|
19715
19787
|
function coveredPathExists(targetRepo, line) {
|
|
19716
19788
|
const path = line.trim().replace(/^\//, "").replace(/\/$/, "");
|
|
19717
|
-
return path !== "" && lexists(
|
|
19789
|
+
return path !== "" && lexists(join27(targetRepo, path));
|
|
19718
19790
|
}
|
|
19719
19791
|
function selfBuildMergedFileRules() {
|
|
19720
19792
|
return {
|
|
@@ -19728,7 +19800,7 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19728
19800
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19729
19801
|
return;
|
|
19730
19802
|
}
|
|
19731
|
-
const abs =
|
|
19803
|
+
const abs = join27(targetRepo, rel);
|
|
19732
19804
|
let isFile;
|
|
19733
19805
|
try {
|
|
19734
19806
|
isFile = lstatSync4(abs).isFile();
|
|
@@ -19752,7 +19824,7 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19752
19824
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19753
19825
|
return;
|
|
19754
19826
|
}
|
|
19755
|
-
const abs =
|
|
19827
|
+
const abs = join27(targetRepo, rel);
|
|
19756
19828
|
let bytes;
|
|
19757
19829
|
try {
|
|
19758
19830
|
bytes = readFileSync23(abs);
|
|
@@ -19779,7 +19851,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19779
19851
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19780
19852
|
return;
|
|
19781
19853
|
}
|
|
19782
|
-
const abs =
|
|
19854
|
+
const abs = join27(targetRepo, rel);
|
|
19783
19855
|
let bytes;
|
|
19784
19856
|
try {
|
|
19785
19857
|
bytes = readFileSync23(abs);
|
|
@@ -19804,7 +19876,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19804
19876
|
function removeSelfBuildEmptyDir(targetRepo, dir, lists, name) {
|
|
19805
19877
|
if (symlinkOnPath(targetRepo, dir))
|
|
19806
19878
|
return;
|
|
19807
|
-
const abs =
|
|
19879
|
+
const abs = join27(targetRepo, dir);
|
|
19808
19880
|
try {
|
|
19809
19881
|
if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
|
|
19810
19882
|
return;
|
|
@@ -19838,7 +19910,7 @@ function uninstallSelfBuild(options) {
|
|
|
19838
19910
|
throw err;
|
|
19839
19911
|
}
|
|
19840
19912
|
const { targetRepo } = options;
|
|
19841
|
-
const pinExisted = lexists(
|
|
19913
|
+
const pinExisted = lexists(join27(targetRepo, BUNDLE_PIN_REL));
|
|
19842
19914
|
const name = (list2, text, _path) => {
|
|
19843
19915
|
list2.push(text);
|
|
19844
19916
|
};
|
|
@@ -19925,7 +19997,7 @@ function uninstall(options) {
|
|
|
19925
19997
|
}
|
|
19926
19998
|
const serverRemoved = removeOwnServerApproval(targetRepo);
|
|
19927
19999
|
if (listRemoved || serverRemoved) {
|
|
19928
|
-
if (
|
|
20000
|
+
if (existsSync16(join27(targetRepo, LOCAL_SETTINGS_REL2))) {
|
|
19929
20001
|
name(result.restored, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
19930
20002
|
} else {
|
|
19931
20003
|
name(result.removed, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
@@ -19967,7 +20039,7 @@ function uninstall(options) {
|
|
|
19967
20039
|
const gitattributes = manifest.gitattributes ?? { created: false, appended: [] };
|
|
19968
20040
|
trimAppendedFile(targetRepo, gitattributes, deleteWhenEmpty(gitattributes.created), gitattributesRules(writeSet), result, name);
|
|
19969
20041
|
const foundNothing = result.removed.length + result.restored.length + result.kept.length + result.failed.length === 0 && result.keptIgnoreLines.length === 0;
|
|
19970
|
-
if (checked.manifest === null && !lexists(
|
|
20042
|
+
if (checked.manifest === null && !lexists(join27(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
|
|
19971
20043
|
return { outcome: "not-installed", exitCode: 0, ...result, credential: nothingToSay };
|
|
19972
20044
|
}
|
|
19973
20045
|
const credential = handleCredential(options, checked);
|
|
@@ -19975,7 +20047,7 @@ function uninstall(options) {
|
|
|
19975
20047
|
if (result.failed.length === 0 && !credentialFailed) {
|
|
19976
20048
|
removeInstalledFile(targetRepo, BUNDLE_PIN_REL, recorded.get(BUNDLE_PIN_REL), writeSet, result, name);
|
|
19977
20049
|
try {
|
|
19978
|
-
rmSync8(
|
|
20050
|
+
rmSync8(join27(targetRepo, INSTALL_MANIFEST_REL), { force: true });
|
|
19979
20051
|
named2.add(INSTALL_MANIFEST_REL);
|
|
19980
20052
|
} catch {
|
|
19981
20053
|
name(result.failed, INSTALL_MANIFEST_REL, INSTALL_MANIFEST_REL);
|
|
@@ -20010,13 +20082,13 @@ function applyMergedJson(targetRepo, rule, file, deleteWhenEmpty, lists, name, e
|
|
|
20010
20082
|
return;
|
|
20011
20083
|
const empty = Object.keys(value).length === 0 && deleteWhenEmpty;
|
|
20012
20084
|
writeBack(targetRepo, rel, file.text, empty ? null : inLineEndingOf(file.text, serializeJson(value)), lists, name);
|
|
20013
|
-
if (adopted && lexists(
|
|
20085
|
+
if (adopted && lexists(join27(targetRepo, rel))) {
|
|
20014
20086
|
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);
|
|
20015
20087
|
}
|
|
20016
20088
|
}
|
|
20017
20089
|
function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, name) {
|
|
20018
20090
|
const rel = CODEX_CONFIG_REL2;
|
|
20019
|
-
const path =
|
|
20091
|
+
const path = join27(targetRepo, rel);
|
|
20020
20092
|
if (symlinkOnPath(targetRepo, rel)) {
|
|
20021
20093
|
if (lexists(path))
|
|
20022
20094
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
@@ -20061,7 +20133,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20061
20133
|
name(lists.leftAlone, `${path} (a symbolic link, or inside one \u2014 not followed)`, path);
|
|
20062
20134
|
return;
|
|
20063
20135
|
}
|
|
20064
|
-
const abs =
|
|
20136
|
+
const abs = join27(targetRepo, path);
|
|
20065
20137
|
let isFile;
|
|
20066
20138
|
try {
|
|
20067
20139
|
isFile = lstatSync4(abs).isFile();
|
|
@@ -20095,7 +20167,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
20095
20167
|
function removeEmptyDir(targetRepo, dir, writeSet, lists, name) {
|
|
20096
20168
|
if (!writeSet.dirs.has(dir) || symlinkOnPath(targetRepo, dir))
|
|
20097
20169
|
return;
|
|
20098
|
-
const abs =
|
|
20170
|
+
const abs = join27(targetRepo, dir);
|
|
20099
20171
|
try {
|
|
20100
20172
|
if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
|
|
20101
20173
|
return;
|
|
@@ -20114,7 +20186,7 @@ function trimAppendedFile(targetRepo, rec, deleteWhenEmpty, rules, lists, name)
|
|
|
20114
20186
|
name(lists.leftAlone, `${rel} (a symbolic link \u2014 not followed)`, rel);
|
|
20115
20187
|
return [];
|
|
20116
20188
|
}
|
|
20117
|
-
const path =
|
|
20189
|
+
const path = join27(targetRepo, rel);
|
|
20118
20190
|
let before;
|
|
20119
20191
|
try {
|
|
20120
20192
|
before = readFileSync23(path, "utf-8");
|
|
@@ -20161,16 +20233,16 @@ function filesUnder(root, rel) {
|
|
|
20161
20233
|
const walk = (dirRel) => {
|
|
20162
20234
|
let names;
|
|
20163
20235
|
try {
|
|
20164
|
-
if (!lstatSync4(
|
|
20236
|
+
if (!lstatSync4(join27(root, dirRel)).isDirectory())
|
|
20165
20237
|
return;
|
|
20166
|
-
names = readdirSync(
|
|
20238
|
+
names = readdirSync(join27(root, dirRel));
|
|
20167
20239
|
} catch {
|
|
20168
20240
|
return;
|
|
20169
20241
|
}
|
|
20170
20242
|
for (const entry of names.sort()) {
|
|
20171
20243
|
const childRel = `${dirRel}/${entry}`;
|
|
20172
20244
|
try {
|
|
20173
|
-
if (lstatSync4(
|
|
20245
|
+
if (lstatSync4(join27(root, childRel)).isDirectory())
|
|
20174
20246
|
walk(childRel);
|
|
20175
20247
|
else
|
|
20176
20248
|
out.push(childRel);
|
|
@@ -20675,7 +20747,7 @@ async function runCrew(repoRoot, deps) {
|
|
|
20675
20747
|
// dist/pace.js
|
|
20676
20748
|
import { spawn as spawn3 } from "node:child_process";
|
|
20677
20749
|
import { readFileSync as readFileSync24 } from "node:fs";
|
|
20678
|
-
import { join as
|
|
20750
|
+
import { join as join28 } from "node:path";
|
|
20679
20751
|
var PACE_USAGE = `${ownCommand()} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
|
|
20680
20752
|
function parsePaceArgs(argv) {
|
|
20681
20753
|
let repoRoot;
|
|
@@ -20789,7 +20861,7 @@ function notPacedLine(reason) {
|
|
|
20789
20861
|
}
|
|
20790
20862
|
function readPromptScript(repoRoot) {
|
|
20791
20863
|
try {
|
|
20792
|
-
return readFileSync24(
|
|
20864
|
+
return readFileSync24(join28(repoRoot, ".claude", "hooks", "user-prompt-scope.sh"), "utf-8");
|
|
20793
20865
|
} catch {
|
|
20794
20866
|
return null;
|
|
20795
20867
|
}
|
|
@@ -20849,7 +20921,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
|
|
|
20849
20921
|
|
|
20850
20922
|
// dist/route.js
|
|
20851
20923
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
20852
|
-
import { join as
|
|
20924
|
+
import { join as join29 } from "node:path";
|
|
20853
20925
|
var ROUTE_USAGE = `${ownCommand()} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
|
|
20854
20926
|
var ROUTE_WORK_READ_DEADLINE_MS = 5e3;
|
|
20855
20927
|
function parseRouteArgs(argv) {
|
|
@@ -20892,7 +20964,7 @@ function parseRouteArgs(argv) {
|
|
|
20892
20964
|
return { ok: true, phase: positionals[0], task: positionals[1], agent: agent ?? "claude-code", repoRoot };
|
|
20893
20965
|
}
|
|
20894
20966
|
function routingPath(engagementId, home) {
|
|
20895
|
-
return
|
|
20967
|
+
return join29(engagementStateDir(engagementId, home), ROUTING_FILENAME);
|
|
20896
20968
|
}
|
|
20897
20969
|
function readCheapStarted(path, phase) {
|
|
20898
20970
|
try {
|
|
@@ -20985,7 +21057,7 @@ async function runRoute(repoRoot, phase, taskId, agent, deps) {
|
|
|
20985
21057
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
20986
21058
|
import { closeSync as closeSync3, fchmodSync, lstatSync as lstatSync5, mkdirSync as mkdirSync12, openSync as openSync3, readdirSync as readdirSync2, readFileSync as readFileSync26, readlinkSync, realpathSync as realpathSync7, rmSync as rmSync9, symlinkSync, writeSync as writeSync3 } from "node:fs";
|
|
20987
21059
|
import { homedir as homedir8 } from "node:os";
|
|
20988
|
-
import { join as
|
|
21060
|
+
import { join as join30, resolve as resolve8 } from "node:path";
|
|
20989
21061
|
var RESTORE_USAGE = `${ownCommand()} restore [<id>]`;
|
|
20990
21062
|
function parseRestoreArgs(rest) {
|
|
20991
21063
|
if (rest.length > 1)
|
|
@@ -21007,7 +21079,7 @@ function runRestore(cwd, id, options = {}) {
|
|
|
21007
21079
|
if (engagementId === null) {
|
|
21008
21080
|
return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${ownCommand()}`);
|
|
21009
21081
|
}
|
|
21010
|
-
const folder2 =
|
|
21082
|
+
const folder2 = join30(options.home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
|
|
21011
21083
|
const refs = savedRefs(root);
|
|
21012
21084
|
if (refs === null)
|
|
21013
21085
|
return refused2("Halfcycle could not read the saved copies in this project, so nothing was put back.");
|
|
@@ -21083,7 +21155,7 @@ function restoreOne(root, folder2, refs, rawId) {
|
|
|
21083
21155
|
if (p.keptIn === "commit")
|
|
21084
21156
|
fromCommit.push(p.path);
|
|
21085
21157
|
else
|
|
21086
|
-
restoreCopy(root,
|
|
21158
|
+
restoreCopy(root, join30(folder2, id), p.path, out);
|
|
21087
21159
|
}
|
|
21088
21160
|
if (fromCommit.length > 0)
|
|
21089
21161
|
restoreFromCommit(root, ref, fromCommit, out);
|
|
@@ -21111,7 +21183,7 @@ function makeFolders(root, rel) {
|
|
|
21111
21183
|
const segments = rel.split("/");
|
|
21112
21184
|
let at = root;
|
|
21113
21185
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
21114
|
-
at =
|
|
21186
|
+
at = join30(at, segments[i]);
|
|
21115
21187
|
const shown2 = segments.slice(0, i + 1).join("/");
|
|
21116
21188
|
let st = lstatOrNull3(at);
|
|
21117
21189
|
if (st === null) {
|
|
@@ -21131,7 +21203,7 @@ function makeFolders(root, rel) {
|
|
|
21131
21203
|
return null;
|
|
21132
21204
|
}
|
|
21133
21205
|
function place(root, rel, content, out) {
|
|
21134
|
-
const abs =
|
|
21206
|
+
const abs = join30(root, rel);
|
|
21135
21207
|
const there = lstatOrNull3(abs);
|
|
21136
21208
|
if (there !== null) {
|
|
21137
21209
|
if (sameAs(abs, there, content))
|
|
@@ -21179,14 +21251,14 @@ function restoreCopy(root, saveDir, rel, out) {
|
|
|
21179
21251
|
let at = saveDir;
|
|
21180
21252
|
const chain = [SAVED_COPIES_DIR_NAME, ...segments];
|
|
21181
21253
|
for (let i = 0; i < chain.length - 1; i++) {
|
|
21182
|
-
at =
|
|
21254
|
+
at = join30(at, chain[i]);
|
|
21183
21255
|
const st2 = lstatOrNull3(at);
|
|
21184
21256
|
if (st2 === null || st2.isSymbolicLink() || !st2.isDirectory()) {
|
|
21185
21257
|
out.left.push(`${rel}: the saved copy does not hold it`);
|
|
21186
21258
|
return;
|
|
21187
21259
|
}
|
|
21188
21260
|
}
|
|
21189
|
-
const src =
|
|
21261
|
+
const src = join30(at, chain[chain.length - 1]);
|
|
21190
21262
|
const st = lstatOrNull3(src);
|
|
21191
21263
|
try {
|
|
21192
21264
|
if (st !== null && st.isSymbolicLink())
|
|
@@ -21290,7 +21362,7 @@ function restoreLines(root, ref, saved, out) {
|
|
|
21290
21362
|
}
|
|
21291
21363
|
function pinnedEngagement(root) {
|
|
21292
21364
|
try {
|
|
21293
|
-
const pin = JSON.parse(readFileSync26(
|
|
21365
|
+
const pin = JSON.parse(readFileSync26(join30(root, BUNDLE_PIN_REL), "utf-8"));
|
|
21294
21366
|
return isEngagementId(pin.engagementId) ? pin.engagementId : null;
|
|
21295
21367
|
} catch {
|
|
21296
21368
|
return null;
|
|
@@ -21312,11 +21384,11 @@ function savedRefs(root) {
|
|
|
21312
21384
|
return refs;
|
|
21313
21385
|
}
|
|
21314
21386
|
function readSaved(folder2, id) {
|
|
21315
|
-
const dir =
|
|
21387
|
+
const dir = join30(folder2, id);
|
|
21316
21388
|
const st = lstatOrNull3(dir);
|
|
21317
21389
|
if (st === null || !st.isDirectory())
|
|
21318
21390
|
return null;
|
|
21319
|
-
const file =
|
|
21391
|
+
const file = join30(dir, SAVED_FILE_NAME);
|
|
21320
21392
|
const fst = lstatOrNull3(file);
|
|
21321
21393
|
if (fst === null || !fst.isFile())
|
|
21322
21394
|
return null;
|
|
@@ -21342,7 +21414,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21342
21414
|
if (!savedIdSchema.safeParse(id).success)
|
|
21343
21415
|
continue;
|
|
21344
21416
|
here.add(id);
|
|
21345
|
-
const dir =
|
|
21417
|
+
const dir = join30(folder2, id);
|
|
21346
21418
|
const st = lstatOrNull3(dir);
|
|
21347
21419
|
const answer = savedPruneAnswer({
|
|
21348
21420
|
refMadeAt: refs.get(id) ?? null,
|
|
@@ -21370,7 +21442,7 @@ function pruneAtRestore(root, folder2, refs, now) {
|
|
|
21370
21442
|
}
|
|
21371
21443
|
function markedRepository(dir) {
|
|
21372
21444
|
try {
|
|
21373
|
-
const text = readFileSync26(
|
|
21445
|
+
const text = readFileSync26(join30(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
|
|
21374
21446
|
return text === "" ? null : text;
|
|
21375
21447
|
} catch {
|
|
21376
21448
|
return null;
|
|
@@ -21400,9 +21472,9 @@ function realOrSelf(p) {
|
|
|
21400
21472
|
}
|
|
21401
21473
|
|
|
21402
21474
|
// dist/request-shapes-verb.js
|
|
21403
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
21475
|
+
import { chmodSync as chmodSync4, existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
21404
21476
|
import { homedir as homedir9, platform as platform5 } from "node:os";
|
|
21405
|
-
import { join as
|
|
21477
|
+
import { join as join31 } from "node:path";
|
|
21406
21478
|
var REQUEST_SHAPES_USAGE = `${ownCommand()} request-shapes on|off|show|clear`;
|
|
21407
21479
|
var REQUEST_SHAPES_FORMS = ["on", "off", "show", "clear"];
|
|
21408
21480
|
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.";
|
|
@@ -21447,7 +21519,7 @@ function formatLine(line) {
|
|
|
21447
21519
|
function showLog(logPath2) {
|
|
21448
21520
|
let text = "";
|
|
21449
21521
|
try {
|
|
21450
|
-
text =
|
|
21522
|
+
text = existsSync17(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
|
|
21451
21523
|
} catch {
|
|
21452
21524
|
text = "";
|
|
21453
21525
|
}
|
|
@@ -21465,9 +21537,9 @@ function ownerOnly(path, mode) {
|
|
|
21465
21537
|
chmodSync4(path, mode);
|
|
21466
21538
|
}
|
|
21467
21539
|
function runRequestShapes(form, home = homedir9()) {
|
|
21468
|
-
const folder2 =
|
|
21469
|
-
const switchPath =
|
|
21470
|
-
const logPath2 =
|
|
21540
|
+
const folder2 = join31(home, HALFCYCLE_DIR_NAME, REQUEST_SHAPES_DIR_NAME);
|
|
21541
|
+
const switchPath = join31(folder2, REQUEST_SHAPES_SWITCH_FILENAME);
|
|
21542
|
+
const logPath2 = join31(folder2, REQUEST_SHAPES_LOG_FILENAME);
|
|
21471
21543
|
try {
|
|
21472
21544
|
switch (form) {
|
|
21473
21545
|
case "on":
|
|
@@ -21484,7 +21556,7 @@ function runRequestShapes(form, home = homedir9()) {
|
|
|
21484
21556
|
case "show":
|
|
21485
21557
|
return { stdout: showLog(logPath2), stderr: "", exitCode: 0 };
|
|
21486
21558
|
case "clear":
|
|
21487
|
-
if (
|
|
21559
|
+
if (existsSync17(logPath2)) {
|
|
21488
21560
|
writeFileSync10(logPath2, "", { mode: 384 });
|
|
21489
21561
|
ownerOnly(logPath2, 384);
|
|
21490
21562
|
}
|
|
@@ -21793,7 +21865,7 @@ ${USAGE}`);
|
|
|
21793
21865
|
if (installArm) {
|
|
21794
21866
|
const engagementIdArg = positionals[1];
|
|
21795
21867
|
const engagementTypeRaw = positionals[2] ?? "client";
|
|
21796
|
-
if (!
|
|
21868
|
+
if (!existsSync18(targetRepo)) {
|
|
21797
21869
|
process.stderr.write(`${folderMissingRefusal(targetRepo)}
|
|
21798
21870
|
`);
|
|
21799
21871
|
process.exit(1);
|