halfcycle 0.11.0 → 0.12.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 +525 -144
- package/bin/pre-tool-use-filter.sh +1 -0
- package/dist/banner-facts.d.ts +7 -2
- package/dist/banner-facts.d.ts.map +1 -1
- package/dist/build-record/obtain.d.ts.map +1 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/check-criteria.d.ts +1 -1
- package/dist/check-criteria.d.ts.map +1 -1
- package/dist/check-report.d.ts +1 -1
- package/dist/check-report.d.ts.map +1 -1
- package/dist/check-tasks.d.ts +10 -1
- package/dist/check-tasks.d.ts.map +1 -1
- package/dist/cli-contract.d.ts +1 -1
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli.js +1018 -731
- package/dist/cli.js.map +3 -3
- package/dist/crew.d.ts +37 -0
- package/dist/crew.d.ts.map +1 -0
- package/dist/draw/index.d.ts +1 -1
- package/dist/draw/index.d.ts.map +1 -1
- package/dist/engagement-credential.d.ts +4 -0
- package/dist/engagement-credential.d.ts.map +1 -1
- package/dist/failure-line.d.ts +3 -2
- package/dist/failure-line.d.ts.map +1 -1
- package/dist/folder-refusals.d.ts.map +1 -1
- package/dist/index.js +271 -206
- package/dist/index.js.map +3 -3
- package/dist/install.d.ts +20 -7
- package/dist/install.d.ts.map +1 -1
- package/dist/local-allow.d.ts +10 -3
- package/dist/local-allow.d.ts.map +1 -1
- package/dist/open-phase.d.ts +1 -1
- package/dist/open-phase.d.ts.map +1 -1
- package/dist/own-version.d.ts +42 -0
- package/dist/own-version.d.ts.map +1 -0
- package/dist/pace.d.ts +2 -2
- package/dist/pace.d.ts.map +1 -1
- package/dist/plan.d.ts +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/pre-create-checks.d.ts.map +1 -1
- package/dist/ready.d.ts +1 -1
- package/dist/ready.d.ts.map +1 -1
- package/dist/regression.d.ts +48 -0
- package/dist/regression.d.ts.map +1 -0
- package/dist/request-shapes-verb.d.ts +1 -1
- package/dist/request-shapes-verb.d.ts.map +1 -1
- package/dist/restore.d.ts +1 -1
- package/dist/restore.d.ts.map +1 -1
- package/dist/review.d.ts +2 -2
- package/dist/review.d.ts.map +1 -1
- package/dist/route.d.ts +1 -1
- package/dist/route.d.ts.map +1 -1
- package/dist/size.d.ts +6 -4
- package/dist/size.d.ts.map +1 -1
- package/dist/uninstall.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -285,12 +285,71 @@ function looksLikeSecret(text) {
|
|
|
285
285
|
var MCP_REGISTRATION_REL = ".mcp.json";
|
|
286
286
|
var MCP_SERVER_KEY = "halfcycle";
|
|
287
287
|
|
|
288
|
+
// ../core/dist/release-version.js
|
|
289
|
+
var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
290
|
+
function isReleaseVersion(value) {
|
|
291
|
+
return typeof value === "string" && RELEASE_VERSION_PATTERN.test(value);
|
|
292
|
+
}
|
|
293
|
+
function significant(part) {
|
|
294
|
+
const trimmed = part.replace(/^0+/, "");
|
|
295
|
+
return trimmed === "" ? "0" : trimmed;
|
|
296
|
+
}
|
|
297
|
+
function compareReleaseVersions(a, b) {
|
|
298
|
+
if (!isReleaseVersion(a) || !isReleaseVersion(b)) {
|
|
299
|
+
throw new TypeError("compareReleaseVersions takes two MAJOR.MINOR.PATCH versions");
|
|
300
|
+
}
|
|
301
|
+
const pa = a.split(".").map(significant);
|
|
302
|
+
const pb = b.split(".").map(significant);
|
|
303
|
+
for (let i = 0; i < 3; i++) {
|
|
304
|
+
const x = pa[i];
|
|
305
|
+
const y = pb[i];
|
|
306
|
+
if (x.length !== y.length)
|
|
307
|
+
return x.length - y.length;
|
|
308
|
+
if (x !== y)
|
|
309
|
+
return x < y ? -1 : 1;
|
|
310
|
+
}
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ../core/dist/release-tag.js
|
|
315
|
+
var PRE_RELEASE_VERSION_PATTERN = /^(\d+\.\d+\.\d+)-rc\.\d+$/;
|
|
316
|
+
var PACKAGE_COMMAND = "npx halfcycle";
|
|
317
|
+
var COMMAND_FORMS = /npx halfcycle(?:@latest|(?![@\p{L}\p{N}-]))/gu;
|
|
318
|
+
function isPreReleaseVersion(value) {
|
|
319
|
+
return typeof value === "string" && PRE_RELEASE_VERSION_PATTERN.test(value);
|
|
320
|
+
}
|
|
321
|
+
function releaseOfVersion(value) {
|
|
322
|
+
if (typeof value !== "string")
|
|
323
|
+
return void 0;
|
|
324
|
+
if (isReleaseVersion(value))
|
|
325
|
+
return value;
|
|
326
|
+
const match = PRE_RELEASE_VERSION_PATTERN.exec(value);
|
|
327
|
+
return match === null ? void 0 : match[1];
|
|
328
|
+
}
|
|
329
|
+
function releaseTagOfVersion(version) {
|
|
330
|
+
return isPreReleaseVersion(version) ? "next" : "latest";
|
|
331
|
+
}
|
|
332
|
+
function halfcycleCommand(version) {
|
|
333
|
+
return `${PACKAGE_COMMAND}@${releaseTagOfVersion(version)}`;
|
|
334
|
+
}
|
|
335
|
+
function bareHalfcycleCommand(version) {
|
|
336
|
+
return releaseTagOfVersion(version) === "next" ? `${PACKAGE_COMMAND}@next` : PACKAGE_COMMAND;
|
|
337
|
+
}
|
|
338
|
+
function rewriteHalfcycleCommands(text, tag) {
|
|
339
|
+
if (tag === "latest")
|
|
340
|
+
return text;
|
|
341
|
+
return text.replace(COMMAND_FORMS, `${PACKAGE_COMMAND}@${tag}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
288
344
|
// ../core/dist/shell-quote.js
|
|
289
345
|
function quoteForShell(value) {
|
|
290
346
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
291
347
|
}
|
|
292
|
-
var HALFCYCLE_COMMAND =
|
|
293
|
-
|
|
348
|
+
var HALFCYCLE_COMMAND = halfcycleCommand();
|
|
349
|
+
function doctorLine(version) {
|
|
350
|
+
return `Run \`${bareHalfcycleCommand(version)} doctor\` to check set up, sign-in, connection, checks and version.`;
|
|
351
|
+
}
|
|
352
|
+
var DOCTOR_LINE = doctorLine();
|
|
294
353
|
|
|
295
354
|
// ../core/dist/folder-trust.js
|
|
296
355
|
var CLAUDE_STATE_FILENAME = ".claude.json";
|
|
@@ -561,32 +620,6 @@ function typedPhaseReference(phase, number) {
|
|
|
561
620
|
return phase.includes(":") && number !== null ? String(number) : phase;
|
|
562
621
|
}
|
|
563
622
|
|
|
564
|
-
// ../core/dist/release-version.js
|
|
565
|
-
var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
566
|
-
function isReleaseVersion(value) {
|
|
567
|
-
return typeof value === "string" && RELEASE_VERSION_PATTERN.test(value);
|
|
568
|
-
}
|
|
569
|
-
function significant(part) {
|
|
570
|
-
const trimmed = part.replace(/^0+/, "");
|
|
571
|
-
return trimmed === "" ? "0" : trimmed;
|
|
572
|
-
}
|
|
573
|
-
function compareReleaseVersions(a, b) {
|
|
574
|
-
if (!isReleaseVersion(a) || !isReleaseVersion(b)) {
|
|
575
|
-
throw new TypeError("compareReleaseVersions takes two MAJOR.MINOR.PATCH versions");
|
|
576
|
-
}
|
|
577
|
-
const pa = a.split(".").map(significant);
|
|
578
|
-
const pb = b.split(".").map(significant);
|
|
579
|
-
for (let i = 0; i < 3; i++) {
|
|
580
|
-
const x = pa[i];
|
|
581
|
-
const y = pb[i];
|
|
582
|
-
if (x.length !== y.length)
|
|
583
|
-
return x.length - y.length;
|
|
584
|
-
if (x !== y)
|
|
585
|
-
return x < y ? -1 : 1;
|
|
586
|
-
}
|
|
587
|
-
return 0;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
623
|
// ../core/dist/github-address.js
|
|
591
624
|
var GITHUB_REPOSITORY_ADDRESS = /^https:\/\/github\.com\/[A-Za-z0-9][A-Za-z0-9-]{0,38}\/(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$/;
|
|
592
625
|
var HOST = "[Gg][Ii][Tt][Hh][Uu][Bb]\\.[Cc][Oo][Mm]";
|
|
@@ -603,8 +636,14 @@ var REMOTE_FORMS = [
|
|
|
603
636
|
];
|
|
604
637
|
|
|
605
638
|
// ../core/dist/sign-in.js
|
|
606
|
-
|
|
607
|
-
|
|
639
|
+
function signInCommand(version) {
|
|
640
|
+
return `"${bareHalfcycleCommand(version)}"`;
|
|
641
|
+
}
|
|
642
|
+
var SIGN_IN_COMMAND = signInCommand();
|
|
643
|
+
function hookRemedy(version) {
|
|
644
|
+
return `Run ${signInCommand(version)} in the project folder to sign in.`;
|
|
645
|
+
}
|
|
646
|
+
var HOOK_REMEDY = hookRemedy();
|
|
608
647
|
|
|
609
648
|
// ../core/dist/task-plan.js
|
|
610
649
|
import { z as z2 } from "zod";
|
|
@@ -1977,7 +2016,7 @@ function codexHookCommand(script, args2) {
|
|
|
1977
2016
|
const invocation = `cd "$(git rev-parse --show-toplevel)" && bash ./${script}`;
|
|
1978
2017
|
return args2 === void 0 ? invocation : `${invocation} ${args2}`;
|
|
1979
2018
|
}
|
|
1980
|
-
var CODEX_WINDOWS_NOT_SET_UP =
|
|
2019
|
+
var CODEX_WINDOWS_NOT_SET_UP = `Halfcycle checks did not run because Halfcycle is not set up in this folder or any folder above it; run ${bareHalfcycleCommand()} in the project folder.`;
|
|
1981
2020
|
var CODEX_WINDOWS_NO_GIT_BASH = "Halfcycle checks did not run because Git for Windows was not found; install Git for Windows, then open a new session.";
|
|
1982
2021
|
function codexHookCommandWindows(script, args2) {
|
|
1983
2022
|
const tick = (text) => "`" + text + "`";
|
|
@@ -2487,15 +2526,14 @@ if (switchedOn) {
|
|
|
2487
2526
|
}
|
|
2488
2527
|
|
|
2489
2528
|
// dist/bin.js
|
|
2490
|
-
import { existsSync as
|
|
2529
|
+
import { existsSync as existsSync17, statSync as statSync2, writeSync as writeSync4 } from "node:fs";
|
|
2491
2530
|
|
|
2492
2531
|
// dist/install.js
|
|
2493
2532
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
2494
2533
|
import { createHash as createHash2 } from "node:crypto";
|
|
2495
|
-
import { existsSync as
|
|
2534
|
+
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2496
2535
|
import { homedir as homedir3 } from "node:os";
|
|
2497
|
-
import { dirname as
|
|
2498
|
-
import { fileURLToPath } from "node:url";
|
|
2536
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join8, posix as posix2, relative, resolve } from "node:path";
|
|
2499
2537
|
|
|
2500
2538
|
// ../events/dist/result.js
|
|
2501
2539
|
import { z as z3 } from "zod";
|
|
@@ -4141,8 +4179,8 @@ var TIME_MEASURED_SENTENCE = "Measured by Halfcycle from what happened in your s
|
|
|
4141
4179
|
function timePhaseElapsedSentenceOf(elapsedSeconds) {
|
|
4142
4180
|
return `${timeDurationLabelOf(elapsedSeconds)} since this phase opened.`;
|
|
4143
4181
|
}
|
|
4144
|
-
function timeUsageTotalTokensOf(
|
|
4145
|
-
return
|
|
4182
|
+
function timeUsageTotalTokensOf(usage5) {
|
|
4183
|
+
return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens + usage5.cacheReadTokens;
|
|
4146
4184
|
}
|
|
4147
4185
|
var TIME_NO_RECORD_SENTENCE = "Halfcycle has not recorded any time for this phase yet. It records at the end of each reply in a session on this project.";
|
|
4148
4186
|
function timeSummaryIsEmpty(summary) {
|
|
@@ -4176,25 +4214,25 @@ var TIME_PERMISSION_PROMPT_LABEL = "Waiting for you to allow an action";
|
|
|
4176
4214
|
function timePermissionPromptLineOf(seconds) {
|
|
4177
4215
|
return seconds === 0 ? void 0 : `${TIME_PERMISSION_PROMPT_LABEL} ${timeDurationLabelOf(seconds)}`;
|
|
4178
4216
|
}
|
|
4179
|
-
function timeUsageNotCachedTokensOf(
|
|
4180
|
-
return
|
|
4217
|
+
function timeUsageNotCachedTokensOf(usage5) {
|
|
4218
|
+
return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens;
|
|
4181
4219
|
}
|
|
4182
|
-
function timeUsageLeadSentenceOf(
|
|
4183
|
-
if (
|
|
4220
|
+
function timeUsageLeadSentenceOf(usage5) {
|
|
4221
|
+
if (usage5.state === "not-available") {
|
|
4184
4222
|
return "Usage not available: your coding agent keeps no usage record Halfcycle can read.";
|
|
4185
4223
|
}
|
|
4186
|
-
const n = timeCountLabelOf(timeUsageNotCachedTokensOf(
|
|
4187
|
-
if (
|
|
4224
|
+
const n = timeCountLabelOf(timeUsageNotCachedTokensOf(usage5));
|
|
4225
|
+
if (usage5.state === "partly-recorded") {
|
|
4188
4226
|
return `At least ${n} tokens not read from the cache, as your coding agent recorded them. Some sessions' usage could not be read.`;
|
|
4189
4227
|
}
|
|
4190
4228
|
return `${n} tokens not read from the cache, as your coding agent recorded them.`;
|
|
4191
4229
|
}
|
|
4192
|
-
function timeUsageInAllSentenceOf(
|
|
4193
|
-
if (
|
|
4230
|
+
function timeUsageInAllSentenceOf(usage5) {
|
|
4231
|
+
if (usage5.state === "not-available")
|
|
4194
4232
|
return void 0;
|
|
4195
|
-
const all = timeCountLabelOf(timeUsageTotalTokensOf(
|
|
4196
|
-
const cached = timeCountLabelOf(
|
|
4197
|
-
const lead =
|
|
4233
|
+
const all = timeCountLabelOf(timeUsageTotalTokensOf(usage5));
|
|
4234
|
+
const cached = timeCountLabelOf(usage5.cacheReadTokens);
|
|
4235
|
+
const lead = usage5.state === "partly-recorded" ? `At least ${all}` : all;
|
|
4198
4236
|
return `${lead} in all; ${cached} of them were read back from the cache.`;
|
|
4199
4237
|
}
|
|
4200
4238
|
function timeReviewsSentenceOf(reviews) {
|
|
@@ -5295,7 +5333,7 @@ var PROJECT_SIZE_DESCRIPTION_MAX = 200;
|
|
|
5295
5333
|
var projectSizeDescriptionSchema = statementText(PROJECT_SIZE_DESCRIPTION_MAX).refine((v) => v.trim().length > 0, { message: "must say what the project is" });
|
|
5296
5334
|
var projectSizeRequestSchema = z17.object({
|
|
5297
5335
|
description: projectSizeDescriptionSchema,
|
|
5298
|
-
estimate: projectSizeEstimateSchema,
|
|
5336
|
+
estimate: projectSizeEstimateSchema.optional(),
|
|
5299
5337
|
existingCode: z17.boolean(),
|
|
5300
5338
|
signIn: z17.boolean(),
|
|
5301
5339
|
sharedData: z17.boolean(),
|
|
@@ -5327,15 +5365,15 @@ var PROJECT_SIZE_FACT_REASON = Object.freeze({
|
|
|
5327
5365
|
moreThanThreeParts: "more-than-three-parts"
|
|
5328
5366
|
});
|
|
5329
5367
|
var PROJECT_SIZE_REASON_WORDS = Object.freeze({
|
|
5330
|
-
days: "
|
|
5331
|
-
weeks: "
|
|
5368
|
+
days: "it was said to take days",
|
|
5369
|
+
weeks: "it was said to take weeks or longer",
|
|
5332
5370
|
"existing-code": "it builds on existing code",
|
|
5333
5371
|
"sign-in": "it has sign-in or user accounts",
|
|
5334
5372
|
"shared-data": "it stores data other people rely on",
|
|
5335
5373
|
"paid-services": "it takes payments or uses outside services that cost money",
|
|
5336
5374
|
"more-than-three-parts": "it has more than three parts"
|
|
5337
5375
|
});
|
|
5338
|
-
var PROJECT_SIZE_MOVE_UP_OFFER = '
|
|
5376
|
+
var PROJECT_SIZE_MOVE_UP_OFFER = 'If the project is bigger than this, say "make it a medium project" or "make it a full project", now or at any time. A project never moves to a smaller size.';
|
|
5339
5377
|
var PROJECT_SIZE_NEVER_SMALLER = "A project never moves to a smaller size.";
|
|
5340
5378
|
function projectSizeMovedUpLineOf(size) {
|
|
5341
5379
|
return `This project is now ${size}. The next step it is served is the full method's next step.`;
|
|
@@ -5487,7 +5525,9 @@ var openPhaseRequestSchema = z18.object({
|
|
|
5487
5525
|
entry: phaseEntryDecisionSchema,
|
|
5488
5526
|
startCommit: closeCommitSchema.optional(),
|
|
5489
5527
|
sortingId: sortingIdSchema.optional(),
|
|
5490
|
-
sizeId: projectSizeIdSchema.optional()
|
|
5528
|
+
sizeId: projectSizeIdSchema.optional(),
|
|
5529
|
+
// Present, and true, only when this phase is opened as a regression phase.
|
|
5530
|
+
regression: z18.literal(true).optional()
|
|
5491
5531
|
}).strict().refine((v) => v.phase !== null || v.startCommit === void 0, {
|
|
5492
5532
|
message: "is sent only when a phase is opened",
|
|
5493
5533
|
path: ["startCommit"]
|
|
@@ -5497,6 +5537,9 @@ var openPhaseRequestSchema = z18.object({
|
|
|
5497
5537
|
}).refine((v) => v.phase !== null || v.sizeId === void 0, {
|
|
5498
5538
|
message: "is sent only when a phase is opened",
|
|
5499
5539
|
path: ["sizeId"]
|
|
5540
|
+
}).refine((v) => v.phase !== null || v.regression === void 0, {
|
|
5541
|
+
message: "is sent only when a phase is opened",
|
|
5542
|
+
path: ["regression"]
|
|
5500
5543
|
});
|
|
5501
5544
|
var phaseEntryDecisionRecordSchema = z18.object({
|
|
5502
5545
|
decisionId: z18.string(),
|
|
@@ -5799,6 +5842,11 @@ var SMALL_CREW = Object.freeze([
|
|
|
5799
5842
|
function isSmallCrewCallsign(callsign) {
|
|
5800
5843
|
return SMALL_CREW.includes(callsign);
|
|
5801
5844
|
}
|
|
5845
|
+
function crewOfProjectSize(size) {
|
|
5846
|
+
if (size !== "small")
|
|
5847
|
+
return CREW_ROSTER;
|
|
5848
|
+
return CREW_ROSTER.filter((member) => isSmallCrewCallsign(member.callsign));
|
|
5849
|
+
}
|
|
5802
5850
|
var crewLayerSchema = methodLayerSchema.extract(["l0", "l1", "l2", "l3", "l4", "l5"]);
|
|
5803
5851
|
var crewRoleLayersSchema = z19.object({
|
|
5804
5852
|
leads: z19.array(crewLayerSchema),
|
|
@@ -6502,9 +6550,8 @@ function paceClockOf(at, now) {
|
|
|
6502
6550
|
}
|
|
6503
6551
|
return `${day} ${clock}`;
|
|
6504
6552
|
}
|
|
6505
|
-
function paceP1Of(n,
|
|
6506
|
-
|
|
6507
|
-
return minutes === null ? first : `${first} The work should take about ${timeDurationLabelOf(minutes * 60)}.`;
|
|
6553
|
+
function paceP1Of(n, _minutes) {
|
|
6554
|
+
return `Halfcycle suggests running ${paceAgentsOf(n)} at once.`;
|
|
6508
6555
|
}
|
|
6509
6556
|
var PACE_NO_BUILD_PLAN = "Halfcycle has no build plan for this phase yet, so it cannot say how long the work will take.";
|
|
6510
6557
|
var PACE_P2 = "This is a careful start, so your allowance lasts. It leaves room for your own work.";
|
|
@@ -6517,27 +6564,30 @@ function paceP6Of(k) {
|
|
|
6517
6564
|
return `It will probably pause ${k === 1 ? "once" : `${String(k)} times`} while your usage window resets.`;
|
|
6518
6565
|
}
|
|
6519
6566
|
var PACE_P7 = 'Or say how much of your allowance to keep free, like "keep 30% free", or how much this build may use, like "use up to 70%".';
|
|
6520
|
-
|
|
6521
|
-
|
|
6567
|
+
function paceP8Of(version) {
|
|
6568
|
+
return `For these to work, run "${bareHalfcycleCommand(version)}" once more in this project.`;
|
|
6569
|
+
}
|
|
6570
|
+
var PACE_P8 = paceP8Of();
|
|
6571
|
+
function paceStartLinesOf(s, promptScriptBehind2 = false, version) {
|
|
6522
6572
|
const limit = s.limitTokens !== void 0 && s.expectedTokens !== void 0 ? { expected: s.expectedTokens, limit: s.limitTokens, multiple: s.multiple ?? null, setBy: s.limitSetBy ?? "default" } : null;
|
|
6523
|
-
const
|
|
6524
|
-
const lines = [paceP1Of(s.number, s.minutes), ...
|
|
6573
|
+
const usage5 = buildStartUsageLinesOf({ workMinutes: s.workMinutes, limit, weekCeilingTokens: s.weekCeilingTokens });
|
|
6574
|
+
const lines = [paceP1Of(s.number, s.minutes), ...usage5.work];
|
|
6525
6575
|
if (s.minutes === null)
|
|
6526
6576
|
lines.push(PACE_NO_BUILD_PLAN);
|
|
6527
6577
|
if (s.keepFreePercent === 0)
|
|
6528
6578
|
lines.push(PACE_P5);
|
|
6529
6579
|
else
|
|
6530
6580
|
lines.push(s.knownWindow !== null ? paceP3Of(s.keepFreePercent) : PACE_P2);
|
|
6531
|
-
if (
|
|
6532
|
-
lines.push(...
|
|
6581
|
+
if (usage5.limit.length > 0) {
|
|
6582
|
+
lines.push(...usage5.limit);
|
|
6533
6583
|
if (s.firstGuess === true)
|
|
6534
6584
|
lines.push(LIMIT_L1B);
|
|
6535
6585
|
}
|
|
6536
6586
|
if (s.pauses > 0)
|
|
6537
6587
|
lines.push(paceP6Of(s.pauses));
|
|
6538
|
-
lines.push(PACE_P4, PACE_P7, ...
|
|
6588
|
+
lines.push(PACE_P4, PACE_P7, ...usage5.change);
|
|
6539
6589
|
if (promptScriptBehind2)
|
|
6540
|
-
lines.push(
|
|
6590
|
+
lines.push(paceP8Of(version));
|
|
6541
6591
|
return lines;
|
|
6542
6592
|
}
|
|
6543
6593
|
function paceS1Of(window, k, until, now, keepFreePercent) {
|
|
@@ -6809,19 +6859,43 @@ function limitUsedShareTextOf(used, limit, weekCeilingTokens) {
|
|
|
6809
6859
|
return figureThenCaveat(`This phase has used ${ofYourWeek(used, weekCeilingTokens)}, out of a limit of ${aboutShareOf(weekPercentOf(limit, weekCeilingTokens))}.`);
|
|
6810
6860
|
}
|
|
6811
6861
|
|
|
6862
|
+
// ../events/dist/regression-line.js
|
|
6863
|
+
import { z as z26 } from "zod";
|
|
6864
|
+
var REGRESSION_EVERY_MIN = 1;
|
|
6865
|
+
var REGRESSION_EVERY_MAX = 20;
|
|
6866
|
+
var REGRESSION_EVERY_RANGE_REFUSAL = "The number of phases between regressions must be from 1 to 20. Nothing was changed.";
|
|
6867
|
+
var regressionEverySchema = z26.number().int({ message: REGRESSION_EVERY_RANGE_REFUSAL }).min(REGRESSION_EVERY_MIN, { message: REGRESSION_EVERY_RANGE_REFUSAL }).max(REGRESSION_EVERY_MAX, { message: REGRESSION_EVERY_RANGE_REFUSAL });
|
|
6868
|
+
var regressionClosedSinceSchema = z26.number().int().min(0);
|
|
6869
|
+
var REGRESSION_LINE_MAX = 200;
|
|
6870
|
+
var regressionLineSchema = z26.string().min(1).max(REGRESSION_LINE_MAX);
|
|
6871
|
+
var regressionEveryRequestSchema = z26.object({
|
|
6872
|
+
// How many phases may close before a regression phase is due, 1 to 20.
|
|
6873
|
+
every: regressionEverySchema
|
|
6874
|
+
}).strict();
|
|
6875
|
+
var regressionEveryResponseSchema = z26.object({
|
|
6876
|
+
// The number now stored.
|
|
6877
|
+
every: regressionEverySchema,
|
|
6878
|
+
// The line as it reads now, or null when there is none to show.
|
|
6879
|
+
line: regressionLineSchema.nullable()
|
|
6880
|
+
}).strict();
|
|
6881
|
+
|
|
6812
6882
|
// ../events/dist/client-version.js
|
|
6813
6883
|
var CHAT_APPROVAL_CLIENT_VERSION = "0.4.7";
|
|
6814
6884
|
function clientMeetsChatApprovalFloor(version) {
|
|
6815
|
-
|
|
6885
|
+
const release = releaseOfVersion(version);
|
|
6886
|
+
return release !== void 0 && compareReleaseVersions(release, CHAT_APPROVAL_CLIENT_VERSION) >= 0;
|
|
6816
6887
|
}
|
|
6817
6888
|
var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
|
|
6818
6889
|
var HEADLESS_CANNOT_APPROVE = "(a headless session cannot approve, by design)";
|
|
6819
6890
|
var TYPED_REVIEW_OFF_SENTENCE = "Reviewing by typing is switched off for now.";
|
|
6820
|
-
|
|
6891
|
+
function clientUpdateSentence(version) {
|
|
6892
|
+
return `To approve by typing in a session, Halfcycle needs updating: run \`${halfcycleCommand(version)}\` in this project's folder, then reopen your session and ask again.`;
|
|
6893
|
+
}
|
|
6894
|
+
var CLIENT_UPDATE_SENTENCE = clientUpdateSentence();
|
|
6821
6895
|
|
|
6822
6896
|
// ../events/dist/device-auth.js
|
|
6823
|
-
import { z as
|
|
6824
|
-
var utcIso86013 =
|
|
6897
|
+
import { z as z27 } from "zod";
|
|
6898
|
+
var utcIso86013 = z27.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
|
|
6825
6899
|
var DEVICE_AUTH_STATUS = {
|
|
6826
6900
|
/** The poll: minted, nobody has acted yet. Keep polling. 200. */
|
|
6827
6901
|
PENDING: "pending",
|
|
@@ -6862,25 +6936,25 @@ var DEVICE_POLL_REFUSALS = [
|
|
|
6862
6936
|
DEVICE_AUTH_STATUS.EXPIRED,
|
|
6863
6937
|
DEVICE_AUTH_STATUS.UNKNOWN
|
|
6864
6938
|
];
|
|
6865
|
-
var deviceAuthStartResponseSchema =
|
|
6866
|
-
deviceCode:
|
|
6867
|
-
userCode:
|
|
6868
|
-
verificationUrl:
|
|
6939
|
+
var deviceAuthStartResponseSchema = z27.object({
|
|
6940
|
+
deviceCode: z27.string().min(1),
|
|
6941
|
+
userCode: z27.string().min(1),
|
|
6942
|
+
verificationUrl: z27.string().min(1),
|
|
6869
6943
|
expiresAt: utcIso86013,
|
|
6870
|
-
pollIntervalMs:
|
|
6944
|
+
pollIntervalMs: z27.number().int().positive()
|
|
6871
6945
|
}).strict();
|
|
6872
|
-
var devicePollPendingSchema =
|
|
6873
|
-
status:
|
|
6874
|
-
pollIntervalMs:
|
|
6946
|
+
var devicePollPendingSchema = z27.object({
|
|
6947
|
+
status: z27.literal(DEVICE_AUTH_STATUS.PENDING),
|
|
6948
|
+
pollIntervalMs: z27.number().int().positive()
|
|
6875
6949
|
}).strict();
|
|
6876
|
-
var devicePollApprovedSchema =
|
|
6877
|
-
status:
|
|
6878
|
-
accountId:
|
|
6879
|
-
credential:
|
|
6950
|
+
var devicePollApprovedSchema = z27.object({
|
|
6951
|
+
status: z27.literal(DEVICE_AUTH_STATUS.APPROVED),
|
|
6952
|
+
accountId: z27.string().min(1),
|
|
6953
|
+
credential: z27.string().min(1),
|
|
6880
6954
|
expiresAt: utcIso86013
|
|
6881
6955
|
}).strict();
|
|
6882
|
-
var deviceAuthRefusalSchema =
|
|
6883
|
-
status:
|
|
6956
|
+
var deviceAuthRefusalSchema = z27.object({
|
|
6957
|
+
status: z27.enum([
|
|
6884
6958
|
DEVICE_AUTH_STATUS.DECLINED,
|
|
6885
6959
|
DEVICE_AUTH_STATUS.EXPIRED,
|
|
6886
6960
|
DEVICE_AUTH_STATUS.UNKNOWN,
|
|
@@ -6891,25 +6965,25 @@ var deviceAuthRefusalSchema = z26.object({
|
|
|
6891
6965
|
DEVICE_AUTH_STATUS.ALREADY_DECIDED,
|
|
6892
6966
|
DEVICE_AUTH_STATUS.TERMS_REQUIRED
|
|
6893
6967
|
]),
|
|
6894
|
-
message:
|
|
6968
|
+
message: z27.string().min(1)
|
|
6895
6969
|
}).strict();
|
|
6896
|
-
var devicePollResponseSchema =
|
|
6970
|
+
var devicePollResponseSchema = z27.union([
|
|
6897
6971
|
devicePollPendingSchema,
|
|
6898
6972
|
devicePollApprovedSchema,
|
|
6899
6973
|
deviceAuthRefusalSchema
|
|
6900
6974
|
]);
|
|
6901
|
-
var deviceConfirmRequestSchema =
|
|
6902
|
-
userCode:
|
|
6903
|
-
decision:
|
|
6904
|
-
externalAuthId:
|
|
6905
|
-
email:
|
|
6906
|
-
acceptTerms:
|
|
6975
|
+
var deviceConfirmRequestSchema = z27.object({
|
|
6976
|
+
userCode: z27.string().min(1),
|
|
6977
|
+
decision: z27.enum(["approve", "decline"]),
|
|
6978
|
+
externalAuthId: z27.string().min(1).optional(),
|
|
6979
|
+
email: z27.string().optional(),
|
|
6980
|
+
acceptTerms: z27.boolean().optional()
|
|
6907
6981
|
}).strict();
|
|
6908
|
-
var deviceConfirmRecordedSchema =
|
|
6909
|
-
status:
|
|
6910
|
-
accountId:
|
|
6982
|
+
var deviceConfirmRecordedSchema = z27.object({
|
|
6983
|
+
status: z27.literal(DEVICE_AUTH_STATUS.RECORDED),
|
|
6984
|
+
accountId: z27.string().min(1).optional()
|
|
6911
6985
|
}).strict();
|
|
6912
|
-
var deviceConfirmResponseSchema =
|
|
6986
|
+
var deviceConfirmResponseSchema = z27.union([
|
|
6913
6987
|
deviceConfirmRecordedSchema,
|
|
6914
6988
|
deviceAuthRefusalSchema
|
|
6915
6989
|
]);
|
|
@@ -6953,52 +7027,58 @@ function requestAccessSentence(engagementId) {
|
|
|
6953
7027
|
}
|
|
6954
7028
|
|
|
6955
7029
|
// ../events/dist/join-refusal.js
|
|
6956
|
-
import { z as
|
|
7030
|
+
import { z as z28 } from "zod";
|
|
6957
7031
|
var NOT_THIS_ACCOUNT_REASON = "not-this-account";
|
|
6958
7032
|
var JOIN_REFUSAL_STATUS = 403;
|
|
6959
|
-
var notThisAccountRefusalSchema =
|
|
6960
|
-
statusCode:
|
|
6961
|
-
error:
|
|
6962
|
-
message:
|
|
6963
|
-
reason:
|
|
7033
|
+
var notThisAccountRefusalSchema = z28.object({
|
|
7034
|
+
statusCode: z28.literal(JOIN_REFUSAL_STATUS),
|
|
7035
|
+
error: z28.string(),
|
|
7036
|
+
message: z28.string(),
|
|
7037
|
+
reason: z28.literal(NOT_THIS_ACCOUNT_REASON)
|
|
6964
7038
|
}).strict();
|
|
6965
|
-
var joinRefusalSchema =
|
|
7039
|
+
var joinRefusalSchema = z28.union([notThisAccountRefusalSchema, wireErrorSchema]);
|
|
6966
7040
|
function isNotThisAccountRefusal(body) {
|
|
6967
7041
|
return typeof body === "object" && body !== null && body.reason === NOT_THIS_ACCOUNT_REASON;
|
|
6968
7042
|
}
|
|
6969
7043
|
|
|
6970
7044
|
// ../events/dist/account-identity.js
|
|
6971
|
-
import { z as
|
|
6972
|
-
var accountIdentitySchema =
|
|
6973
|
-
accountId:
|
|
6974
|
-
email:
|
|
7045
|
+
import { z as z29 } from "zod";
|
|
7046
|
+
var accountIdentitySchema = z29.object({
|
|
7047
|
+
accountId: z29.string().min(1),
|
|
7048
|
+
email: z29.string().min(1).optional()
|
|
6975
7049
|
}).strict();
|
|
6976
7050
|
function safeParseAccountIdentity(payload) {
|
|
6977
7051
|
return accountIdentitySchema.safeParse(payload);
|
|
6978
7052
|
}
|
|
6979
7053
|
|
|
6980
7054
|
// ../events/dist/credential-wire.js
|
|
6981
|
-
import { z as
|
|
6982
|
-
var engagementCredentialResponseSchema =
|
|
6983
|
-
engagementId:
|
|
6984
|
-
sessionToken:
|
|
6985
|
-
mcpUrl:
|
|
6986
|
-
guardUrl:
|
|
6987
|
-
controlTelemetryUrl:
|
|
6988
|
-
methodVersion:
|
|
6989
|
-
consoleUrl:
|
|
7055
|
+
import { z as z30 } from "zod";
|
|
7056
|
+
var engagementCredentialResponseSchema = z30.object({
|
|
7057
|
+
engagementId: z30.string(),
|
|
7058
|
+
sessionToken: z30.string(),
|
|
7059
|
+
mcpUrl: z30.string(),
|
|
7060
|
+
guardUrl: z30.string(),
|
|
7061
|
+
controlTelemetryUrl: z30.string(),
|
|
7062
|
+
methodVersion: z30.string().optional(),
|
|
7063
|
+
consoleUrl: z30.string().optional()
|
|
6990
7064
|
}).strict();
|
|
6991
|
-
var ciTokenExchangeResponseSchema =
|
|
6992
|
-
token:
|
|
7065
|
+
var ciTokenExchangeResponseSchema = z30.object({
|
|
7066
|
+
token: z30.string(),
|
|
6993
7067
|
expiresAt: utcIso8601,
|
|
6994
|
-
engagementId:
|
|
7068
|
+
engagementId: z30.string()
|
|
6995
7069
|
}).strict();
|
|
6996
7070
|
var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
|
|
6997
7071
|
var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
|
|
6998
7072
|
var UPDATE_CLAUDE_CODE_PLACEHOLDER = "halfcycle:update-claude-code";
|
|
6999
7073
|
var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
|
|
7000
|
-
|
|
7001
|
-
|
|
7074
|
+
function noCredentialSentence(version) {
|
|
7075
|
+
return `Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "${bareHalfcycleCommand(version)}" in the project folder to sign in again.`;
|
|
7076
|
+
}
|
|
7077
|
+
var NO_CREDENTIAL_SENTENCE = noCredentialSentence();
|
|
7078
|
+
function notThisAccountSentence(version) {
|
|
7079
|
+
return `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "${bareHalfcycleCommand(version)}" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
|
|
7080
|
+
}
|
|
7081
|
+
var NOT_THIS_ACCOUNT_SENTENCE = notThisAccountSentence();
|
|
7002
7082
|
var UPDATE_CLAUDE_CODE_SENTENCE = "Halfcycle is not connected: this Claude Code version does not name the server it connects to, so no credential was sent. Update Claude Code, then reopen.";
|
|
7003
7083
|
var NO_NODE_SENTENCE = "Halfcycle is not connected: Node is not installed, or not on the path this session uses, so nothing here is checked. Install Node, then reopen.";
|
|
7004
7084
|
var FOLDER_NOT_TRUSTED_SENTENCE = "Folder not yet trusted. Open this folder once in interactive Claude Code and trust it; then headless runs work.";
|
|
@@ -7009,19 +7089,19 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
|
|
|
7009
7089
|
});
|
|
7010
7090
|
|
|
7011
7091
|
// ../events/dist/project-engagement.js
|
|
7012
|
-
import { z as
|
|
7013
|
-
var projectIdSchema =
|
|
7014
|
-
var projectEngagementRequestSchema =
|
|
7015
|
-
derivedName:
|
|
7092
|
+
import { z as z31 } from "zod";
|
|
7093
|
+
var projectIdSchema = z31.string().uuid();
|
|
7094
|
+
var projectEngagementRequestSchema = z31.object({
|
|
7095
|
+
derivedName: z31.string()
|
|
7016
7096
|
}).strict();
|
|
7017
7097
|
|
|
7018
7098
|
// ../events/dist/phase-record.js
|
|
7019
|
-
import { z as
|
|
7099
|
+
import { z as z33 } from "zod";
|
|
7020
7100
|
|
|
7021
7101
|
// ../events/dist/build-record-v4.js
|
|
7022
|
-
import { z as
|
|
7102
|
+
import { z as z32 } from "zod";
|
|
7023
7103
|
var BUILD_RECORD_V4_FORMAT = "halfcycle-build-record/v4";
|
|
7024
|
-
var recordReviewClaimSchema =
|
|
7104
|
+
var recordReviewClaimSchema = z32.object({
|
|
7025
7105
|
...reviewClaimAnswerSchema.shape,
|
|
7026
7106
|
note: reviewNoteSchema.nullable()
|
|
7027
7107
|
}).strict().superRefine((c, ctx) => {
|
|
@@ -7029,19 +7109,19 @@ var recordReviewClaimSchema = z31.object({
|
|
|
7029
7109
|
ctx.addIssue({ code: "custom", path: ["note"], message: "carries the owner's note exactly on a reject" });
|
|
7030
7110
|
}
|
|
7031
7111
|
});
|
|
7032
|
-
var recordCallSchema =
|
|
7033
|
-
var recordReviewV4Schema =
|
|
7112
|
+
var recordCallSchema = z32.object(workCallSchema.shape).omit({ at: true }).strict().superRefine((c, ctx) => checkReadCallHow(c, ctx));
|
|
7113
|
+
var recordReviewV4Schema = z32.object({
|
|
7034
7114
|
...recordReviewSchema.shape,
|
|
7035
|
-
claims:
|
|
7036
|
-
calls:
|
|
7115
|
+
claims: z32.array(recordReviewClaimSchema).max(REVIEW_CLAIMS_MAX),
|
|
7116
|
+
calls: z32.array(recordCallSchema)
|
|
7037
7117
|
}).strict().superRefine((v, ctx) => checkRecordReview(v, ctx));
|
|
7038
|
-
var buildRecordV4Schema =
|
|
7118
|
+
var buildRecordV4Schema = z32.object({
|
|
7039
7119
|
...buildRecordV3Schema.shape,
|
|
7040
|
-
format:
|
|
7120
|
+
format: z32.literal(BUILD_RECORD_V4_FORMAT),
|
|
7041
7121
|
path: phasePathSchema,
|
|
7042
7122
|
review: recordReviewV4Schema
|
|
7043
7123
|
}).strict().superRefine((record2, ctx) => checkReviewedRecord(record2, ctx));
|
|
7044
|
-
var buildRecordSchema =
|
|
7124
|
+
var buildRecordSchema = z32.discriminatedUnion("format", [
|
|
7045
7125
|
buildRecordV2Schema,
|
|
7046
7126
|
buildRecordV3Schema,
|
|
7047
7127
|
buildRecordV4Schema
|
|
@@ -7073,81 +7153,81 @@ function removalsOf(record2) {
|
|
|
7073
7153
|
}
|
|
7074
7154
|
|
|
7075
7155
|
// ../events/dist/phase-record.js
|
|
7076
|
-
var phaseRecordRefusalReasonSchema =
|
|
7156
|
+
var phaseRecordRefusalReasonSchema = z33.enum([
|
|
7077
7157
|
"still-open",
|
|
7078
7158
|
"reopened",
|
|
7079
7159
|
"never-opened",
|
|
7080
7160
|
"never-closed",
|
|
7081
7161
|
"closed-before-records"
|
|
7082
7162
|
]);
|
|
7083
|
-
var phaseRecordRecordSchema =
|
|
7084
|
-
outcome:
|
|
7163
|
+
var phaseRecordRecordSchema = z33.object({
|
|
7164
|
+
outcome: z33.literal("record"),
|
|
7085
7165
|
phase: phaseNameSchema,
|
|
7086
7166
|
record: buildRecordSchema
|
|
7087
7167
|
}).strict().refine((body) => body.record.phase === body.phase, {
|
|
7088
7168
|
message: "the record is for a different phase than the one asked for",
|
|
7089
7169
|
path: ["record", "phase"]
|
|
7090
7170
|
});
|
|
7091
|
-
var phaseRecordWithheldSchema =
|
|
7092
|
-
outcome:
|
|
7171
|
+
var phaseRecordWithheldSchema = z33.object({
|
|
7172
|
+
outcome: z33.literal("withheld"),
|
|
7093
7173
|
phase: phaseNameSchema,
|
|
7094
|
-
sequence:
|
|
7174
|
+
sequence: z33.number().int().min(1),
|
|
7095
7175
|
closedAt: utcIso8601,
|
|
7096
|
-
message:
|
|
7176
|
+
message: z33.string()
|
|
7097
7177
|
}).strict();
|
|
7098
7178
|
var phaseRecordRefusedSchema = wireErrorSchema.extend({
|
|
7099
|
-
outcome:
|
|
7179
|
+
outcome: z33.literal("refused"),
|
|
7100
7180
|
phase: phaseNameSchema,
|
|
7101
|
-
statusCode:
|
|
7102
|
-
error:
|
|
7181
|
+
statusCode: z33.literal(409),
|
|
7182
|
+
error: z33.literal("PhaseRecordRefused"),
|
|
7103
7183
|
reason: phaseRecordRefusalReasonSchema
|
|
7104
7184
|
}).strict();
|
|
7105
|
-
var phaseRecordResponseSchema =
|
|
7185
|
+
var phaseRecordResponseSchema = z33.discriminatedUnion("outcome", [
|
|
7106
7186
|
phaseRecordRecordSchema,
|
|
7107
7187
|
phaseRecordWithheldSchema,
|
|
7108
7188
|
phaseRecordRefusedSchema
|
|
7109
7189
|
]);
|
|
7110
7190
|
|
|
7111
7191
|
// ../events/dist/scope-chat.js
|
|
7112
|
-
import { z as
|
|
7113
|
-
var chatScopeApproveRequestSchema =
|
|
7114
|
-
act:
|
|
7192
|
+
import { z as z34 } from "zod";
|
|
7193
|
+
var chatScopeApproveRequestSchema = z34.object({
|
|
7194
|
+
act: z34.literal("approve"),
|
|
7115
7195
|
reference: phaseIdentitySchema,
|
|
7116
7196
|
planVersion: planVersionSchema
|
|
7117
7197
|
}).strict();
|
|
7118
|
-
var chatScopeSendBackRequestSchema =
|
|
7119
|
-
act:
|
|
7198
|
+
var chatScopeSendBackRequestSchema = z34.object({
|
|
7199
|
+
act: z34.literal("send-back"),
|
|
7120
7200
|
reference: phaseIdentitySchema,
|
|
7121
7201
|
planVersion: planVersionSchema,
|
|
7122
7202
|
note: scopeNoteWireSchema.optional()
|
|
7123
7203
|
}).strict();
|
|
7124
|
-
var chatScopeRequestSchema =
|
|
7204
|
+
var chatScopeRequestSchema = z34.discriminatedUnion("act", [
|
|
7125
7205
|
chatScopeApproveRequestSchema,
|
|
7126
7206
|
chatScopeSendBackRequestSchema
|
|
7127
7207
|
]);
|
|
7128
7208
|
var CHAT_SCOPE_ACTS = SCOPE_ACTS;
|
|
7129
|
-
var chatScopeRecordedSchema =
|
|
7130
|
-
outcome:
|
|
7131
|
-
act:
|
|
7209
|
+
var chatScopeRecordedSchema = z34.object({
|
|
7210
|
+
outcome: z34.literal("recorded"),
|
|
7211
|
+
act: z34.enum(CHAT_SCOPE_ACTS),
|
|
7132
7212
|
phase: scopePhaseSchema,
|
|
7133
7213
|
planVersion: planVersionSchema
|
|
7134
7214
|
}).strict();
|
|
7135
|
-
var chatScopeAlreadyApprovedSchema =
|
|
7136
|
-
outcome:
|
|
7137
|
-
replanNeeded:
|
|
7215
|
+
var chatScopeAlreadyApprovedSchema = z34.object({
|
|
7216
|
+
outcome: z34.literal("already-approved"),
|
|
7217
|
+
replanNeeded: z34.boolean()
|
|
7138
7218
|
}).strict();
|
|
7139
|
-
var chatScopeRefusedSchema =
|
|
7140
|
-
outcome:
|
|
7141
|
-
problems:
|
|
7219
|
+
var chatScopeRefusedSchema = z34.object({
|
|
7220
|
+
outcome: z34.literal("refused"),
|
|
7221
|
+
problems: z34.array(systemDescriptionProblemSchema).min(1)
|
|
7142
7222
|
}).strict();
|
|
7143
|
-
var chatAmbiguousSchema =
|
|
7144
|
-
outcome:
|
|
7145
|
-
phases:
|
|
7223
|
+
var chatAmbiguousSchema = z34.object({
|
|
7224
|
+
outcome: z34.literal("ambiguous"),
|
|
7225
|
+
phases: z34.array(scopePhaseSchema).min(2)
|
|
7146
7226
|
}).strict();
|
|
7147
7227
|
function bareOutcome(outcome) {
|
|
7148
|
-
return
|
|
7228
|
+
return z34.object({ outcome: z34.literal(outcome) }).strict();
|
|
7149
7229
|
}
|
|
7150
|
-
var chatScopeResultSchema =
|
|
7230
|
+
var chatScopeResultSchema = z34.discriminatedUnion("outcome", [
|
|
7151
7231
|
chatScopeRecordedSchema,
|
|
7152
7232
|
chatScopeAlreadyApprovedSchema,
|
|
7153
7233
|
chatScopeRefusedSchema,
|
|
@@ -7162,19 +7242,19 @@ var chatScopeResultSchema = z33.discriminatedUnion("outcome", [
|
|
|
7162
7242
|
]);
|
|
7163
7243
|
|
|
7164
7244
|
// ../events/dist/review-chat.js
|
|
7165
|
-
import { z as
|
|
7166
|
-
var chatReviewAcceptRequestSchema =
|
|
7167
|
-
act:
|
|
7245
|
+
import { z as z35 } from "zod";
|
|
7246
|
+
var chatReviewAcceptRequestSchema = z35.object({
|
|
7247
|
+
act: z35.literal("accept"),
|
|
7168
7248
|
reference: phaseIdentitySchema,
|
|
7169
|
-
reviewVersion:
|
|
7170
|
-
parts:
|
|
7171
|
-
waitingClaims:
|
|
7249
|
+
reviewVersion: z35.number().int().min(0),
|
|
7250
|
+
parts: z35.array(systemPartIdSchema).max(REVIEW_PARTS_MAX),
|
|
7251
|
+
waitingClaims: z35.literal(true).optional()
|
|
7172
7252
|
}).strict();
|
|
7173
7253
|
function notedRequest(act) {
|
|
7174
|
-
return
|
|
7175
|
-
act:
|
|
7254
|
+
return z35.object({
|
|
7255
|
+
act: z35.literal(act),
|
|
7176
7256
|
reference: phaseIdentitySchema,
|
|
7177
|
-
reviewVersion:
|
|
7257
|
+
reviewVersion: z35.number().int().min(0),
|
|
7178
7258
|
part: systemPartIdSchema,
|
|
7179
7259
|
note: reviewNoteWireSchema
|
|
7180
7260
|
}).strict();
|
|
@@ -7183,14 +7263,14 @@ var chatReviewQuestionRequestSchema = notedRequest("question");
|
|
|
7183
7263
|
var chatReviewSendBackRequestSchema = notedRequest("send-back");
|
|
7184
7264
|
var chatReviewCarryRequestSchema = notedRequest("carry");
|
|
7185
7265
|
var chatReviewDropRequestSchema = notedRequest("drop");
|
|
7186
|
-
var chatReviewRejectRequestSchema =
|
|
7187
|
-
act:
|
|
7266
|
+
var chatReviewRejectRequestSchema = z35.object({
|
|
7267
|
+
act: z35.literal("reject"),
|
|
7188
7268
|
reference: phaseIdentitySchema,
|
|
7189
|
-
reviewVersion:
|
|
7269
|
+
reviewVersion: z35.number().int().min(0),
|
|
7190
7270
|
task: buildTaskIdSchema,
|
|
7191
7271
|
note: reviewNoteWireSchema
|
|
7192
7272
|
}).strict();
|
|
7193
|
-
var chatReviewRequestSchema =
|
|
7273
|
+
var chatReviewRequestSchema = z35.discriminatedUnion("act", [
|
|
7194
7274
|
chatReviewAcceptRequestSchema,
|
|
7195
7275
|
chatReviewQuestionRequestSchema,
|
|
7196
7276
|
chatReviewSendBackRequestSchema,
|
|
@@ -7199,12 +7279,12 @@ var chatReviewRequestSchema = z34.discriminatedUnion("act", [
|
|
|
7199
7279
|
chatReviewRejectRequestSchema
|
|
7200
7280
|
]);
|
|
7201
7281
|
function bareOutcome2(outcome) {
|
|
7202
|
-
return
|
|
7282
|
+
return z35.object({ outcome: z35.literal(outcome) }).strict();
|
|
7203
7283
|
}
|
|
7204
7284
|
function notTheConsoleNotDecidable(option) {
|
|
7205
7285
|
return option !== reviewNotDecidableSchema;
|
|
7206
7286
|
}
|
|
7207
|
-
var chatReviewResultSchema =
|
|
7287
|
+
var chatReviewResultSchema = z35.discriminatedUnion("outcome", [
|
|
7208
7288
|
reviewPartsNotDecidableSchema,
|
|
7209
7289
|
...reviewDecisionResultSchema.options.filter(notTheConsoleNotDecidable),
|
|
7210
7290
|
bareOutcome2("show-first"),
|
|
@@ -7212,15 +7292,15 @@ var chatReviewResultSchema = z34.discriminatedUnion("outcome", [
|
|
|
7212
7292
|
chatAmbiguousSchema,
|
|
7213
7293
|
bareOutcome2("nothing-waiting")
|
|
7214
7294
|
]);
|
|
7215
|
-
var reviewAnswerRequestSchema =
|
|
7295
|
+
var reviewAnswerRequestSchema = z35.object({
|
|
7216
7296
|
part: systemPartIdSchema,
|
|
7217
7297
|
answer: statementText(REVIEW_ANSWER_MAX)
|
|
7218
7298
|
}).strict();
|
|
7219
|
-
var reviewAnswerRecordedSchema =
|
|
7220
|
-
outcome:
|
|
7299
|
+
var reviewAnswerRecordedSchema = z35.object({
|
|
7300
|
+
outcome: z35.literal("recorded"),
|
|
7221
7301
|
name: systemPartSchema.shape.name
|
|
7222
7302
|
}).strict();
|
|
7223
|
-
var reviewAnswerResultSchema =
|
|
7303
|
+
var reviewAnswerResultSchema = z35.discriminatedUnion("outcome", [
|
|
7224
7304
|
reviewAnswerRecordedSchema,
|
|
7225
7305
|
bareOutcome2("no-question"),
|
|
7226
7306
|
reviewRefusedSchema,
|
|
@@ -7228,27 +7308,27 @@ var reviewAnswerResultSchema = z34.discriminatedUnion("outcome", [
|
|
|
7228
7308
|
]);
|
|
7229
7309
|
|
|
7230
7310
|
// ../events/dist/owner-waits.js
|
|
7231
|
-
import { z as
|
|
7311
|
+
import { z as z37 } from "zod";
|
|
7232
7312
|
|
|
7233
7313
|
// ../events/dist/owner-wait-answer.js
|
|
7234
|
-
import { z as
|
|
7235
|
-
var ownerWaitShortIdSchema =
|
|
7314
|
+
import { z as z36 } from "zod";
|
|
7315
|
+
var ownerWaitShortIdSchema = z36.string().regex(/^[0-9a-f]{8}$/);
|
|
7236
7316
|
var OWNER_WAIT_ANSWER_ACTS = ["approve", "decline"];
|
|
7237
|
-
var ownerWaitAnswerActSchema =
|
|
7238
|
-
var ownerWaitChatRequestSchema =
|
|
7239
|
-
|
|
7240
|
-
|
|
7317
|
+
var ownerWaitAnswerActSchema = z36.enum(OWNER_WAIT_ANSWER_ACTS);
|
|
7318
|
+
var ownerWaitChatRequestSchema = z36.discriminatedUnion("act", [
|
|
7319
|
+
z36.object({ act: z36.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
|
|
7320
|
+
z36.object({ act: z36.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
|
|
7241
7321
|
]);
|
|
7242
7322
|
function bareOutcome3(outcome) {
|
|
7243
|
-
return
|
|
7323
|
+
return z36.object({ outcome: z36.literal(outcome) }).strict();
|
|
7244
7324
|
}
|
|
7245
|
-
var ownerWaitChatResultSchema =
|
|
7246
|
-
|
|
7247
|
-
outcome:
|
|
7325
|
+
var ownerWaitChatResultSchema = z36.discriminatedUnion("outcome", [
|
|
7326
|
+
z36.object({
|
|
7327
|
+
outcome: z36.literal("recorded"),
|
|
7248
7328
|
act: ownerWaitAnswerActSchema,
|
|
7249
|
-
question:
|
|
7329
|
+
question: z36.string().min(1)
|
|
7250
7330
|
}).strict(),
|
|
7251
|
-
|
|
7331
|
+
z36.object({ outcome: z36.literal("refused"), problems: z36.array(systemDescriptionProblemSchema).min(1) }).strict(),
|
|
7252
7332
|
bareOutcome3("not-owner"),
|
|
7253
7333
|
bareOutcome3("no-owner"),
|
|
7254
7334
|
bareOutcome3("not-found"),
|
|
@@ -7270,7 +7350,7 @@ var OWNER_WAIT_ITEMS = Object.freeze([
|
|
|
7270
7350
|
Object.freeze({ id: "change-standing-rule", words: "Changing a standing rule." })
|
|
7271
7351
|
]);
|
|
7272
7352
|
var OWNER_WAIT_ITEM_IDS = Object.freeze(OWNER_WAIT_ITEMS.map((item) => item.id));
|
|
7273
|
-
var ownerWaitItemIdSchema =
|
|
7353
|
+
var ownerWaitItemIdSchema = z37.enum(OWNER_WAIT_ITEM_IDS);
|
|
7274
7354
|
var OWNER_WAIT_FIXED_COUNT = OWNER_WAIT_ITEMS.length;
|
|
7275
7355
|
var OWNER_WAIT_ADDITIONS_MAX = 20;
|
|
7276
7356
|
var OWNER_WAIT_TEXT_MAX = 240;
|
|
@@ -7280,23 +7360,23 @@ function ownerWaitItemOf(id) {
|
|
|
7280
7360
|
throw new Error(`no owner-wait item ${id}`);
|
|
7281
7361
|
return item;
|
|
7282
7362
|
}
|
|
7283
|
-
var ownerWaitItemRefSchema =
|
|
7284
|
-
|
|
7285
|
-
|
|
7363
|
+
var ownerWaitItemRefSchema = z37.union([
|
|
7364
|
+
z37.number().int().min(1),
|
|
7365
|
+
z37.string().uuid({ message: "must be a number from 1 to 6, or the id shown beside an added item" })
|
|
7286
7366
|
]);
|
|
7287
7367
|
var ownerWaitText = descriptionText(OWNER_WAIT_TEXT_MAX);
|
|
7288
|
-
var ownerWaitAddRequestSchema =
|
|
7289
|
-
engagementId:
|
|
7368
|
+
var ownerWaitAddRequestSchema = z37.object({
|
|
7369
|
+
engagementId: z37.string().min(1),
|
|
7290
7370
|
text: ownerWaitText,
|
|
7291
7371
|
reason: ownerWaitText
|
|
7292
7372
|
}).strict();
|
|
7293
|
-
var ownerWaitAddResultSchema =
|
|
7294
|
-
|
|
7295
|
-
|
|
7373
|
+
var ownerWaitAddResultSchema = z37.discriminatedUnion("outcome", [
|
|
7374
|
+
z37.object({ outcome: z37.literal("added"), id: z37.string().uuid(), addedAt: utcIso8601 }).strict(),
|
|
7375
|
+
z37.object({ outcome: z37.literal("refused"), reason: z37.literal("too-many") }).strict()
|
|
7296
7376
|
]);
|
|
7297
|
-
var ownerWaitUndoableSchema =
|
|
7298
|
-
var ownerWaitDeclareRequestSchema =
|
|
7299
|
-
engagementId:
|
|
7377
|
+
var ownerWaitUndoableSchema = z37.enum(["yes", "no"]);
|
|
7378
|
+
var ownerWaitDeclareRequestSchema = z37.object({
|
|
7379
|
+
engagementId: z37.string().min(1),
|
|
7300
7380
|
item: ownerWaitItemRefSchema,
|
|
7301
7381
|
question: ownerWaitText,
|
|
7302
7382
|
recommendation: ownerWaitText,
|
|
@@ -7309,47 +7389,47 @@ var ownerWaitDeclareRequestSchema = z36.object({
|
|
|
7309
7389
|
path: ["how"]
|
|
7310
7390
|
});
|
|
7311
7391
|
var OWNER_WAIT_DECLARE_REFUSALS = ["no-open-phase", "unknown-item", "say-if-undoable"];
|
|
7312
|
-
var ownerWaitDeclareResultSchema =
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
outcome:
|
|
7316
|
-
id:
|
|
7317
|
-
callId:
|
|
7318
|
-
say:
|
|
7392
|
+
var ownerWaitDeclareResultSchema = z37.discriminatedUnion("outcome", [
|
|
7393
|
+
z37.object({ outcome: z37.literal("recorded"), id: z37.string().uuid(), say: z37.string().min(1) }).strict(),
|
|
7394
|
+
z37.object({
|
|
7395
|
+
outcome: z37.literal("go-ahead"),
|
|
7396
|
+
id: z37.string().uuid(),
|
|
7397
|
+
callId: z37.number().int().positive(),
|
|
7398
|
+
say: z37.string().min(1)
|
|
7319
7399
|
}).strict(),
|
|
7320
|
-
|
|
7400
|
+
z37.object({ outcome: z37.literal("refused"), reason: z37.enum(OWNER_WAIT_DECLARE_REFUSALS) }).strict()
|
|
7321
7401
|
]);
|
|
7322
|
-
var ownerWaitAddedItemSchema =
|
|
7323
|
-
id:
|
|
7324
|
-
words:
|
|
7325
|
-
reason:
|
|
7402
|
+
var ownerWaitAddedItemSchema = z37.object({
|
|
7403
|
+
id: z37.string().uuid(),
|
|
7404
|
+
words: z37.string().min(1),
|
|
7405
|
+
reason: z37.string().min(1),
|
|
7326
7406
|
addedAt: utcIso8601
|
|
7327
7407
|
}).strict();
|
|
7328
|
-
var ownerWaitListSchema =
|
|
7329
|
-
fixed:
|
|
7330
|
-
added:
|
|
7408
|
+
var ownerWaitListSchema = z37.object({
|
|
7409
|
+
fixed: z37.array(z37.object({ id: ownerWaitItemIdSchema, words: z37.string().min(1) }).strict()).length(OWNER_WAIT_FIXED_COUNT),
|
|
7410
|
+
added: z37.array(ownerWaitAddedItemSchema).max(OWNER_WAIT_ADDITIONS_MAX)
|
|
7331
7411
|
}).strict();
|
|
7332
|
-
var ownerWaitDeclarationSchema =
|
|
7333
|
-
id:
|
|
7412
|
+
var ownerWaitDeclarationSchema = z37.object({
|
|
7413
|
+
id: z37.string().uuid(),
|
|
7334
7414
|
fixedItem: ownerWaitItemIdSchema.nullable(),
|
|
7335
|
-
addition:
|
|
7336
|
-
question:
|
|
7337
|
-
recommendation:
|
|
7415
|
+
addition: z37.object({ id: z37.string().uuid(), words: z37.string().min(1) }).strict().nullable(),
|
|
7416
|
+
question: z37.string().min(1),
|
|
7417
|
+
recommendation: z37.string().min(1),
|
|
7338
7418
|
task: buildTaskIdSchema.nullable(),
|
|
7339
7419
|
declaredAt: utcIso8601
|
|
7340
7420
|
}).strict().refine((d) => d.fixedItem === null !== (d.addition === null), {
|
|
7341
7421
|
message: "names exactly one item: a fixed one or an added one",
|
|
7342
7422
|
path: ["fixedItem"]
|
|
7343
7423
|
});
|
|
7344
|
-
var ownerWaitDeclarationsSchema =
|
|
7345
|
-
var ownerWaitOpenSchema =
|
|
7424
|
+
var ownerWaitDeclarationsSchema = z37.object({ declarations: z37.array(ownerWaitDeclarationSchema) }).strict();
|
|
7425
|
+
var ownerWaitOpenSchema = z37.object({
|
|
7346
7426
|
wait: ownerWaitShortIdSchema,
|
|
7347
7427
|
task: buildTaskIdSchema.nullable(),
|
|
7348
|
-
question:
|
|
7349
|
-
recommendation:
|
|
7428
|
+
question: z37.string().min(1),
|
|
7429
|
+
recommendation: z37.string().min(1),
|
|
7350
7430
|
declaredAt: utcIso8601
|
|
7351
7431
|
}).strict();
|
|
7352
|
-
var ownerWaitsOpenSchema =
|
|
7432
|
+
var ownerWaitsOpenSchema = z37.object({ waits: z37.array(ownerWaitOpenSchema) }).strict();
|
|
7353
7433
|
function ownerWaitItemPhraseOf(item) {
|
|
7354
7434
|
const cut = item.words.split(", such as ")[0] ?? item.words;
|
|
7355
7435
|
const bare = cut.endsWith(".") ? cut.slice(0, -1) : cut;
|
|
@@ -7368,15 +7448,15 @@ function ownerWaitClosedLineOf(item, question2) {
|
|
|
7368
7448
|
var OWNER_WAIT_DECLARATIONS_READ_FAILED = "Halfcycle could not read the decisions that waited for you.";
|
|
7369
7449
|
|
|
7370
7450
|
// ../events/dist/autonomy.js
|
|
7371
|
-
import { z as
|
|
7451
|
+
import { z as z38 } from "zod";
|
|
7372
7452
|
var AUTONOMY_CHANGED_IN = ["console", "chat"];
|
|
7373
|
-
var autonomyChangedInSchema =
|
|
7374
|
-
var autonomySettingsSchema =
|
|
7375
|
-
on:
|
|
7453
|
+
var autonomyChangedInSchema = z38.enum(AUTONOMY_CHANGED_IN);
|
|
7454
|
+
var autonomySettingsSchema = z38.object({
|
|
7455
|
+
on: z38.boolean(),
|
|
7376
7456
|
// whether builds run on their own
|
|
7377
|
-
mayPublish:
|
|
7457
|
+
mayPublish: z38.boolean(),
|
|
7378
7458
|
// whether builds may publish or release without waiting
|
|
7379
|
-
maySpend:
|
|
7459
|
+
maySpend: z38.boolean(),
|
|
7380
7460
|
// whether builds may spend money or sign up without waiting
|
|
7381
7461
|
changedAt: utcIso8601.nullable(),
|
|
7382
7462
|
// when the setting last changed; null if never
|
|
@@ -7386,37 +7466,37 @@ var autonomySettingsSchema = z37.object({
|
|
|
7386
7466
|
message: "says when and where it changed, or neither",
|
|
7387
7467
|
path: ["changedIn"]
|
|
7388
7468
|
});
|
|
7389
|
-
var autonomyChangeRequestSchema =
|
|
7390
|
-
on:
|
|
7469
|
+
var autonomyChangeRequestSchema = z38.object({
|
|
7470
|
+
on: z38.boolean(),
|
|
7391
7471
|
// the switch
|
|
7392
|
-
mayPublish:
|
|
7472
|
+
mayPublish: z38.boolean(),
|
|
7393
7473
|
// the publish box
|
|
7394
|
-
maySpend:
|
|
7474
|
+
maySpend: z38.boolean()
|
|
7395
7475
|
// the spend box
|
|
7396
7476
|
}).strict();
|
|
7397
7477
|
var AUTONOMY_CHAT_ACTS = ["on", "off"];
|
|
7398
|
-
var autonomyChatActSchema =
|
|
7399
|
-
var autonomyChatRequestSchema =
|
|
7478
|
+
var autonomyChatActSchema = z38.enum(AUTONOMY_CHAT_ACTS);
|
|
7479
|
+
var autonomyChatRequestSchema = z38.object({
|
|
7400
7480
|
act: autonomyChatActSchema
|
|
7401
7481
|
// what the owner typed
|
|
7402
7482
|
}).strict();
|
|
7403
|
-
var autonomyChatResultSchema =
|
|
7404
|
-
|
|
7405
|
-
outcome:
|
|
7406
|
-
on:
|
|
7483
|
+
var autonomyChatResultSchema = z38.discriminatedUnion("outcome", [
|
|
7484
|
+
z38.object({
|
|
7485
|
+
outcome: z38.literal("recorded"),
|
|
7486
|
+
on: z38.boolean()
|
|
7407
7487
|
// whether builds now run on their own
|
|
7408
7488
|
}).strict(),
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7489
|
+
z38.object({ outcome: z38.literal("not-owner") }).strict(),
|
|
7490
|
+
z38.object({ outcome: z38.literal("no-owner") }).strict(),
|
|
7491
|
+
z38.object({ outcome: z38.literal("not-offered") }).strict(),
|
|
7492
|
+
z38.object({ outcome: z38.literal("unavailable") }).strict()
|
|
7413
7493
|
]);
|
|
7414
|
-
var autonomyStateSchema =
|
|
7415
|
-
on:
|
|
7494
|
+
var autonomyStateSchema = z38.object({
|
|
7495
|
+
on: z38.boolean(),
|
|
7416
7496
|
// whether builds run on their own
|
|
7417
|
-
mayPublish:
|
|
7497
|
+
mayPublish: z38.boolean(),
|
|
7418
7498
|
// whether builds may publish or release without waiting
|
|
7419
|
-
maySpend:
|
|
7499
|
+
maySpend: z38.boolean()
|
|
7420
7500
|
// whether builds may spend money or sign up without waiting
|
|
7421
7501
|
}).strict();
|
|
7422
7502
|
var autonomyStateFileSchema = autonomyStateSchema.extend({
|
|
@@ -7425,8 +7505,8 @@ var autonomyStateFileSchema = autonomyStateSchema.extend({
|
|
|
7425
7505
|
}).strict();
|
|
7426
7506
|
var AUTONOMY_STATE_FILE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
7427
7507
|
var AUTONOMY_ACTIVITY_KINDS = ["went-ahead", "waiting", "approved", "closed", "waiting-on-owner"];
|
|
7428
|
-
var autonomyActivityKindSchema =
|
|
7429
|
-
var autonomyActivityRowSchema =
|
|
7508
|
+
var autonomyActivityKindSchema = z38.enum(AUTONOMY_ACTIVITY_KINDS);
|
|
7509
|
+
var autonomyActivityRowSchema = z38.object({
|
|
7430
7510
|
kind: autonomyActivityKindSchema,
|
|
7431
7511
|
// what happened
|
|
7432
7512
|
phase: scopePhaseSchema,
|
|
@@ -7477,11 +7557,11 @@ var autonomyActivityRowSchema = z37.object({
|
|
|
7477
7557
|
ctx.addIssue({ code: "custom", message: "says how to undo it exactly when it can be undone", path: ["how"] });
|
|
7478
7558
|
}
|
|
7479
7559
|
});
|
|
7480
|
-
var autonomyActivitySchema =
|
|
7481
|
-
rows:
|
|
7560
|
+
var autonomyActivitySchema = z38.object({
|
|
7561
|
+
rows: z38.array(autonomyActivityRowSchema)
|
|
7482
7562
|
// newest first
|
|
7483
7563
|
}).strict();
|
|
7484
|
-
var savedIdSchema =
|
|
7564
|
+
var savedIdSchema = z38.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lower-case letters, digits and single hyphens");
|
|
7485
7565
|
var SAVED_REF_PREFIX = "refs/halfcycle/saved/";
|
|
7486
7566
|
function savedRefOf(id) {
|
|
7487
7567
|
return `${SAVED_REF_PREFIX}${id}`;
|
|
@@ -7520,33 +7600,33 @@ function isGitFolderSegment(segment) {
|
|
|
7520
7600
|
return t === ".git" || /^git~\d+$/.test(t);
|
|
7521
7601
|
}
|
|
7522
7602
|
var SAVED_PATH_KEPT_IN = ["commit", "copy"];
|
|
7523
|
-
var savedPathSchema =
|
|
7524
|
-
path:
|
|
7603
|
+
var savedPathSchema = z38.object({
|
|
7604
|
+
path: z38.string().max(4096).refine(isProjectRelativePath, { message: "must be a path inside the project" }),
|
|
7525
7605
|
// the file, relative to the project
|
|
7526
|
-
keptIn:
|
|
7606
|
+
keptIn: z38.enum(SAVED_PATH_KEPT_IN)
|
|
7527
7607
|
// in the save's commit, or copied beside this file
|
|
7528
7608
|
}).strict();
|
|
7529
|
-
var savedCommitSchema =
|
|
7530
|
-
var savedBranchSchema =
|
|
7531
|
-
name:
|
|
7609
|
+
var savedCommitSchema = z38.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash");
|
|
7610
|
+
var savedBranchSchema = z38.object({
|
|
7611
|
+
name: z38.string().min(1).max(255).refine((n) => !hasControlCharacter(n) && !n.startsWith("-") && !n.includes("..") && n !== "HEAD", {
|
|
7532
7612
|
message: "must be a branch name"
|
|
7533
7613
|
}),
|
|
7534
7614
|
// the branch's name
|
|
7535
7615
|
commit: savedCommitSchema
|
|
7536
7616
|
// the commit it pointed at
|
|
7537
7617
|
}).strict();
|
|
7538
|
-
var savedSchema =
|
|
7618
|
+
var savedSchema = z38.object({
|
|
7539
7619
|
id: savedIdSchema,
|
|
7540
7620
|
// what restore is typed with
|
|
7541
7621
|
savedAt: utcIso8601,
|
|
7542
7622
|
// when the copy was made
|
|
7543
|
-
ref:
|
|
7623
|
+
ref: z38.string().min(1),
|
|
7544
7624
|
// where the saved commit is kept
|
|
7545
|
-
what:
|
|
7625
|
+
what: z38.string().min(1).max(2e3),
|
|
7546
7626
|
// what the action would have changed, in words
|
|
7547
|
-
paths:
|
|
7627
|
+
paths: z38.array(savedPathSchema),
|
|
7548
7628
|
// every saved file
|
|
7549
|
-
branches:
|
|
7629
|
+
branches: z38.array(savedBranchSchema)
|
|
7550
7630
|
// every saved line of work
|
|
7551
7631
|
}).strict().refine((s) => s.ref === savedRefOf(s.id), { message: "must be the ref of this save", path: ["ref"] });
|
|
7552
7632
|
function autonomyA13(accepted, carried) {
|
|
@@ -7600,23 +7680,23 @@ function recordedClaimLine(claim) {
|
|
|
7600
7680
|
var NO_UNCHECKED_CLAIMS_LINE = "No agent said a check passes that Halfcycle never ran.";
|
|
7601
7681
|
|
|
7602
7682
|
// ../events/dist/relayed-choices.js
|
|
7603
|
-
import { z as
|
|
7683
|
+
import { z as z39 } from "zod";
|
|
7604
7684
|
var RELAYED_CHOICES_FILENAME = "relayed.json";
|
|
7605
7685
|
var RELAYED_SAID_MAX = 80;
|
|
7606
|
-
var relayedChoiceSchema =
|
|
7686
|
+
var relayedChoiceSchema = z39.object({
|
|
7607
7687
|
at: utcIso8601,
|
|
7608
|
-
said:
|
|
7688
|
+
said: z39.string().min(1).max(RELAYED_SAID_MAX)
|
|
7609
7689
|
}).strict();
|
|
7610
|
-
var relayedChoicesFileSchema =
|
|
7690
|
+
var relayedChoicesFileSchema = z39.object({
|
|
7611
7691
|
phase: phaseNameSchema,
|
|
7612
|
-
entries:
|
|
7692
|
+
entries: z39.array(relayedChoiceSchema)
|
|
7613
7693
|
}).strict();
|
|
7614
7694
|
function relayedChoiceLineOf(entry) {
|
|
7615
7695
|
return `On ${paceWhenOf(new Date(entry.at))} "${entry.said}" reached Halfcycle through Codex, which cannot show who typed it. Halfcycle could not confirm that you typed it, and it let this phase use more of your allowance.`;
|
|
7616
7696
|
}
|
|
7617
7697
|
|
|
7618
7698
|
// ../events/dist/tier-figures.js
|
|
7619
|
-
import { z as
|
|
7699
|
+
import { z as z40 } from "zod";
|
|
7620
7700
|
var TIER_ORDER = ["fast", "standard", "deep"];
|
|
7621
7701
|
function tierRankOf(tier) {
|
|
7622
7702
|
return TIER_ORDER.indexOf(tier);
|
|
@@ -7649,15 +7729,15 @@ function routeNoSuchTaskLineOf(task) {
|
|
|
7649
7729
|
return `"${task}" is not a task in the plan. Check the task id, then run this again.`;
|
|
7650
7730
|
}
|
|
7651
7731
|
var ROUTING_FILENAME = "routing.json";
|
|
7652
|
-
var routingRecordSchema =
|
|
7653
|
-
phase:
|
|
7654
|
-
cheapStarted:
|
|
7732
|
+
var routingRecordSchema = z40.object({
|
|
7733
|
+
phase: z40.string().min(1).max(200),
|
|
7734
|
+
cheapStarted: z40.array(z40.string().min(1).max(200)).max(2e3)
|
|
7655
7735
|
}).strict();
|
|
7656
7736
|
var PHASE_MODEL_NAME_MAX = 64;
|
|
7657
|
-
var phaseModelsFileSchema =
|
|
7658
|
-
phase:
|
|
7659
|
-
openedAt:
|
|
7660
|
-
tasks:
|
|
7737
|
+
var phaseModelsFileSchema = z40.object({
|
|
7738
|
+
phase: z40.string().min(1).max(200),
|
|
7739
|
+
openedAt: z40.string().min(1).max(64),
|
|
7740
|
+
tasks: z40.record(z40.string().min(1).max(200), z40.record(z40.string().min(1).max(PHASE_MODEL_NAME_MAX), z40.number().int().min(0)))
|
|
7661
7741
|
}).strict();
|
|
7662
7742
|
function inAttemptOrder(attempts) {
|
|
7663
7743
|
return [...attempts].sort((a, b) => a.attempt - b.attempt);
|
|
@@ -7715,9 +7795,9 @@ var CHECKS_IF_STILL_UNREPORTED = "If still unreported: Codex, approve in /hooks;
|
|
|
7715
7795
|
var CHECKS_GONE_QUIET = "Halfcycle's checks have not reported from this machine lately.";
|
|
7716
7796
|
|
|
7717
7797
|
// dist/engagement-credential.js
|
|
7718
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
7798
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
7719
7799
|
import { platform as platform2 } from "node:os";
|
|
7720
|
-
import { join as
|
|
7800
|
+
import { join as join4 } from "node:path";
|
|
7721
7801
|
|
|
7722
7802
|
// dist/account-credential.js
|
|
7723
7803
|
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -7776,6 +7856,46 @@ function forgetStoredCredential(serviceUrl, home) {
|
|
|
7776
7856
|
return true;
|
|
7777
7857
|
}
|
|
7778
7858
|
|
|
7859
|
+
// dist/own-version.js
|
|
7860
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
7861
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
7862
|
+
import { fileURLToPath } from "node:url";
|
|
7863
|
+
var moduleDir = dirname2(fileURLToPath(import.meta.url));
|
|
7864
|
+
function findBundleRoot(startDir) {
|
|
7865
|
+
let candidate = startDir;
|
|
7866
|
+
for (let i = 0; i < 4; i++) {
|
|
7867
|
+
const manifest = join3(candidate, ".claude-plugin", "plugin.json");
|
|
7868
|
+
try {
|
|
7869
|
+
if (existsSync2(manifest))
|
|
7870
|
+
return candidate;
|
|
7871
|
+
} catch {
|
|
7872
|
+
}
|
|
7873
|
+
candidate = dirname2(candidate);
|
|
7874
|
+
}
|
|
7875
|
+
return join3(startDir, "..");
|
|
7876
|
+
}
|
|
7877
|
+
var BUNDLE_ROOT = findBundleRoot(moduleDir);
|
|
7878
|
+
function bundleVersion() {
|
|
7879
|
+
try {
|
|
7880
|
+
const pkg = JSON.parse(readFileSync2(join3(BUNDLE_ROOT, "package.json"), "utf-8"));
|
|
7881
|
+
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
7882
|
+
} catch {
|
|
7883
|
+
return void 0;
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
var cachedVersion;
|
|
7887
|
+
function ownVersion() {
|
|
7888
|
+
if (cachedVersion === void 0)
|
|
7889
|
+
cachedVersion = { value: bundleVersion() };
|
|
7890
|
+
return cachedVersion.value;
|
|
7891
|
+
}
|
|
7892
|
+
function ownCommand() {
|
|
7893
|
+
return halfcycleCommand(ownVersion());
|
|
7894
|
+
}
|
|
7895
|
+
function ownBareCommand() {
|
|
7896
|
+
return bareHalfcycleCommand(ownVersion());
|
|
7897
|
+
}
|
|
7898
|
+
|
|
7779
7899
|
// dist/engagement-credential.js
|
|
7780
7900
|
var ENGAGEMENTS_DIR = ENGAGEMENTS_DIR_NAME;
|
|
7781
7901
|
var ENV_FILENAME = ENGAGEMENT_ENV_FILENAME;
|
|
@@ -7783,7 +7903,7 @@ var HALFCYCLE_DIR = HALFCYCLE_DIR_NAME;
|
|
|
7783
7903
|
var InvalidEngagementIdError = class extends Error {
|
|
7784
7904
|
constructor(engagementId) {
|
|
7785
7905
|
const shown2 = JSON.stringify(engagementId.length > 80 ? `${engagementId.slice(0, 80)}\u2026` : engagementId);
|
|
7786
|
-
super(`${shown2} is not a Halfcycle project id, so it cannot name a folder on this machine and nothing was written. A project id looks like 3f2a1c4e-8b7d-4e2f-9a61-0c5d7e8f9b10. If it is the "${PIN_ENGAGEMENT_ID_FIELD}" in .halfcycle/bundle.json, delete that file and run "
|
|
7906
|
+
super(`${shown2} is not a Halfcycle project id, so it cannot name a folder on this machine and nothing was written. A project id looks like 3f2a1c4e-8b7d-4e2f-9a61-0c5d7e8f9b10. If it is the "${PIN_ENGAGEMENT_ID_FIELD}" in .halfcycle/bundle.json, delete that file and run "${ownBareCommand()}" again: this repository is then set up as a new Halfcycle project.`);
|
|
7787
7907
|
this.name = "InvalidEngagementIdError";
|
|
7788
7908
|
}
|
|
7789
7909
|
};
|
|
@@ -7793,13 +7913,13 @@ function assertEngagementId(engagementId) {
|
|
|
7793
7913
|
}
|
|
7794
7914
|
function engagementStateDir(engagementId, home) {
|
|
7795
7915
|
assertEngagementId(engagementId);
|
|
7796
|
-
return
|
|
7916
|
+
return join4(halfcycleHome(home), ENGAGEMENTS_DIR, engagementId);
|
|
7797
7917
|
}
|
|
7798
7918
|
function engagementEnvPath(engagementId, home) {
|
|
7799
|
-
return
|
|
7919
|
+
return join4(engagementStateDir(engagementId, home), ENV_FILENAME);
|
|
7800
7920
|
}
|
|
7801
7921
|
function notThisAccountMarkerPath(engagementId, home) {
|
|
7802
|
-
return
|
|
7922
|
+
return join4(engagementStateDir(engagementId, home), NOT_THIS_ACCOUNT_MARKER_FILENAME);
|
|
7803
7923
|
}
|
|
7804
7924
|
function writeNotThisAccountMarker(engagementId, home) {
|
|
7805
7925
|
const dir = engagementStateDir(engagementId, home);
|
|
@@ -7829,7 +7949,7 @@ function reconcileEnvText2(existing, values, keys = ENGAGEMENT_ENV_KEYS) {
|
|
|
7829
7949
|
}
|
|
7830
7950
|
function readEngagementEnv(engagementId, home) {
|
|
7831
7951
|
try {
|
|
7832
|
-
return parseEnvText(
|
|
7952
|
+
return parseEnvText(readFileSync3(engagementEnvPath(engagementId, home), "utf-8"));
|
|
7833
7953
|
} catch {
|
|
7834
7954
|
return null;
|
|
7835
7955
|
}
|
|
@@ -7839,7 +7959,7 @@ function writeEngagementEnv(engagementId, values, home, keys = ENGAGEMENT_ENV_KE
|
|
|
7839
7959
|
const dir = engagementStateDir(engagementId, home);
|
|
7840
7960
|
let existing;
|
|
7841
7961
|
try {
|
|
7842
|
-
existing =
|
|
7962
|
+
existing = readFileSync3(path, "utf-8");
|
|
7843
7963
|
} catch {
|
|
7844
7964
|
existing = null;
|
|
7845
7965
|
}
|
|
@@ -7870,7 +7990,7 @@ function engagementResolutionShell() {
|
|
|
7870
7990
|
return `# --- Halfcycle credential resolution (generated; do not edit) ----------------
|
|
7871
7991
|
# THE CREDENTIAL IS NOT IN THIS REPOSITORY. It lives at
|
|
7872
7992
|
# $HOME/${HALFCYCLE_DIR}/${ENGAGEMENTS_DIR}/<engagement-id>/${ENV_FILENAME}
|
|
7873
|
-
# written owner-only by
|
|
7993
|
+
# written owner-only by \`${bareHalfcycleCommand()}\`. What this repository holds is the
|
|
7874
7994
|
# engagement id, in the committed .halfcycle/bundle.json. So: read the id out of
|
|
7875
7995
|
# the pin, then name the file. There is no jq here and node is not dependable on a
|
|
7876
7996
|
# desktop session's PATH, so the id is cut out with sed \u2014 after flattening the
|
|
@@ -7924,8 +8044,8 @@ halfcycle_env_file() {
|
|
|
7924
8044
|
|
|
7925
8045
|
// dist/identity.js
|
|
7926
8046
|
import { execFileSync } from "node:child_process";
|
|
7927
|
-
import { existsSync as
|
|
7928
|
-
import { dirname as
|
|
8047
|
+
import { existsSync as existsSync3, linkSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8048
|
+
import { dirname as dirname3, isAbsolute, join as join5 } from "node:path";
|
|
7929
8049
|
import { randomUUID } from "node:crypto";
|
|
7930
8050
|
var PROJECT_IDENTITY_NOTE = 'projectId is NOT the engagement id \u2014 it identifies this repository/worktree to Halfcycle, not an engagement. The engagement id lives in .halfcycle/bundle.json, in its "engagementId" field.';
|
|
7931
8051
|
var PROJECT_IDENTITY_REL = ".halfcycle/project.json";
|
|
@@ -8056,7 +8176,7 @@ function readIgnoredPaths(targetRepoRoot, paths) {
|
|
|
8056
8176
|
const toplevel = git(targetRepoRoot, ["rev-parse", "--show-toplevel"]);
|
|
8057
8177
|
if (toplevel === null)
|
|
8058
8178
|
return null;
|
|
8059
|
-
return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source:
|
|
8179
|
+
return ignored.map((entry) => isAbsolute(entry.source) ? entry : { ...entry, source: join5(toplevel, entry.source) });
|
|
8060
8180
|
}
|
|
8061
8181
|
function mintIdentity(targetRepoRoot) {
|
|
8062
8182
|
return {
|
|
@@ -8067,10 +8187,10 @@ function mintIdentity(targetRepoRoot) {
|
|
|
8067
8187
|
};
|
|
8068
8188
|
}
|
|
8069
8189
|
function plannedRemoteRepair(targetRepoRoot) {
|
|
8070
|
-
const path =
|
|
8190
|
+
const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
|
|
8071
8191
|
let onDisk;
|
|
8072
8192
|
try {
|
|
8073
|
-
const parsed = JSON.parse(
|
|
8193
|
+
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
8074
8194
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
8075
8195
|
return null;
|
|
8076
8196
|
onDisk = parsed;
|
|
@@ -8089,7 +8209,7 @@ function plannedRemoteRepair(targetRepoRoot) {
|
|
|
8089
8209
|
});
|
|
8090
8210
|
}
|
|
8091
8211
|
function replaceProjectIdentityFile(targetRepoRoot, text) {
|
|
8092
|
-
const path =
|
|
8212
|
+
const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
|
|
8093
8213
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
8094
8214
|
try {
|
|
8095
8215
|
writeFileSync3(temporary, text, { encoding: "utf-8", flag: "wx" });
|
|
@@ -8100,8 +8220,8 @@ function replaceProjectIdentityFile(targetRepoRoot, text) {
|
|
|
8100
8220
|
}
|
|
8101
8221
|
}
|
|
8102
8222
|
function planProjectIdentity(targetRepoRoot) {
|
|
8103
|
-
const path =
|
|
8104
|
-
if (!
|
|
8223
|
+
const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
|
|
8224
|
+
if (!existsSync3(path)) {
|
|
8105
8225
|
return { action: "add", after: serializeProjectIdentity(mintIdentity(targetRepoRoot)) };
|
|
8106
8226
|
}
|
|
8107
8227
|
const repaired = plannedRemoteRepair(targetRepoRoot);
|
|
@@ -8109,7 +8229,7 @@ function planProjectIdentity(targetRepoRoot) {
|
|
|
8109
8229
|
return { action: "change", after: repaired };
|
|
8110
8230
|
let current;
|
|
8111
8231
|
try {
|
|
8112
|
-
current =
|
|
8232
|
+
current = readFileSync4(path, "utf-8");
|
|
8113
8233
|
} catch {
|
|
8114
8234
|
current = null;
|
|
8115
8235
|
}
|
|
@@ -8119,8 +8239,8 @@ function serializeProjectIdentity(identity) {
|
|
|
8119
8239
|
return JSON.stringify(identity, null, 2) + "\n";
|
|
8120
8240
|
}
|
|
8121
8241
|
function createProjectIdentityFile(targetRepoRoot, text) {
|
|
8122
|
-
const path =
|
|
8123
|
-
mkdirSync3(
|
|
8242
|
+
const path = join5(targetRepoRoot, PROJECT_IDENTITY_REL);
|
|
8243
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
8124
8244
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
8125
8245
|
try {
|
|
8126
8246
|
writeFileSync3(temporary, text ?? serializeProjectIdentity(mintIdentity(targetRepoRoot)), {
|
|
@@ -8139,8 +8259,8 @@ function createProjectIdentityFile(targetRepoRoot, text) {
|
|
|
8139
8259
|
}
|
|
8140
8260
|
|
|
8141
8261
|
// dist/install-plan.js
|
|
8142
|
-
import { existsSync as
|
|
8143
|
-
import { join as
|
|
8262
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
|
|
8263
|
+
import { join as join7, posix } from "node:path";
|
|
8144
8264
|
|
|
8145
8265
|
// dist/install-manifest.js
|
|
8146
8266
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -8491,8 +8611,8 @@ function union(a, b) {
|
|
|
8491
8611
|
}
|
|
8492
8612
|
|
|
8493
8613
|
// dist/scan.js
|
|
8494
|
-
import { existsSync as
|
|
8495
|
-
import { join as
|
|
8614
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8615
|
+
import { join as join6 } from "node:path";
|
|
8496
8616
|
var HALFCYCLE_STATE_FORMAT = "halfcycle-state-file/v1";
|
|
8497
8617
|
var HALFCYCLE_STATE_NOTE = [
|
|
8498
8618
|
"This file is a local cache written ONCE when Halfcycle was installed. It is not a",
|
|
@@ -8515,7 +8635,7 @@ var LAYER_ARTEFACT_PATHS = {
|
|
|
8515
8635
|
function scanLayers(targetRepo, scannedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
8516
8636
|
const layers = {};
|
|
8517
8637
|
for (const [layer, paths] of Object.entries(LAYER_ARTEFACT_PATHS)) {
|
|
8518
|
-
const found = paths.filter((p) =>
|
|
8638
|
+
const found = paths.filter((p) => existsSync4(join6(targetRepo, p)));
|
|
8519
8639
|
if (found.length > 0) {
|
|
8520
8640
|
layers[layer] = {
|
|
8521
8641
|
value: "asserted",
|
|
@@ -8529,17 +8649,17 @@ function scanLayers(targetRepo, scannedAt = (/* @__PURE__ */ new Date()).toISOSt
|
|
|
8529
8649
|
}
|
|
8530
8650
|
var BOOTSTRAP_STATE_REL = ".halfcycle/state.json";
|
|
8531
8651
|
function planBootstrapScan(targetRepo) {
|
|
8532
|
-
const statePath =
|
|
8533
|
-
if (
|
|
8534
|
-
const existing = JSON.parse(
|
|
8652
|
+
const statePath = join6(targetRepo, BOOTSTRAP_STATE_REL);
|
|
8653
|
+
if (existsSync4(statePath)) {
|
|
8654
|
+
const existing = JSON.parse(readFileSync5(statePath, "utf-8"));
|
|
8535
8655
|
return { state: existing, text: null };
|
|
8536
8656
|
}
|
|
8537
8657
|
const state = scanLayers(targetRepo);
|
|
8538
8658
|
return { state, text: JSON.stringify(state, null, 2) + "\n" };
|
|
8539
8659
|
}
|
|
8540
8660
|
function writeBootstrapState(targetRepo, text) {
|
|
8541
|
-
mkdirSync4(
|
|
8542
|
-
writeFileSync4(
|
|
8661
|
+
mkdirSync4(join6(targetRepo, ".halfcycle"), { recursive: true });
|
|
8662
|
+
writeFileSync4(join6(targetRepo, BOOTSTRAP_STATE_REL), text, "utf-8");
|
|
8543
8663
|
}
|
|
8544
8664
|
|
|
8545
8665
|
// dist/install-plan.js
|
|
@@ -8689,12 +8809,12 @@ function planInstall(options) {
|
|
|
8689
8809
|
throw new Error("[halfcycle] a plan for a project needs its kind as well as its id.");
|
|
8690
8810
|
}
|
|
8691
8811
|
}
|
|
8692
|
-
if (!
|
|
8812
|
+
if (!existsSync5(targetRepo)) {
|
|
8693
8813
|
throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
|
|
8694
8814
|
}
|
|
8695
8815
|
const writeSet = closedWriteSet();
|
|
8696
8816
|
const previous = readPreviousManifest(targetRepo);
|
|
8697
|
-
const dirsBefore = new Set([...writeSet.dirs].filter((dir) =>
|
|
8817
|
+
const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync5(join7(targetRepo, dir))));
|
|
8698
8818
|
const { version, commands } = planCommandStubs(targetRepo, previous, writeSet);
|
|
8699
8819
|
const codexSkills = planCodexSkills(targetRepo, previous, writeSet);
|
|
8700
8820
|
const vendored = planVendoredBinary(targetRepo);
|
|
@@ -8716,7 +8836,7 @@ function planInstall(options) {
|
|
|
8716
8836
|
const gitattributes = planGitattributes(targetRepo);
|
|
8717
8837
|
const identity = planProjectIdentity(targetRepo);
|
|
8718
8838
|
if (identity.after === null)
|
|
8719
|
-
|
|
8839
|
+
readFileSync6(join7(targetRepo, PROJECT_IDENTITY_REL));
|
|
8720
8840
|
const credentialPlan = !preview && credential !== void 0 ? planEngagementCredential(credential, engagementId, home) : null;
|
|
8721
8841
|
const credentialWrite = credentialPlan === null ? null : { outcome: credentialPlan.outcome, existed: credentialPlan.existed, path: engagementEnvPath(engagementId, home) };
|
|
8722
8842
|
const storedAfter = credentialPlan?.storedAfter ?? (preview ? Object.fromEntries(ENGAGEMENT_ENV_KEYS.map((key) => [key, "supplied at sign-in"])) : readEngagementEnv(engagementId, home) ?? {});
|
|
@@ -8849,7 +8969,7 @@ function entriesOf(steps, record2) {
|
|
|
8849
8969
|
}
|
|
8850
8970
|
};
|
|
8851
8971
|
const pending = (rel) => {
|
|
8852
|
-
const there =
|
|
8972
|
+
const there = existsSync5(join7(targetRepo, rel));
|
|
8853
8973
|
entries.push({ path: rel, action: there ? "change" : "add", kind: kindOf(rel), bytes: PENDING });
|
|
8854
8974
|
};
|
|
8855
8975
|
steps.commands.forEach(fromFile);
|
|
@@ -8897,7 +9017,7 @@ function entriesOf(steps, record2) {
|
|
|
8897
9017
|
action: "leave",
|
|
8898
9018
|
kind: "codex-server",
|
|
8899
9019
|
bytes: null,
|
|
8900
|
-
...
|
|
9020
|
+
...existsSync5(join7(targetRepo, CODEX_CONFIG_REL2)) ? { yours: true } : {}
|
|
8901
9021
|
});
|
|
8902
9022
|
break;
|
|
8903
9023
|
}
|
|
@@ -8976,10 +9096,10 @@ function applyInstallPlan(plan) {
|
|
|
8976
9096
|
putPlannedCodexHooks(targetRepo, steps.codexHooks);
|
|
8977
9097
|
putPlannedCodexConfig(targetRepo, steps.codexConfig);
|
|
8978
9098
|
const gitignore = putPlannedGitignore(targetRepo, steps.gitignore);
|
|
8979
|
-
const gitignorePath =
|
|
8980
|
-
const gitignoreAfter =
|
|
9099
|
+
const gitignorePath = join7(targetRepo, GITIGNORE_REL);
|
|
9100
|
+
const gitignoreAfter = existsSync5(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : null;
|
|
8981
9101
|
const gitattributes = putPlannedGitattributes(targetRepo, steps.gitattributes);
|
|
8982
|
-
const gitattributesAfter = readTextOrNull(
|
|
9102
|
+
const gitattributesAfter = readTextOrNull(join7(targetRepo, GITATTRIBUTES_REL));
|
|
8983
9103
|
writeProjectIdentity(targetRepo, steps.identity);
|
|
8984
9104
|
const credential = steps.credential;
|
|
8985
9105
|
const credentialOutcome = steps.credentialWrite !== null && credential !== void 0 ? writeEngagementCredential(credential, engagementId, steps.home) : null;
|
|
@@ -9000,9 +9120,9 @@ function applyInstallPlan(plan) {
|
|
|
9000
9120
|
gitattributesAfter,
|
|
9001
9121
|
credential: credentialOutcome,
|
|
9002
9122
|
legacy,
|
|
9003
|
-
bytesAfter: (rel) =>
|
|
9004
|
-
dirExistsAfter: (dir) =>
|
|
9005
|
-
stateExistsAfter:
|
|
9123
|
+
bytesAfter: (rel) => readFileSync6(join7(targetRepo, rel)),
|
|
9124
|
+
dirExistsAfter: (dir) => existsSync5(join7(targetRepo, dir)),
|
|
9125
|
+
stateExistsAfter: existsSync5(join7(targetRepo, BOOTSTRAP_STATE_REL))
|
|
9006
9126
|
});
|
|
9007
9127
|
const record2 = planInstallRecord(targetRepo, manifestText);
|
|
9008
9128
|
putPlannedFile(targetRepo, record2);
|
|
@@ -9360,8 +9480,8 @@ function crewProfileRel(agent, callsign) {
|
|
|
9360
9480
|
}
|
|
9361
9481
|
function crewProfileTarget(targetRepo, rel) {
|
|
9362
9482
|
const folder2 = rel.startsWith(`${CLAUDE_AGENTS_DIR}/`) ? CLAUDE_AGENTS_DIR : CODEX_AGENTS_DIR;
|
|
9363
|
-
const target = resolve(
|
|
9364
|
-
if (
|
|
9483
|
+
const target = resolve(join8(targetRepo, rel));
|
|
9484
|
+
if (dirname4(target) !== resolve(targetRepo, folder2)) {
|
|
9365
9485
|
throw new Error(`[halfcycle] crew profile "${rel}" does not sit directly in ${folder2}.`);
|
|
9366
9486
|
}
|
|
9367
9487
|
return target;
|
|
@@ -9444,41 +9564,25 @@ function renderCrewProfiles(roster = CREW_ROSTER) {
|
|
|
9444
9564
|
])
|
|
9445
9565
|
];
|
|
9446
9566
|
}
|
|
9447
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
9448
|
-
var __dirname = dirname3(__filename);
|
|
9449
|
-
function findBundleRoot(startDir) {
|
|
9450
|
-
let candidate = startDir;
|
|
9451
|
-
for (let i = 0; i < 4; i++) {
|
|
9452
|
-
const manifest = join7(candidate, ".claude-plugin", "plugin.json");
|
|
9453
|
-
try {
|
|
9454
|
-
if (existsSync5(manifest))
|
|
9455
|
-
return candidate;
|
|
9456
|
-
} catch {
|
|
9457
|
-
}
|
|
9458
|
-
candidate = dirname3(candidate);
|
|
9459
|
-
}
|
|
9460
|
-
return join7(startDir, "..");
|
|
9461
|
-
}
|
|
9462
|
-
var BUNDLE_ROOT = findBundleRoot(__dirname);
|
|
9463
9567
|
function resolveVendoredBinary() {
|
|
9464
9568
|
const candidates = [
|
|
9465
|
-
|
|
9466
|
-
|
|
9569
|
+
join8(BUNDLE_ROOT, "bin", "bin.bundle.mjs"),
|
|
9570
|
+
join8(BUNDLE_ROOT, "..", "runner", "dist", "bin.bundle.mjs")
|
|
9467
9571
|
];
|
|
9468
9572
|
for (const candidate of candidates) {
|
|
9469
|
-
if (
|
|
9573
|
+
if (existsSync6(candidate))
|
|
9470
9574
|
return candidate;
|
|
9471
9575
|
}
|
|
9472
9576
|
throw new Error(`[halfcycle] Cannot find the self-contained guard binary bin.bundle.mjs. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
|
|
9473
9577
|
}
|
|
9474
9578
|
function resolvePreFilterShell() {
|
|
9475
9579
|
const candidates = [
|
|
9476
|
-
|
|
9477
|
-
|
|
9580
|
+
join8(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
|
|
9581
|
+
join8(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
|
|
9478
9582
|
];
|
|
9479
9583
|
for (const candidate of candidates) {
|
|
9480
|
-
if (
|
|
9481
|
-
return
|
|
9584
|
+
if (existsSync6(candidate))
|
|
9585
|
+
return readFileSync7(candidate, "utf-8");
|
|
9482
9586
|
}
|
|
9483
9587
|
throw new Error(`[halfcycle] Cannot find ${PRE_FILTER_SHELL_FILENAME}. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
|
|
9484
9588
|
}
|
|
@@ -9517,7 +9621,7 @@ function isAllowlisted(targetRelPath) {
|
|
|
9517
9621
|
var CREW_ROSTER_REL = ".halfcycle/crew.json";
|
|
9518
9622
|
var CAPTURED_INDEX_REL = "test/fixtures/captured/manifest.json";
|
|
9519
9623
|
function capturedIndexScaffold() {
|
|
9520
|
-
return
|
|
9624
|
+
return join8(BUNDLE_ROOT, "scaffolding", "test", "fixtures", "captured", "manifest.json");
|
|
9521
9625
|
}
|
|
9522
9626
|
var GITIGNORE_REL = ".gitignore";
|
|
9523
9627
|
var GITATTRIBUTES_REL = ".gitattributes";
|
|
@@ -9592,14 +9696,15 @@ function closedWriteSet() {
|
|
|
9592
9696
|
function sha256OfWhatThisVersionWrites(commands) {
|
|
9593
9697
|
const out = /* @__PURE__ */ new Map();
|
|
9594
9698
|
const fromFile = (rel, src) => {
|
|
9595
|
-
if (
|
|
9596
|
-
out.set(rel, fileSha256(
|
|
9699
|
+
if (existsSync6(src))
|
|
9700
|
+
out.set(rel, fileSha256(readFileSync7(src)));
|
|
9597
9701
|
};
|
|
9598
9702
|
for (const cmd of commands) {
|
|
9599
|
-
const srcFile =
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
out.set(
|
|
9703
|
+
const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
|
|
9704
|
+
if (existsSync6(srcFile)) {
|
|
9705
|
+
const text = commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion());
|
|
9706
|
+
out.set(commandStubRel(cmd.name), fileSha256(Buffer.from(text, "utf-8")));
|
|
9707
|
+
out.set(skillRel(cmd.name), fileSha256(Buffer.from(codexSkillContent(cmd.name, text), "utf-8")));
|
|
9603
9708
|
}
|
|
9604
9709
|
}
|
|
9605
9710
|
for (const [rel, content] of hookScriptsThisVersionWrites())
|
|
@@ -9616,7 +9721,7 @@ function bytesAreOurs(path, bytes, recorded, writeSet) {
|
|
|
9616
9721
|
}
|
|
9617
9722
|
function readPreviousManifest(targetRepo) {
|
|
9618
9723
|
try {
|
|
9619
|
-
return parseManifest(
|
|
9724
|
+
return parseManifest(readFileSync7(join8(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
|
|
9620
9725
|
} catch {
|
|
9621
9726
|
return null;
|
|
9622
9727
|
}
|
|
@@ -9720,8 +9825,8 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
|
|
|
9720
9825
|
for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
|
|
9721
9826
|
let plain = dirVerdicts.get(dir);
|
|
9722
9827
|
if (plain === void 0) {
|
|
9723
|
-
const abs =
|
|
9724
|
-
plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(
|
|
9828
|
+
const abs = join8(targetRepo, dir);
|
|
9829
|
+
plain = !lstatOrNull(abs)?.isSymbolicLink() && lstatOrNull(join8(abs, ".git")) === null;
|
|
9725
9830
|
dirVerdicts.set(dir, plain);
|
|
9726
9831
|
}
|
|
9727
9832
|
if (!plain)
|
|
@@ -9735,7 +9840,7 @@ function findNotShared(targetRepo, manifest, collidedPaths) {
|
|
|
9735
9840
|
...merged,
|
|
9736
9841
|
INSTALL_MANIFEST_REL
|
|
9737
9842
|
])
|
|
9738
|
-
].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(
|
|
9843
|
+
].filter((rel) => !collided.has(rel)).filter((rel) => lstatOrNull(join8(targetRepo, rel)) !== null).filter(travelsThroughPlainDirs).sort();
|
|
9739
9844
|
const ignored = readIgnoredPaths(targetRepo, paths);
|
|
9740
9845
|
if (ignored === null)
|
|
9741
9846
|
return null;
|
|
@@ -9758,7 +9863,7 @@ function lstatOrNull(path) {
|
|
|
9758
9863
|
}
|
|
9759
9864
|
function readFileOrNull(path) {
|
|
9760
9865
|
try {
|
|
9761
|
-
return
|
|
9866
|
+
return readFileSync7(path, "utf-8");
|
|
9762
9867
|
} catch {
|
|
9763
9868
|
return null;
|
|
9764
9869
|
}
|
|
@@ -9767,8 +9872,8 @@ function isPlainObject3(value) {
|
|
|
9767
9872
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9768
9873
|
}
|
|
9769
9874
|
function readPluginManifest() {
|
|
9770
|
-
const manifestPath =
|
|
9771
|
-
const raw =
|
|
9875
|
+
const manifestPath = join8(BUNDLE_ROOT, ".claude-plugin", "plugin.json");
|
|
9876
|
+
const raw = readFileSync7(manifestPath, "utf-8");
|
|
9772
9877
|
return JSON.parse(raw);
|
|
9773
9878
|
}
|
|
9774
9879
|
function parseCommandStub(raw, name) {
|
|
@@ -9811,6 +9916,9 @@ function codexSkillBody(name, sourceBody) {
|
|
|
9811
9916
|
}
|
|
9812
9917
|
return transformed;
|
|
9813
9918
|
}
|
|
9919
|
+
function commandFileText(source, version) {
|
|
9920
|
+
return rewriteHalfcycleCommands(source, releaseTagOfVersion(version));
|
|
9921
|
+
}
|
|
9814
9922
|
function codexSkillContent(name, sourceRaw) {
|
|
9815
9923
|
const { description, body } = parseCommandStub(sourceRaw, name);
|
|
9816
9924
|
const transformed = codexSkillBody(name, body);
|
|
@@ -9823,14 +9931,14 @@ ${transformed}`;
|
|
|
9823
9931
|
function planManifestCommands(manifest, targetRepo, previous, writeSet) {
|
|
9824
9932
|
const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
|
|
9825
9933
|
return (manifest.commands ?? []).map((cmd) => {
|
|
9826
|
-
const srcFile =
|
|
9827
|
-
if (!
|
|
9934
|
+
const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
|
|
9935
|
+
if (!existsSync6(srcFile)) {
|
|
9828
9936
|
throw new Error(`[halfcycle] plugin.json declares command "${cmd.name}" at "${cmd.path}", but no file exists there (${srcFile}). The manifest and commands/ must agree.`);
|
|
9829
9937
|
}
|
|
9830
9938
|
const rel = commandStubRel(cmd.name);
|
|
9831
|
-
const content =
|
|
9939
|
+
const content = commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion());
|
|
9832
9940
|
const recorded = recordedFiles.get(rel);
|
|
9833
|
-
const planned = decideCollisionSafe(
|
|
9941
|
+
const planned = decideCollisionSafe(join8(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
|
|
9834
9942
|
return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
|
|
9835
9943
|
});
|
|
9836
9944
|
}
|
|
@@ -9840,8 +9948,8 @@ function putPlannedFile(targetRepoRoot, planned) {
|
|
|
9840
9948
|
if (!isAllowlisted(planned.rel)) {
|
|
9841
9949
|
throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${planned.rel}". Only the method surface and scaffolding paths are writable.`);
|
|
9842
9950
|
}
|
|
9843
|
-
const target =
|
|
9844
|
-
mkdirSync5(
|
|
9951
|
+
const target = join8(targetRepoRoot, planned.rel);
|
|
9952
|
+
mkdirSync5(dirname4(target), { recursive: true });
|
|
9845
9953
|
writeFileSync5(target, planned.text, "utf-8");
|
|
9846
9954
|
}
|
|
9847
9955
|
function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths) {
|
|
@@ -9851,7 +9959,7 @@ function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths)
|
|
|
9851
9959
|
}
|
|
9852
9960
|
if (readFileOrNull(targetAbsPath) === content)
|
|
9853
9961
|
return;
|
|
9854
|
-
mkdirSync5(
|
|
9962
|
+
mkdirSync5(dirname4(targetAbsPath), { recursive: true });
|
|
9855
9963
|
writeFileSync5(targetAbsPath, content, "utf-8");
|
|
9856
9964
|
writtenPaths.push(rel);
|
|
9857
9965
|
}
|
|
@@ -9860,7 +9968,7 @@ function decideCollisionSafe(targetAbsPath, targetRepoRoot, content, isOurs) {
|
|
|
9860
9968
|
if (!isAllowlisted(rel)) {
|
|
9861
9969
|
throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
|
|
9862
9970
|
}
|
|
9863
|
-
const current =
|
|
9971
|
+
const current = existsSync6(targetAbsPath) ? readFileSync7(targetAbsPath) : null;
|
|
9864
9972
|
const text = inLineEndingOf(current?.toString("utf-8") ?? null, content);
|
|
9865
9973
|
if (current === null)
|
|
9866
9974
|
return { rel, text, outcome: "written", replacedExisting: false };
|
|
@@ -9888,7 +9996,7 @@ function decideOwned(targetAbsPath, targetRepoRoot, content) {
|
|
|
9888
9996
|
const text = inLineEndingOf(current, content);
|
|
9889
9997
|
if (current === text)
|
|
9890
9998
|
return { rel, text, outcome: "skipped", replacedExisting: false };
|
|
9891
|
-
return { rel, text, outcome: "written", replacedExisting:
|
|
9999
|
+
return { rel, text, outcome: "written", replacedExisting: existsSync6(targetAbsPath) };
|
|
9892
10000
|
}
|
|
9893
10001
|
var GENERATED_HEADER_LINE_INDEX = 1;
|
|
9894
10002
|
function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedMarker) {
|
|
@@ -9896,9 +10004,9 @@ function decideOwnedGenerated(targetAbsPath, targetRepoRoot, content, generatedM
|
|
|
9896
10004
|
if (!isAllowlisted(rel)) {
|
|
9897
10005
|
throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
|
|
9898
10006
|
}
|
|
9899
|
-
if (!
|
|
10007
|
+
if (!existsSync6(targetAbsPath))
|
|
9900
10008
|
return { rel, text: content, outcome: "written", replacedExisting: false };
|
|
9901
|
-
const current =
|
|
10009
|
+
const current = readFileSync7(targetAbsPath, "utf-8");
|
|
9902
10010
|
if (current === content)
|
|
9903
10011
|
return { rel, text: content, outcome: "skipped", replacedExisting: false };
|
|
9904
10012
|
const headerLine = current.split("\n")[GENERATED_HEADER_LINE_INDEX] ?? "";
|
|
@@ -9914,7 +10022,7 @@ function recordOwnedGenerated(report, planned) {
|
|
|
9914
10022
|
return planned.outcome;
|
|
9915
10023
|
}
|
|
9916
10024
|
function recordOwned(report, targetAbsPath, targetRepoRoot, content, rel) {
|
|
9917
|
-
const existedBefore =
|
|
10025
|
+
const existedBefore = existsSync6(targetAbsPath);
|
|
9918
10026
|
const outcome = writeOwned(targetAbsPath, targetRepoRoot, content);
|
|
9919
10027
|
record(report, outcome, rel);
|
|
9920
10028
|
if (outcome === "written" && existedBefore)
|
|
@@ -9936,8 +10044,8 @@ var DENY_PATTERNS = [
|
|
|
9936
10044
|
];
|
|
9937
10045
|
var USER_PROMPT_REMINDER_SENTENCE = "Where this project stands, and what order to work in, is recorded in the Halfcycle service and nowhere in this repository \u2014 call halfcycle_resolve before you answer or plan, because the files here will look complete and current and still be wrong about position.";
|
|
9938
10046
|
function readExistingSettings(targetRepo) {
|
|
9939
|
-
const settingsPath =
|
|
9940
|
-
return
|
|
10047
|
+
const settingsPath = join8(targetRepo, SETTINGS_REL);
|
|
10048
|
+
return existsSync6(settingsPath) ? JSON.parse(readFileSync7(settingsPath, "utf-8")) : void 0;
|
|
9941
10049
|
}
|
|
9942
10050
|
var SettingsParseRefused = class extends Error {
|
|
9943
10051
|
constructor(message) {
|
|
@@ -9946,14 +10054,14 @@ var SettingsParseRefused = class extends Error {
|
|
|
9946
10054
|
}
|
|
9947
10055
|
};
|
|
9948
10056
|
function settingsParseRefusal(targetRepo, err) {
|
|
9949
|
-
const file =
|
|
10057
|
+
const file = join8(targetRepo, SETTINGS_REL);
|
|
9950
10058
|
const offset = /position (\d+)/.exec(err instanceof Error ? err.message : "")?.[1];
|
|
9951
10059
|
let where = "";
|
|
9952
10060
|
if (offset !== void 0) {
|
|
9953
|
-
const before =
|
|
10061
|
+
const before = readFileSync7(file, "utf-8").slice(0, Number(offset)).split("\n");
|
|
9954
10062
|
where = ` (line ${before.length}, column ${before[before.length - 1].length + 1})`;
|
|
9955
10063
|
}
|
|
9956
|
-
return `${file} could not be read as JSON${where} \u2014 fix it and run "
|
|
10064
|
+
return `${file} could not be read as JSON${where} \u2014 fix it and run "${ownBareCommand()}" again.`;
|
|
9957
10065
|
}
|
|
9958
10066
|
var SETTINGS_SCHEMA = "https://json.schemastore.org/claude-code-settings.json";
|
|
9959
10067
|
var HALFCYCLE_STATUS_LINE = Object.freeze({
|
|
@@ -9961,18 +10069,18 @@ var HALFCYCLE_STATUS_LINE = Object.freeze({
|
|
|
9961
10069
|
command: 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/guard-runner.sh statusline'
|
|
9962
10070
|
});
|
|
9963
10071
|
function statusLineSetElsewhere(targetRepo, home = homedir3(), env = process.env) {
|
|
9964
|
-
const places = [
|
|
10072
|
+
const places = [join8(targetRepo, ".claude", "settings.local.json"), join8(home, ".claude", "settings.json")];
|
|
9965
10073
|
const configDir = env["CLAUDE_CONFIG_DIR"];
|
|
9966
10074
|
if (configDir !== void 0 && configDir !== "") {
|
|
9967
|
-
const expanded = configDir === "~" ? home : configDir.startsWith("~/") ?
|
|
9968
|
-
places.push(
|
|
10075
|
+
const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join8(home, configDir.slice(2)) : configDir;
|
|
10076
|
+
places.push(join8(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
|
|
9969
10077
|
}
|
|
9970
10078
|
return places.some((path) => {
|
|
9971
|
-
if (!
|
|
10079
|
+
if (!existsSync6(path))
|
|
9972
10080
|
return false;
|
|
9973
10081
|
let value;
|
|
9974
10082
|
try {
|
|
9975
|
-
value = JSON.parse(
|
|
10083
|
+
value = JSON.parse(readFileSync7(path, "utf-8"));
|
|
9976
10084
|
} catch {
|
|
9977
10085
|
return true;
|
|
9978
10086
|
}
|
|
@@ -10446,12 +10554,12 @@ function removeRetiredGeneratedFile(targetRepo, rel, headerLineIndex, knownHeade
|
|
|
10446
10554
|
return decided === "removed" ? removePlannedRetiredFile(targetRepo, rel) : decided;
|
|
10447
10555
|
}
|
|
10448
10556
|
function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashes) {
|
|
10449
|
-
const path =
|
|
10450
|
-
if (!
|
|
10557
|
+
const path = join8(targetRepo, rel);
|
|
10558
|
+
if (!existsSync6(path))
|
|
10451
10559
|
return "absent";
|
|
10452
10560
|
let raw;
|
|
10453
10561
|
try {
|
|
10454
|
-
raw =
|
|
10562
|
+
raw = readFileSync7(path, "utf-8");
|
|
10455
10563
|
} catch {
|
|
10456
10564
|
return "failed";
|
|
10457
10565
|
}
|
|
@@ -10465,7 +10573,7 @@ function decideRetiredRemoval(targetRepo, rel, headerLineIndex, knownHeaderHashe
|
|
|
10465
10573
|
}
|
|
10466
10574
|
function removePlannedRetiredFile(targetRepo, rel) {
|
|
10467
10575
|
try {
|
|
10468
|
-
rmSync2(
|
|
10576
|
+
rmSync2(join8(targetRepo, rel));
|
|
10469
10577
|
} catch {
|
|
10470
10578
|
return "failed";
|
|
10471
10579
|
}
|
|
@@ -10486,7 +10594,7 @@ var MCP_HEADERS_HELPER_COMMAND = `node -e "${nodeFloorProgram([
|
|
|
10486
10594
|
`const u=p.dirname(d);`,
|
|
10487
10595
|
`if(u===d)break;`,
|
|
10488
10596
|
`d=u}`,
|
|
10489
|
-
`console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run
|
|
10597
|
+
`console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
|
|
10490
10598
|
`process.exit(1)`
|
|
10491
10599
|
].join(""))}"`;
|
|
10492
10600
|
var CODEX_ADDRESS_PATTERN = /^https?:\/\/[A-Za-z0-9.-]+(:[0-9]{1,5})?(\/[A-Za-z0-9._-]+)*\/mcp$/;
|
|
@@ -10510,7 +10618,7 @@ function codexMcpHeadersHelperCommand(address) {
|
|
|
10510
10618
|
`if(u===d)break;`,
|
|
10511
10619
|
`d=u}`,
|
|
10512
10620
|
`if(b===''){`,
|
|
10513
|
-
`console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run
|
|
10621
|
+
`console.error('halfcycle: no ${VENDORED_BIN_REL} at or above '+process.cwd()+'; run ${ownBareCommand()} in the project folder');`,
|
|
10514
10622
|
`n()}else{`,
|
|
10515
10623
|
`const r=c.spawnSync(process.execPath,[b,'mcp-headers','--url','${address}'],{stdio:['ignore','pipe','inherit']});`,
|
|
10516
10624
|
`if(r.status===0&&r.stdout&&r.stdout.length){o=1;process.stdout.write(r.stdout)}else{n()}}`,
|
|
@@ -10848,9 +10956,9 @@ function gitignoreCovers(content) {
|
|
|
10848
10956
|
return REQUIRED_GITIGNORE_ENTRIES.every((entry) => lines.has(entry));
|
|
10849
10957
|
}
|
|
10850
10958
|
function planGitignore(targetRepo) {
|
|
10851
|
-
const gitignorePath =
|
|
10959
|
+
const gitignorePath = join8(targetRepo, GITIGNORE_REL);
|
|
10852
10960
|
const header = GITIGNORE_HEADER;
|
|
10853
|
-
const before =
|
|
10961
|
+
const before = existsSync6(gitignorePath) ? readFileSync7(gitignorePath, "utf-8") : null;
|
|
10854
10962
|
if (before === null) {
|
|
10855
10963
|
return { before, after: [header, ...REQUIRED_GITIGNORE_ENTRIES].join("\n") + "\n", outcome: "written" };
|
|
10856
10964
|
}
|
|
@@ -10866,7 +10974,7 @@ function putPlannedGitignore(targetRepo, planned) {
|
|
|
10866
10974
|
if (planned.outcome !== "written" || planned.after === null)
|
|
10867
10975
|
return planned.outcome;
|
|
10868
10976
|
try {
|
|
10869
|
-
writeFileSync5(
|
|
10977
|
+
writeFileSync5(join8(targetRepo, GITIGNORE_REL), planned.after, "utf-8");
|
|
10870
10978
|
return "written";
|
|
10871
10979
|
} catch {
|
|
10872
10980
|
return "failed";
|
|
@@ -10875,7 +10983,7 @@ function putPlannedGitignore(targetRepo, planned) {
|
|
|
10875
10983
|
var GITATTRIBUTES_HEADER = "# Halfcycle \u2014 generated scripts keep LF line endings";
|
|
10876
10984
|
function readTextOrNull(path) {
|
|
10877
10985
|
try {
|
|
10878
|
-
return
|
|
10986
|
+
return readFileSync7(path, "utf-8");
|
|
10879
10987
|
} catch {
|
|
10880
10988
|
return null;
|
|
10881
10989
|
}
|
|
@@ -10885,12 +10993,12 @@ function lfRules(paths) {
|
|
|
10885
10993
|
}
|
|
10886
10994
|
var REQUIRED_GITATTRIBUTES_RULES = lfRules(Object.keys(OWNED_GENERATED_HEADERS));
|
|
10887
10995
|
function planGitattributes(targetRepo) {
|
|
10888
|
-
const path =
|
|
10996
|
+
const path = join8(targetRepo, GITATTRIBUTES_REL);
|
|
10889
10997
|
const rules = REQUIRED_GITATTRIBUTES_RULES;
|
|
10890
10998
|
const before = readTextOrNull(path);
|
|
10891
10999
|
if (lstatOrNull(path)?.isSymbolicLink() === true)
|
|
10892
11000
|
return { before, after: before, outcome: "failed" };
|
|
10893
|
-
if (!
|
|
11001
|
+
if (!existsSync6(path)) {
|
|
10894
11002
|
return { before, after: [GITATTRIBUTES_HEADER, ...rules].join("\n") + "\n", outcome: "written" };
|
|
10895
11003
|
}
|
|
10896
11004
|
if (before === null)
|
|
@@ -10908,7 +11016,7 @@ function putPlannedGitattributes(targetRepo, planned) {
|
|
|
10908
11016
|
if (planned.outcome !== "written" || planned.after === null)
|
|
10909
11017
|
return planned.outcome;
|
|
10910
11018
|
try {
|
|
10911
|
-
writeAllowlisted(
|
|
11019
|
+
writeAllowlisted(join8(targetRepo, GITATTRIBUTES_REL), targetRepo, planned.after, []);
|
|
10912
11020
|
return "written";
|
|
10913
11021
|
} catch {
|
|
10914
11022
|
return "failed";
|
|
@@ -10942,7 +11050,7 @@ function planBundlePin(targetRepoRoot, version, engagementId, engagementType, cr
|
|
|
10942
11050
|
...carriedMethodVersion !== void 0 ? { methodVersion: carriedMethodVersion } : {},
|
|
10943
11051
|
...projectPage !== void 0 ? { projectPage } : {}
|
|
10944
11052
|
};
|
|
10945
|
-
const current = readFileOrNull(
|
|
11053
|
+
const current = readFileOrNull(join8(targetRepoRoot, BUNDLE_PIN_REL));
|
|
10946
11054
|
const text = inLineEndingOf(current, JSON.stringify(pin, null, 2) + "\n");
|
|
10947
11055
|
return {
|
|
10948
11056
|
rel: BUNDLE_PIN_REL,
|
|
@@ -10973,7 +11081,7 @@ function planBundlePinPreview(targetRepoRoot, version, home) {
|
|
|
10973
11081
|
return planBundlePin(targetRepoRoot, version, existing.engagementId, existing.engagementType, void 0, void 0);
|
|
10974
11082
|
}
|
|
10975
11083
|
function writeBundlePin(targetRepoRoot, planned, writtenPaths) {
|
|
10976
|
-
const pinPath =
|
|
11084
|
+
const pinPath = join8(targetRepoRoot, BUNDLE_PIN_REL);
|
|
10977
11085
|
writeAllowlisted(pinPath, targetRepoRoot, planned.text, writtenPaths);
|
|
10978
11086
|
}
|
|
10979
11087
|
function updatePinnedMethodVersion(repoRoot, engagementId, version) {
|
|
@@ -10985,7 +11093,7 @@ function updatePinnedMethodVersion(repoRoot, engagementId, version) {
|
|
|
10985
11093
|
}
|
|
10986
11094
|
if (pin === null || pin.engagementId !== engagementId)
|
|
10987
11095
|
return;
|
|
10988
|
-
const pinPath =
|
|
11096
|
+
const pinPath = join8(repoRoot, BUNDLE_PIN_REL);
|
|
10989
11097
|
const updated = { ...pin, methodVersion: version };
|
|
10990
11098
|
const pinText = inLineEndingOf(readFileOrNull(pinPath), JSON.stringify(updated, null, 2) + "\n");
|
|
10991
11099
|
try {
|
|
@@ -11005,17 +11113,17 @@ function renderCrewRoster() {
|
|
|
11005
11113
|
}
|
|
11006
11114
|
function writeCrewRoster(targetRepoRoot, report) {
|
|
11007
11115
|
const rendered = renderCrewRoster();
|
|
11008
|
-
const crewPath =
|
|
11116
|
+
const crewPath = join8(targetRepoRoot, CREW_ROSTER_REL);
|
|
11009
11117
|
return recordOwned(report, crewPath, targetRepoRoot, rendered, CREW_ROSTER_REL);
|
|
11010
11118
|
}
|
|
11011
11119
|
function pinCreatedByAccountId(pin) {
|
|
11012
11120
|
return nonEmpty(pin?.createdByAccountId) ?? nonEmpty(pin?.accountId);
|
|
11013
11121
|
}
|
|
11014
11122
|
function readBundlePin(targetRepoRoot) {
|
|
11015
|
-
const pinPath =
|
|
11016
|
-
if (!
|
|
11123
|
+
const pinPath = join8(targetRepoRoot, BUNDLE_PIN_REL);
|
|
11124
|
+
if (!existsSync6(pinPath))
|
|
11017
11125
|
return null;
|
|
11018
|
-
return JSON.parse(
|
|
11126
|
+
return JSON.parse(readFileSync7(pinPath, "utf-8"));
|
|
11019
11127
|
}
|
|
11020
11128
|
function readPinnedEngagement(targetRepoRoot, home) {
|
|
11021
11129
|
const pin = readBundlePin(targetRepoRoot);
|
|
@@ -11038,7 +11146,7 @@ function readPinnedEngagement(targetRepoRoot, home) {
|
|
|
11038
11146
|
}
|
|
11039
11147
|
function readLegacyEnvLocal(targetRepoRoot) {
|
|
11040
11148
|
try {
|
|
11041
|
-
return parseEnvText(
|
|
11149
|
+
return parseEnvText(readFileSync7(join8(targetRepoRoot, LEGACY_ENV_LOCAL_REL), "utf-8"));
|
|
11042
11150
|
} catch {
|
|
11043
11151
|
return null;
|
|
11044
11152
|
}
|
|
@@ -11090,7 +11198,7 @@ function writeEngagementCredential(credential, engagementId, home) {
|
|
|
11090
11198
|
function planEngagementCredential(credential, engagementId, home) {
|
|
11091
11199
|
let existing;
|
|
11092
11200
|
try {
|
|
11093
|
-
existing =
|
|
11201
|
+
existing = readFileSync7(engagementEnvPath(engagementId, home), "utf-8");
|
|
11094
11202
|
} catch {
|
|
11095
11203
|
existing = null;
|
|
11096
11204
|
}
|
|
@@ -11133,12 +11241,12 @@ function engagementCredentialValues(credential, engagementId) {
|
|
|
11133
11241
|
}
|
|
11134
11242
|
var LEGACY_ENV_LOCAL_REL = ".env.local";
|
|
11135
11243
|
function migrateLegacyEnvLocal(targetRepo, stored) {
|
|
11136
|
-
const path =
|
|
11137
|
-
if (!
|
|
11244
|
+
const path = join8(targetRepo, LEGACY_ENV_LOCAL_REL);
|
|
11245
|
+
if (!existsSync6(path))
|
|
11138
11246
|
return { outcome: "absent", unbacked: [], missingForAdoption: [] };
|
|
11139
11247
|
let raw;
|
|
11140
11248
|
try {
|
|
11141
|
-
raw =
|
|
11249
|
+
raw = readFileSync7(path, "utf-8");
|
|
11142
11250
|
} catch {
|
|
11143
11251
|
return { outcome: "failed", unbacked: [], missingForAdoption: [] };
|
|
11144
11252
|
}
|
|
@@ -11164,7 +11272,7 @@ function migrateLegacyEnvLocal(targetRepo, stored) {
|
|
|
11164
11272
|
return { outcome: "stripped", unbacked: [], missingForAdoption: [], keptText: kept.join("\n") };
|
|
11165
11273
|
}
|
|
11166
11274
|
function applyLegacyMigration(targetRepo, decided) {
|
|
11167
|
-
const path =
|
|
11275
|
+
const path = join8(targetRepo, LEGACY_ENV_LOCAL_REL);
|
|
11168
11276
|
try {
|
|
11169
11277
|
if (decided.outcome === "removed") {
|
|
11170
11278
|
rmSync2(path);
|
|
@@ -11190,23 +11298,23 @@ function planCodexSkills(targetRepo, previous, writeSet) {
|
|
|
11190
11298
|
const manifest = readPluginManifest();
|
|
11191
11299
|
const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
|
|
11192
11300
|
return (manifest.commands ?? []).map((cmd) => {
|
|
11193
|
-
const srcFile =
|
|
11194
|
-
if (!
|
|
11301
|
+
const srcFile = join8(BUNDLE_ROOT, ".claude-plugin", cmd.path);
|
|
11302
|
+
if (!existsSync6(srcFile)) {
|
|
11195
11303
|
throw new Error(`[halfcycle] plugin.json declares command "${cmd.name}" at "${cmd.path}", but no file exists there (${srcFile}). The manifest and commands/ must agree.`);
|
|
11196
11304
|
}
|
|
11197
11305
|
const rel = skillRel(cmd.name);
|
|
11198
|
-
const content = codexSkillContent(cmd.name,
|
|
11306
|
+
const content = codexSkillContent(cmd.name, commandFileText(readFileSync7(srcFile, "utf-8"), ownVersion()));
|
|
11199
11307
|
const recorded = recordedFiles.get(rel);
|
|
11200
|
-
const planned = decideCollisionSafe(
|
|
11308
|
+
const planned = decideCollisionSafe(join8(targetRepo, rel), targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
|
|
11201
11309
|
return { ...planned, editedCollided: planned.outcome === "collided" && recorded !== void 0 };
|
|
11202
11310
|
});
|
|
11203
11311
|
}
|
|
11204
11312
|
function planVendoredBinary(targetRepo) {
|
|
11205
|
-
return decideOwned(
|
|
11313
|
+
return decideOwned(join8(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs"), targetRepo, readFileSync7(resolveVendoredBinary(), "utf-8"));
|
|
11206
11314
|
}
|
|
11207
11315
|
function planSettings(targetRepo, previous, home, env) {
|
|
11208
|
-
const settingsPath =
|
|
11209
|
-
const preexisted =
|
|
11316
|
+
const settingsPath = join8(targetRepo, SETTINGS_REL);
|
|
11317
|
+
const preexisted = existsSync6(settingsPath);
|
|
11210
11318
|
const generated = JSON.parse(generateSettingsJson());
|
|
11211
11319
|
let existing;
|
|
11212
11320
|
try {
|
|
@@ -11230,11 +11338,11 @@ function planSettings(targetRepo, previous, home, env) {
|
|
|
11230
11338
|
return { file, preexisted, existing, generated, merged, retiredOurs };
|
|
11231
11339
|
}
|
|
11232
11340
|
function planHookScripts(targetRepo) {
|
|
11233
|
-
return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(
|
|
11341
|
+
return hookScriptsThisVersionWrites().map(([rel, content]) => decideOwnedGenerated(join8(targetRepo, rel), targetRepo, content, OWNED_GENERATED_HEADERS[rel]));
|
|
11234
11342
|
}
|
|
11235
11343
|
function planMcpRegistration(targetRepo, credential) {
|
|
11236
|
-
const mcpPath =
|
|
11237
|
-
const existing =
|
|
11344
|
+
const mcpPath = join8(targetRepo, MCP_REGISTRATION_REL);
|
|
11345
|
+
const existing = existsSync6(mcpPath) ? readFileSync7(mcpPath, "utf-8") : null;
|
|
11238
11346
|
if (credential === void 0)
|
|
11239
11347
|
return { kind: "none", existing };
|
|
11240
11348
|
if (credential === "pending")
|
|
@@ -11246,11 +11354,11 @@ function planMcpRegistration(targetRepo, credential) {
|
|
|
11246
11354
|
function putPlannedMcp(targetRepo, planned) {
|
|
11247
11355
|
if (planned.kind !== "write" || planned.outcome === "skipped")
|
|
11248
11356
|
return;
|
|
11249
|
-
writeAllowlisted(
|
|
11357
|
+
writeAllowlisted(join8(targetRepo, MCP_REGISTRATION_REL), targetRepo, planned.text, []);
|
|
11250
11358
|
}
|
|
11251
11359
|
function fileInFolderPlace(root, rel) {
|
|
11252
11360
|
for (let dir = posix2.dirname(rel); dir !== "."; dir = posix2.dirname(dir)) {
|
|
11253
|
-
const found = lstatOrNull(
|
|
11361
|
+
const found = lstatOrNull(join8(root, dir));
|
|
11254
11362
|
if (found !== null && !found.isDirectory())
|
|
11255
11363
|
return true;
|
|
11256
11364
|
}
|
|
@@ -11259,7 +11367,7 @@ function fileInFolderPlace(root, rel) {
|
|
|
11259
11367
|
function symlinkOnPath(root, rel) {
|
|
11260
11368
|
let current = root;
|
|
11261
11369
|
for (const segment of rel.split("/")) {
|
|
11262
|
-
current =
|
|
11370
|
+
current = join8(current, segment);
|
|
11263
11371
|
try {
|
|
11264
11372
|
if (lstatSync(current).isSymbolicLink())
|
|
11265
11373
|
return true;
|
|
@@ -11280,14 +11388,14 @@ function planCodexHooks(targetRepo) {
|
|
|
11280
11388
|
});
|
|
11281
11389
|
if (symlinkOnPath(targetRepo, CODEX_HOOKS_REL2))
|
|
11282
11390
|
return leave("link", null);
|
|
11283
|
-
const folder2 = lstatOrNull(
|
|
11391
|
+
const folder2 = lstatOrNull(join8(targetRepo, posix2.dirname(CODEX_HOOKS_REL2)));
|
|
11284
11392
|
if (folder2 !== null && !folder2.isDirectory())
|
|
11285
11393
|
return leave("unreadable", null);
|
|
11286
|
-
const path =
|
|
11394
|
+
const path = join8(targetRepo, CODEX_HOOKS_REL2);
|
|
11287
11395
|
let before = null;
|
|
11288
11396
|
if (lstatOrNull(path) !== null) {
|
|
11289
11397
|
try {
|
|
11290
|
-
before =
|
|
11398
|
+
before = readFileSync7(path, "utf-8");
|
|
11291
11399
|
} catch {
|
|
11292
11400
|
return leave("unreadable", null);
|
|
11293
11401
|
}
|
|
@@ -11321,13 +11429,13 @@ function planCodexHooks(targetRepo) {
|
|
|
11321
11429
|
function putPlannedCodexHooks(targetRepo, planned) {
|
|
11322
11430
|
if (planned.outcome !== "written" || planned.text === null)
|
|
11323
11431
|
return;
|
|
11324
|
-
writeAllowlisted(
|
|
11432
|
+
writeAllowlisted(join8(targetRepo, CODEX_HOOKS_REL2), targetRepo, planned.text, []);
|
|
11325
11433
|
}
|
|
11326
11434
|
function readCodexConfig(targetRepo) {
|
|
11327
|
-
const folder2 = lstatOrNull(
|
|
11435
|
+
const folder2 = lstatOrNull(join8(targetRepo, posix2.dirname(CODEX_CONFIG_REL2)));
|
|
11328
11436
|
if (folder2 !== null && !folder2.isDirectory())
|
|
11329
11437
|
return "unreadable";
|
|
11330
|
-
const path =
|
|
11438
|
+
const path = join8(targetRepo, CODEX_CONFIG_REL2);
|
|
11331
11439
|
const stat = lstatOrNull(path);
|
|
11332
11440
|
if (stat === null)
|
|
11333
11441
|
return { text: null };
|
|
@@ -11335,7 +11443,7 @@ function readCodexConfig(targetRepo) {
|
|
|
11335
11443
|
return "unreadable";
|
|
11336
11444
|
let bytes;
|
|
11337
11445
|
try {
|
|
11338
|
-
bytes =
|
|
11446
|
+
bytes = readFileSync7(path);
|
|
11339
11447
|
} catch {
|
|
11340
11448
|
return "unreadable";
|
|
11341
11449
|
}
|
|
@@ -11381,10 +11489,10 @@ function planCodexConfig(targetRepo, credential) {
|
|
|
11381
11489
|
function putPlannedCodexConfig(targetRepo, planned) {
|
|
11382
11490
|
if (planned.outcome !== "written" || planned.text === null)
|
|
11383
11491
|
return;
|
|
11384
|
-
writeAllowlisted(
|
|
11492
|
+
writeAllowlisted(join8(targetRepo, CODEX_CONFIG_REL2), targetRepo, planned.text, []);
|
|
11385
11493
|
}
|
|
11386
11494
|
function planCrewRoster(targetRepo) {
|
|
11387
|
-
return decideOwned(
|
|
11495
|
+
return decideOwned(join8(targetRepo, CREW_ROSTER_REL), targetRepo, renderCrewRoster());
|
|
11388
11496
|
}
|
|
11389
11497
|
function planCrewProfiles(targetRepo, previous, writeSet) {
|
|
11390
11498
|
const recordedFiles = new Map((previous?.files ?? []).filter((file) => writeSet.files.has(file.path)).map((file) => [file.path, file]));
|
|
@@ -11399,7 +11507,7 @@ function planCrewProfiles(targetRepo, previous, writeSet) {
|
|
|
11399
11507
|
});
|
|
11400
11508
|
}
|
|
11401
11509
|
function planInstallRecord(targetRepo, text) {
|
|
11402
|
-
return decideOwned(
|
|
11510
|
+
return decideOwned(join8(targetRepo, INSTALL_MANIFEST_REL), targetRepo, text);
|
|
11403
11511
|
}
|
|
11404
11512
|
function accountInstall(steps, actual) {
|
|
11405
11513
|
const { targetRepo, writeSet, pin } = steps;
|
|
@@ -11627,7 +11735,7 @@ function mcpHasHalfcycleEntry(text) {
|
|
|
11627
11735
|
function writeMissingSelfBuildCrewProfiles(targetRepoRoot, report) {
|
|
11628
11736
|
for (const { rel, text } of renderCrewProfiles()) {
|
|
11629
11737
|
const target = crewProfileTarget(targetRepoRoot, rel);
|
|
11630
|
-
if (
|
|
11738
|
+
if (existsSync6(target))
|
|
11631
11739
|
continue;
|
|
11632
11740
|
writeAllowlisted(target, targetRepoRoot, text, report.writtenPaths);
|
|
11633
11741
|
}
|
|
@@ -11635,7 +11743,7 @@ function writeMissingSelfBuildCrewProfiles(targetRepoRoot, report) {
|
|
|
11635
11743
|
function installSelfBuild(options) {
|
|
11636
11744
|
const { targetRepo, engagementId, credential, freshlyMinted, home } = options;
|
|
11637
11745
|
assertEngagementId(engagementId);
|
|
11638
|
-
if (!
|
|
11746
|
+
if (!existsSync6(targetRepo)) {
|
|
11639
11747
|
throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
|
|
11640
11748
|
}
|
|
11641
11749
|
const report = {
|
|
@@ -11646,7 +11754,7 @@ function installSelfBuild(options) {
|
|
|
11646
11754
|
editedCollidedPaths: [],
|
|
11647
11755
|
replacedPaths: []
|
|
11648
11756
|
};
|
|
11649
|
-
const at = (rel) =>
|
|
11757
|
+
const at = (rel) => join8(targetRepo, rel);
|
|
11650
11758
|
const version = readPluginManifest().version;
|
|
11651
11759
|
const existingMcp = readFileOrNull(at(MCP_REGISTRATION_REL));
|
|
11652
11760
|
const mcpText = existingMcp === null || !mcpHasHalfcycleEntry(existingMcp) ? inLineEndingOf(existingMcp, generateMcpRegistration(existingMcp, credential.mcpUrl)) : null;
|
|
@@ -11657,17 +11765,17 @@ function installSelfBuild(options) {
|
|
|
11657
11765
|
if (mcpText !== null) {
|
|
11658
11766
|
writeAllowlisted(at(MCP_REGISTRATION_REL), targetRepo, mcpText, report.writtenPaths);
|
|
11659
11767
|
}
|
|
11660
|
-
if (!
|
|
11661
|
-
recordOwned(report, at(VENDORED_BIN_REL), targetRepo,
|
|
11768
|
+
if (!existsSync6(at(VENDORED_BIN_REL))) {
|
|
11769
|
+
recordOwned(report, at(VENDORED_BIN_REL), targetRepo, readFileSync7(resolveVendoredBinary(), "utf-8"), VENDORED_BIN_REL);
|
|
11662
11770
|
}
|
|
11663
|
-
if (!
|
|
11771
|
+
if (!existsSync6(at(SELF_BUILD_GITIGNORE_REL))) {
|
|
11664
11772
|
writeAllowlisted(at(SELF_BUILD_GITIGNORE_REL), targetRepo, SELF_BUILD_GITIGNORE_TEXT, report.writtenPaths);
|
|
11665
11773
|
}
|
|
11666
|
-
if (!
|
|
11774
|
+
if (!existsSync6(at(CREW_ROSTER_REL))) {
|
|
11667
11775
|
writeCrewRoster(targetRepo, report);
|
|
11668
11776
|
}
|
|
11669
11777
|
writeMissingSelfBuildCrewProfiles(targetRepo, report);
|
|
11670
|
-
if (!
|
|
11778
|
+
if (!existsSync6(at(BUNDLE_PIN_REL))) {
|
|
11671
11779
|
writeBundlePin(targetRepo, planBundlePin(targetRepo, version, engagementId, "self-build", void 0, credential.methodVersion), report.writtenPaths);
|
|
11672
11780
|
}
|
|
11673
11781
|
return { writtenPaths: report.writtenPaths, credentialWritten };
|
|
@@ -11841,12 +11949,12 @@ function closeServer(server) {
|
|
|
11841
11949
|
}
|
|
11842
11950
|
|
|
11843
11951
|
// dist/sign-in-attempt.js
|
|
11844
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
11952
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
11845
11953
|
import { homedir as homedir4 } from "node:os";
|
|
11846
|
-
import { join as
|
|
11954
|
+
import { join as join9 } from "node:path";
|
|
11847
11955
|
var ATTEMPT_FILENAME = "sign-in-attempt.json";
|
|
11848
11956
|
function attemptPath(home) {
|
|
11849
|
-
return
|
|
11957
|
+
return join9(home ?? homedir4(), HALFCYCLE_DIR_NAME, ATTEMPT_FILENAME);
|
|
11850
11958
|
}
|
|
11851
11959
|
function realFolder(folder2) {
|
|
11852
11960
|
try {
|
|
@@ -11858,7 +11966,7 @@ function realFolder(folder2) {
|
|
|
11858
11966
|
function recordSignInAttempt(folder2, expiresAt, home) {
|
|
11859
11967
|
try {
|
|
11860
11968
|
const path = attemptPath(home);
|
|
11861
|
-
mkdirSync6(
|
|
11969
|
+
mkdirSync6(join9(home ?? homedir4(), HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
|
|
11862
11970
|
const record2 = { folder: realFolder(folder2), expiresAt };
|
|
11863
11971
|
writeFileSync6(path, `${JSON.stringify(record2)}
|
|
11864
11972
|
`, { mode: 384 });
|
|
@@ -11875,7 +11983,7 @@ function clearSignInAttempt(home) {
|
|
|
11875
11983
|
}
|
|
11876
11984
|
function readSignInAttempt(home) {
|
|
11877
11985
|
try {
|
|
11878
|
-
const parsed = JSON.parse(
|
|
11986
|
+
const parsed = JSON.parse(readFileSync8(attemptPath(home), "utf-8"));
|
|
11879
11987
|
if (typeof parsed !== "object" || parsed === null)
|
|
11880
11988
|
return null;
|
|
11881
11989
|
const { folder: folder2, expiresAt } = parsed;
|
|
@@ -12716,7 +12824,7 @@ function installFailureLine() {
|
|
|
12716
12824
|
if (code === 0 || verdict)
|
|
12717
12825
|
return;
|
|
12718
12826
|
try {
|
|
12719
|
-
writeSync(2, `${
|
|
12827
|
+
writeSync(2, `${doctorLine(ownVersion())}
|
|
12720
12828
|
`);
|
|
12721
12829
|
} catch {
|
|
12722
12830
|
}
|
|
@@ -13169,9 +13277,9 @@ async function joinPinnedEngagement(serviceUrl, engagementId, targetRepo, deps =
|
|
|
13169
13277
|
|
|
13170
13278
|
// dist/pre-create-checks.js
|
|
13171
13279
|
import { spawnSync } from "node:child_process";
|
|
13172
|
-
import { existsSync as
|
|
13173
|
-
import { delimiter, dirname as
|
|
13174
|
-
var KEPT_FOLDER_ID_LINE =
|
|
13280
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
13281
|
+
import { delimiter, dirname as dirname5, join as join10 } from "node:path";
|
|
13282
|
+
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.`;
|
|
13175
13283
|
var PreCreateRefused = class extends Error {
|
|
13176
13284
|
constructor(message) {
|
|
13177
13285
|
super(message);
|
|
@@ -13207,14 +13315,14 @@ function refuseSystemFolder(targetRepo, platform6 = process.platform) {
|
|
|
13207
13315
|
};
|
|
13208
13316
|
const target = key(targetRepo);
|
|
13209
13317
|
if (SYSTEM_FOLDERS.some((folder2) => key(folder2) === target)) {
|
|
13210
|
-
throw new PreCreateRefused(`${targetRepo} is a system folder \u2014 run "
|
|
13318
|
+
throw new PreCreateRefused(`${targetRepo} is a system folder \u2014 run "${ownBareCommand()}" inside a project folder.`);
|
|
13211
13319
|
}
|
|
13212
13320
|
}
|
|
13213
13321
|
function refuseInsideProject(targetRepo) {
|
|
13214
13322
|
let folder2 = realOrLiteral(targetRepo);
|
|
13215
|
-
for (let parent =
|
|
13216
|
-
if (
|
|
13217
|
-
throw new PreCreateRefused(`${targetRepo} is inside the Halfcycle project at ${parent} \u2014 open that folder in your coding agent instead, or run "
|
|
13323
|
+
for (let parent = dirname5(folder2); parent !== folder2; folder2 = parent, parent = dirname5(parent)) {
|
|
13324
|
+
if (existsSync7(join10(parent, BUNDLE_PIN_REL))) {
|
|
13325
|
+
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.`);
|
|
13218
13326
|
}
|
|
13219
13327
|
}
|
|
13220
13328
|
}
|
|
@@ -13234,16 +13342,16 @@ async function offerGitForWindows() {
|
|
|
13234
13342
|
const yes = isInteractiveRun() && /^\s*y(es)?\s*$/i.test(await askLine("Git for Windows is not installed, and Claude Code needs it to run Halfcycle. Install it now with winget? [y/N] "));
|
|
13235
13343
|
if (yes && runWingetInstall())
|
|
13236
13344
|
return true;
|
|
13237
|
-
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 "
|
|
13345
|
+
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.`);
|
|
13238
13346
|
}
|
|
13239
13347
|
function gitBashPath(env) {
|
|
13240
|
-
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" &&
|
|
13348
|
+
const onPath = (env["PATH"] ?? "").split(delimiter).filter((dir) => dir !== "" && existsSync7(join10(dir, "git.exe"))).map((dir) => join10(dir, "..", "bin", "bash.exe"));
|
|
13241
13349
|
const candidates = [
|
|
13242
13350
|
env["CLAUDE_CODE_GIT_BASH_PATH"],
|
|
13243
13351
|
...onPath,
|
|
13244
|
-
env["ProgramFiles"] === void 0 ? void 0 :
|
|
13352
|
+
env["ProgramFiles"] === void 0 ? void 0 : join10(env["ProgramFiles"], "Git", "bin", "bash.exe")
|
|
13245
13353
|
];
|
|
13246
|
-
return candidates.find((file) => file !== void 0 && file !== "" &&
|
|
13354
|
+
return candidates.find((file) => file !== void 0 && file !== "" && existsSync7(file));
|
|
13247
13355
|
}
|
|
13248
13356
|
function hasGitBash(env) {
|
|
13249
13357
|
return gitBashPath(env) !== void 0;
|
|
@@ -13252,10 +13360,10 @@ function runWingetInstall() {
|
|
|
13252
13360
|
return spawnSync("winget", [...GIT_INSTALL_ARGS], { stdio: "inherit" }).status === 0;
|
|
13253
13361
|
}
|
|
13254
13362
|
function unreadableProjectRefusal(targetRepo) {
|
|
13255
|
-
return `${
|
|
13363
|
+
return `${join10(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.`;
|
|
13256
13364
|
}
|
|
13257
13365
|
function cannotWriteRefusal(targetRepo) {
|
|
13258
|
-
return `cannot write to ${targetRepo} \u2014 run "
|
|
13366
|
+
return `cannot write to ${targetRepo} \u2014 run "${ownBareCommand()}" in a folder you can write to.`;
|
|
13259
13367
|
}
|
|
13260
13368
|
function claimFolderId(targetRepo) {
|
|
13261
13369
|
const planned = planProjectIdentity(targetRepo);
|
|
@@ -13272,7 +13380,7 @@ function claimFolderId(targetRepo) {
|
|
|
13272
13380
|
function readFolderId(targetRepo) {
|
|
13273
13381
|
let folderId;
|
|
13274
13382
|
try {
|
|
13275
|
-
const text =
|
|
13383
|
+
const text = readFileSync9(join10(targetRepo, PROJECT_IDENTITY_REL), "utf-8");
|
|
13276
13384
|
folderId = JSON.parse(text)?.projectId;
|
|
13277
13385
|
} catch {
|
|
13278
13386
|
return void 0;
|
|
@@ -13295,7 +13403,7 @@ function folderMissingRefusal(targetRepo) {
|
|
|
13295
13403
|
return `halfcycle: no such directory: ${targetRepo}`;
|
|
13296
13404
|
}
|
|
13297
13405
|
function folderIsFileRefusal(targetRepo) {
|
|
13298
|
-
return `halfcycle: ${targetRepo} is a file \u2014 run "
|
|
13406
|
+
return `halfcycle: ${targetRepo} is a file \u2014 run "${ownBareCommand()}" with a project folder instead.`;
|
|
13299
13407
|
}
|
|
13300
13408
|
function monorepoRefusal(targetRepo) {
|
|
13301
13409
|
return `halfcycle: refusing to install into the Halfcycle monorepo itself (${targetRepo}).
|
|
@@ -13305,7 +13413,7 @@ function monorepoRefusal(targetRepo) {
|
|
|
13305
13413
|
To install into a real project: halfcycle <path-to-that-repo>`;
|
|
13306
13414
|
}
|
|
13307
13415
|
function homeFolderRefusal(targetRepo) {
|
|
13308
|
-
return `halfcycle: refusing to set up in your home folder (${targetRepo}). Run "
|
|
13416
|
+
return `halfcycle: refusing to set up in your home folder (${targetRepo}). Run "${ownBareCommand()}" inside a project folder.`;
|
|
13309
13417
|
}
|
|
13310
13418
|
function isHomeFolder(dir) {
|
|
13311
13419
|
try {
|
|
@@ -13318,16 +13426,16 @@ function isHomeFolder(dir) {
|
|
|
13318
13426
|
}
|
|
13319
13427
|
|
|
13320
13428
|
// dist/preview.js
|
|
13321
|
-
import { accessSync, constants, existsSync as
|
|
13322
|
-
import { join as
|
|
13429
|
+
import { accessSync, constants, existsSync as existsSync8, readFileSync as readFileSync11, statSync } from "node:fs";
|
|
13430
|
+
import { join as join12 } from "node:path";
|
|
13323
13431
|
|
|
13324
13432
|
// dist/halfcycle-monorepo.js
|
|
13325
|
-
import { readFileSync as
|
|
13326
|
-
import { join as
|
|
13433
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
13434
|
+
import { join as join11 } from "node:path";
|
|
13327
13435
|
var MONOREPO_PACKAGE_NAME = "halfcycle-monorepo";
|
|
13328
13436
|
function isHalfcycleMonorepo(dir) {
|
|
13329
13437
|
try {
|
|
13330
|
-
const pkg = JSON.parse(
|
|
13438
|
+
const pkg = JSON.parse(readFileSync10(join11(dir, "package.json"), "utf-8"));
|
|
13331
13439
|
return pkg.name === MONOREPO_PACKAGE_NAME;
|
|
13332
13440
|
} catch {
|
|
13333
13441
|
return false;
|
|
@@ -13349,7 +13457,7 @@ var WORDS = {
|
|
|
13349
13457
|
newProject: " A new project on Halfcycle's service for this folder, made when you sign in.",
|
|
13350
13458
|
gitForWindows: " Git for Windows, which Claude Code needs: setting up will ask before installing it with winget.",
|
|
13351
13459
|
firstSession: "Your first session will then write your project's own files as you answer its questions, starting with AGENTS.md and CLAUDE.md, which hold your project's own instructions.",
|
|
13352
|
-
toSetUp: `To set up for real, run: ${
|
|
13460
|
+
toSetUp: `To set up for real, run: ${ownCommand()}`,
|
|
13353
13461
|
planFailed: "halfcycle: setting up this folder would stop on a file that could not be read. Nothing has been written; fix that file, then run the preview again."
|
|
13354
13462
|
};
|
|
13355
13463
|
function runPreview(targetRepo, options) {
|
|
@@ -13369,19 +13477,19 @@ function runPreview(targetRepo, options) {
|
|
|
13369
13477
|
};
|
|
13370
13478
|
}
|
|
13371
13479
|
function claudeMdLine(targetRepo) {
|
|
13372
|
-
const path =
|
|
13373
|
-
if (!
|
|
13480
|
+
const path = join12(targetRepo, CLAUDE_MD_REL);
|
|
13481
|
+
if (!existsSync8(path))
|
|
13374
13482
|
return void 0;
|
|
13375
13483
|
let text;
|
|
13376
13484
|
try {
|
|
13377
|
-
text =
|
|
13485
|
+
text = readFileSync11(path, "utf-8");
|
|
13378
13486
|
} catch {
|
|
13379
13487
|
return WORDS.claudeMdWillAdd;
|
|
13380
13488
|
}
|
|
13381
13489
|
return text.includes(AGENTS_IMPORT_LINE) ? WORDS.claudeMdAlreadyPoints : WORDS.claudeMdWillAdd;
|
|
13382
13490
|
}
|
|
13383
13491
|
function previewRefusal(targetRepo, options) {
|
|
13384
|
-
if (!
|
|
13492
|
+
if (!existsSync8(targetRepo))
|
|
13385
13493
|
return folderMissingRefusal(targetRepo);
|
|
13386
13494
|
if (!statSync(targetRepo).isDirectory())
|
|
13387
13495
|
return folderIsFileRefusal(targetRepo);
|
|
@@ -13402,7 +13510,7 @@ function previewRefusal(targetRepo, options) {
|
|
|
13402
13510
|
} catch {
|
|
13403
13511
|
return cannotWriteRefusal(targetRepo);
|
|
13404
13512
|
}
|
|
13405
|
-
if (
|
|
13513
|
+
if (existsSync8(join12(targetRepo, PROJECT_IDENTITY_REL)) && readFolderId(targetRepo) === void 0) {
|
|
13406
13514
|
return unreadableProjectRefusal(targetRepo);
|
|
13407
13515
|
}
|
|
13408
13516
|
try {
|
|
@@ -13460,14 +13568,16 @@ function renderPreview(plan, missingGitForWindows, claudeMdWords) {
|
|
|
13460
13568
|
|
|
13461
13569
|
// dist/local-allow.js
|
|
13462
13570
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13463
|
-
import { existsSync as
|
|
13464
|
-
import { dirname as
|
|
13571
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync12, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
13572
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
13465
13573
|
var LOCAL_SETTINGS_REL2 = ".claude/settings.local.json";
|
|
13466
13574
|
var LOCAL_ALLOW_RECORD_REL = ".halfcycle/local-allow.json";
|
|
13467
13575
|
var LOCAL_ALLOW_EDIT_MODE = "acceptEdits";
|
|
13468
13576
|
var SCRIPTS = ["test", "build", "lint", "typecheck"];
|
|
13469
13577
|
var SCRIPT_RUNNERS = ["npm run", "pnpm", "pnpm run", "yarn", "bun run"];
|
|
13578
|
+
var HALFCYCLE_TOOLS_RULE = `mcp__${MCP_SERVER_KEY}`;
|
|
13470
13579
|
var bash = (command) => `Bash(${command}:*)`;
|
|
13580
|
+
var INSTALLED_HOOK_SCRIPTS = Object.keys(OWNED_GENERATED_HEADERS).filter((path) => path.startsWith(".claude/hooks/"));
|
|
13471
13581
|
var LOCAL_ALLOW_RULES = [
|
|
13472
13582
|
...SCRIPT_RUNNERS.flatMap((runner) => SCRIPTS.map((script) => bash(`${runner} ${script}`))),
|
|
13473
13583
|
bash("npm test"),
|
|
@@ -13477,8 +13587,14 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13477
13587
|
bash("cargo test"),
|
|
13478
13588
|
bash("cargo build"),
|
|
13479
13589
|
bash("cargo check"),
|
|
13590
|
+
bash("cargo run"),
|
|
13591
|
+
bash("go run"),
|
|
13480
13592
|
bash("pytest"),
|
|
13481
13593
|
bash("python -m pytest"),
|
|
13594
|
+
bash("bun test"),
|
|
13595
|
+
bash("bun build"),
|
|
13596
|
+
bash("deno test"),
|
|
13597
|
+
bash("deno check"),
|
|
13482
13598
|
bash("make test"),
|
|
13483
13599
|
bash("make build"),
|
|
13484
13600
|
bash("git status"),
|
|
@@ -13508,11 +13624,20 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13508
13624
|
bash("go mod tidy"),
|
|
13509
13625
|
bash("go get"),
|
|
13510
13626
|
bash("cargo add"),
|
|
13627
|
+
bash("pip install"),
|
|
13628
|
+
bash("pip3 install"),
|
|
13629
|
+
bash("bundle install"),
|
|
13511
13630
|
bash("node"),
|
|
13512
13631
|
bash("npx"),
|
|
13513
13632
|
bash("pnpm exec"),
|
|
13514
13633
|
bash("bunx"),
|
|
13634
|
+
bash("python3"),
|
|
13635
|
+
bash("python"),
|
|
13636
|
+
bash("ruby"),
|
|
13637
|
+
bash("deno run"),
|
|
13638
|
+
bash("bundle exec"),
|
|
13515
13639
|
"Bash(node_modules/.bin/*)",
|
|
13640
|
+
...INSTALLED_HOOK_SCRIPTS.flatMap((path) => [bash(`./${path}`), bash(`bash ${path}`)]),
|
|
13516
13641
|
bash("mkdir"),
|
|
13517
13642
|
bash("touch"),
|
|
13518
13643
|
bash("cp"),
|
|
@@ -13532,13 +13657,14 @@ var LOCAL_ALLOW_RULES = [
|
|
|
13532
13657
|
bash("sort"),
|
|
13533
13658
|
bash("uniq"),
|
|
13534
13659
|
bash("cut"),
|
|
13535
|
-
bash("tr")
|
|
13660
|
+
bash("tr"),
|
|
13661
|
+
HALFCYCLE_TOOLS_RULE
|
|
13536
13662
|
];
|
|
13537
13663
|
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, deleting and changing anything on GitHub 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: ";
|
|
13538
|
-
var LOCAL_ALLOW_ON_LINE =
|
|
13539
|
-
var LOCAL_ALLOW_OFF_LINE =
|
|
13664
|
+
var LOCAL_ALLOW_ON_LINE = `Your coding agent can now work without asking on this computer. To undo it, run: ${ownCommand()} allow --remove`;
|
|
13665
|
+
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`;
|
|
13540
13666
|
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.";
|
|
13541
|
-
var LOCAL_ALLOW_UNREADABLE_LINE =
|
|
13667
|
+
var LOCAL_ALLOW_UNREADABLE_LINE = `Halfcycle did not change .claude/settings.local.json because it is not a JSON object it can add to. Fix the file, then run: ${ownCommand()} allow`;
|
|
13542
13668
|
var LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE = "Halfcycle has not added any rules on this computer, so there is nothing to remove.";
|
|
13543
13669
|
var LOCAL_ALLOW_REMOVED_LINE = "Halfcycle took out the rules it added. Everything you had stays.";
|
|
13544
13670
|
function isYes(answer) {
|
|
@@ -13556,11 +13682,11 @@ function trackedByGit(repo) {
|
|
|
13556
13682
|
return run.status === 0;
|
|
13557
13683
|
}
|
|
13558
13684
|
function readRecord2(repo) {
|
|
13559
|
-
const path =
|
|
13560
|
-
if (!
|
|
13685
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13686
|
+
if (!existsSync9(path))
|
|
13561
13687
|
return null;
|
|
13562
13688
|
try {
|
|
13563
|
-
const raw = JSON.parse(
|
|
13689
|
+
const raw = JSON.parse(readFileSync12(path, "utf8"));
|
|
13564
13690
|
if (!isObject2(raw) || !Array.isArray(raw["rules"]))
|
|
13565
13691
|
return null;
|
|
13566
13692
|
return {
|
|
@@ -13573,12 +13699,12 @@ function readRecord2(repo) {
|
|
|
13573
13699
|
}
|
|
13574
13700
|
}
|
|
13575
13701
|
function writeRecord(repo, record2) {
|
|
13576
|
-
const path =
|
|
13577
|
-
mkdirSync7(
|
|
13702
|
+
const path = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13703
|
+
mkdirSync7(dirname6(path), { recursive: true });
|
|
13578
13704
|
writeFileSync7(path, JSON.stringify({ rules: record2.rules, defaultMode: record2.defaultMode, before: record2.before }, null, 2) + "\n");
|
|
13579
13705
|
}
|
|
13580
13706
|
function holdsLocalAllowRecord(repo) {
|
|
13581
|
-
return
|
|
13707
|
+
return existsSync9(join13(repo, LOCAL_ALLOW_RECORD_REL));
|
|
13582
13708
|
}
|
|
13583
13709
|
function layoutOf(text) {
|
|
13584
13710
|
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -13593,9 +13719,9 @@ function render(value, like) {
|
|
|
13593
13719
|
function writeLocalAllow(repo) {
|
|
13594
13720
|
if (trackedByGit(repo))
|
|
13595
13721
|
return { kind: "tracked", line: LOCAL_ALLOW_TRACKED_LINE };
|
|
13596
|
-
const path =
|
|
13597
|
-
const existed =
|
|
13598
|
-
const text = existed ?
|
|
13722
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13723
|
+
const existed = existsSync9(path);
|
|
13724
|
+
const text = existed ? readFileSync12(path, "utf8") : null;
|
|
13599
13725
|
let settings = {};
|
|
13600
13726
|
if (text !== null) {
|
|
13601
13727
|
try {
|
|
@@ -13625,7 +13751,7 @@ function writeLocalAllow(repo) {
|
|
|
13625
13751
|
if (setsMode)
|
|
13626
13752
|
permissions["defaultMode"] = LOCAL_ALLOW_EDIT_MODE;
|
|
13627
13753
|
const next = { ...settings, permissions };
|
|
13628
|
-
mkdirSync7(
|
|
13754
|
+
mkdirSync7(dirname6(path), { recursive: true });
|
|
13629
13755
|
writeFileSync7(path, render(next, text));
|
|
13630
13756
|
const earlier = readRecord2(repo);
|
|
13631
13757
|
writeRecord(repo, {
|
|
@@ -13637,15 +13763,15 @@ function writeLocalAllow(repo) {
|
|
|
13637
13763
|
}
|
|
13638
13764
|
function removeLocalAllow(repo) {
|
|
13639
13765
|
const record2 = readRecord2(repo);
|
|
13640
|
-
const recordPath =
|
|
13766
|
+
const recordPath = join13(repo, LOCAL_ALLOW_RECORD_REL);
|
|
13641
13767
|
if (record2 === null) {
|
|
13642
|
-
if (
|
|
13768
|
+
if (existsSync9(recordPath))
|
|
13643
13769
|
rmSync4(recordPath, { force: true });
|
|
13644
13770
|
return { kind: "nothing", line: LOCAL_ALLOW_NOTHING_TO_REMOVE_LINE };
|
|
13645
13771
|
}
|
|
13646
|
-
const path =
|
|
13647
|
-
if (
|
|
13648
|
-
const text =
|
|
13772
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13773
|
+
if (existsSync9(path)) {
|
|
13774
|
+
const text = readFileSync12(path, "utf8");
|
|
13649
13775
|
let parsed = null;
|
|
13650
13776
|
try {
|
|
13651
13777
|
parsed = JSON.parse(text);
|
|
@@ -13712,10 +13838,10 @@ function canonical(value) {
|
|
|
13712
13838
|
function shouldAskLocalAllow(repo, interactive) {
|
|
13713
13839
|
if (!interactive || holdsLocalAllowRecord(repo))
|
|
13714
13840
|
return false;
|
|
13715
|
-
const path =
|
|
13716
|
-
if (!
|
|
13841
|
+
const path = join13(repo, LOCAL_SETTINGS_REL2);
|
|
13842
|
+
if (!existsSync9(path))
|
|
13717
13843
|
return true;
|
|
13718
|
-
const parsed = parseObject(
|
|
13844
|
+
const parsed = parseObject(readFileSync12(path, "utf8"));
|
|
13719
13845
|
const allow = isObject2(parsed?.["permissions"]) ? parsed["permissions"]["allow"] : void 0;
|
|
13720
13846
|
if (!Array.isArray(allow))
|
|
13721
13847
|
return true;
|
|
@@ -13734,9 +13860,9 @@ function localAllowClosingLine(repo, said) {
|
|
|
13734
13860
|
|
|
13735
13861
|
// dist/doctor.js
|
|
13736
13862
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
13737
|
-
import { existsSync as
|
|
13863
|
+
import { existsSync as existsSync10, lstatSync as lstatSync2, readFileSync as readFileSync13, realpathSync as realpathSync4 } from "node:fs";
|
|
13738
13864
|
import { homedir as homedir6 } from "node:os";
|
|
13739
|
-
import { join as
|
|
13865
|
+
import { join as join14 } from "node:path";
|
|
13740
13866
|
var GUARD_RUNNER_REL2 = ".claude/hooks/guard-runner.sh";
|
|
13741
13867
|
var CONNECTION_VERB = "check-connection";
|
|
13742
13868
|
var WRAPPER_TIMEOUT_MS = 3e4;
|
|
@@ -13775,13 +13901,13 @@ function runDoctor(root, options) {
|
|
|
13775
13901
|
};
|
|
13776
13902
|
}
|
|
13777
13903
|
function setUpLine(root, upToDate) {
|
|
13778
|
-
if (!
|
|
13904
|
+
if (!existsSync10(join14(root, BUNDLE_PIN_REL))) {
|
|
13779
13905
|
return {
|
|
13780
13906
|
state: "no",
|
|
13781
13907
|
hooksMissing: false,
|
|
13782
13908
|
line: {
|
|
13783
13909
|
ok: false,
|
|
13784
|
-
text: `Set up: no. Halfcycle is not set up in this folder. To set it up, run ${
|
|
13910
|
+
text: `Set up: no. Halfcycle is not set up in this folder. To set it up, run ${ownCommand()} here.`
|
|
13785
13911
|
}
|
|
13786
13912
|
};
|
|
13787
13913
|
}
|
|
@@ -13789,9 +13915,9 @@ function setUpLine(root, upToDate) {
|
|
|
13789
13915
|
const missing = [];
|
|
13790
13916
|
if (!pinNamesProject(root))
|
|
13791
13917
|
missing.push("Halfcycle's project record");
|
|
13792
|
-
if (!
|
|
13918
|
+
if (!existsSync10(join14(root, INSTALL_MANIFEST_REL)))
|
|
13793
13919
|
missing.push("the install record");
|
|
13794
|
-
if (!
|
|
13920
|
+
if (!existsSync10(join14(root, VENDORED_BIN_REL)))
|
|
13795
13921
|
missing.push("Halfcycle's check program");
|
|
13796
13922
|
if (!mcpNamesServer(root))
|
|
13797
13923
|
missing.push("Halfcycle's server entry");
|
|
@@ -13804,7 +13930,7 @@ function setUpLine(root, upToDate) {
|
|
|
13804
13930
|
hooksMissing,
|
|
13805
13931
|
line: {
|
|
13806
13932
|
ok: false,
|
|
13807
|
-
text: `Set up: not complete. Missing: ${naturalList(missing)}. To put it back, run ${
|
|
13933
|
+
text: `Set up: not complete. Missing: ${naturalList(missing)}. To put it back, run ${ownCommand()} here; nothing of yours is changed.`
|
|
13808
13934
|
}
|
|
13809
13935
|
};
|
|
13810
13936
|
}
|
|
@@ -13817,14 +13943,14 @@ function pinNamesProject(root) {
|
|
|
13817
13943
|
}
|
|
13818
13944
|
}
|
|
13819
13945
|
function mcpNamesServer(root) {
|
|
13820
|
-
const parsed = readJson(
|
|
13946
|
+
const parsed = readJson(join14(root, MCP_REGISTRATION_REL));
|
|
13821
13947
|
if (!isObject3(parsed) || !isObject3(parsed["mcpServers"]))
|
|
13822
13948
|
return false;
|
|
13823
13949
|
const entry = parsed["mcpServers"][MCP_SERVER_KEY];
|
|
13824
13950
|
return isObject3(entry) && typeof entry["url"] === "string";
|
|
13825
13951
|
}
|
|
13826
13952
|
function settingsRegisterEveryCommand(root) {
|
|
13827
|
-
const parsed = readJson(
|
|
13953
|
+
const parsed = readJson(join14(root, SETTINGS_REL));
|
|
13828
13954
|
if (!isObject3(parsed) || !isObject3(parsed["hooks"]))
|
|
13829
13955
|
return false;
|
|
13830
13956
|
const registered = /* @__PURE__ */ new Set();
|
|
@@ -13855,8 +13981,8 @@ function startWrapper(root, options) {
|
|
|
13855
13981
|
}
|
|
13856
13982
|
shell = gitBash;
|
|
13857
13983
|
}
|
|
13858
|
-
const wrapper =
|
|
13859
|
-
if (!
|
|
13984
|
+
const wrapper = join14(root, GUARD_RUNNER_REL2);
|
|
13985
|
+
if (!existsSync10(wrapper))
|
|
13860
13986
|
return failed(repairSentence());
|
|
13861
13987
|
const run = spawnSync3(shell, [wrapper, CONNECTION_VERB], {
|
|
13862
13988
|
cwd: root,
|
|
@@ -13883,7 +14009,7 @@ function startWrapper(root, options) {
|
|
|
13883
14009
|
}
|
|
13884
14010
|
function settingsDisablingHooks(root) {
|
|
13885
14011
|
for (const rel of [SETTINGS_REL, LOCAL_SETTINGS_REL]) {
|
|
13886
|
-
const parsed = readJson(
|
|
14012
|
+
const parsed = readJson(join14(root, rel));
|
|
13887
14013
|
if (isObject3(parsed) && parsed["disableAllHooks"] === true)
|
|
13888
14014
|
return rel;
|
|
13889
14015
|
}
|
|
@@ -13913,7 +14039,7 @@ function parseAnswer(stdout) {
|
|
|
13913
14039
|
function usageOf(said) {
|
|
13914
14040
|
if (!Array.isArray(said))
|
|
13915
14041
|
return [];
|
|
13916
|
-
const
|
|
14042
|
+
const usage5 = [];
|
|
13917
14043
|
for (const figure of said) {
|
|
13918
14044
|
if (!isObject3(figure))
|
|
13919
14045
|
continue;
|
|
@@ -13926,9 +14052,9 @@ function usageOf(said) {
|
|
|
13926
14052
|
continue;
|
|
13927
14053
|
if (typeof resetsAt !== "string" || Number.isNaN(Date.parse(resetsAt)))
|
|
13928
14054
|
continue;
|
|
13929
|
-
|
|
14055
|
+
usage5.push({ agent, window, usedPercentage, resetsAt: new Date(resetsAt) });
|
|
13930
14056
|
}
|
|
13931
|
-
return
|
|
14057
|
+
return usage5;
|
|
13932
14058
|
}
|
|
13933
14059
|
function checksRunningLine(running, hooksMissing) {
|
|
13934
14060
|
if (running.state === "not-run")
|
|
@@ -13945,7 +14071,7 @@ function failed(text) {
|
|
|
13945
14071
|
return { state: "failed", line: { ok: false, text } };
|
|
13946
14072
|
}
|
|
13947
14073
|
function repairSentence() {
|
|
13948
|
-
return `Checks running: no. Halfcycle's check could not start. To repair it, run ${
|
|
14074
|
+
return `Checks running: no. Halfcycle's check could not start. To repair it, run ${ownCommand()} here.`;
|
|
13949
14075
|
}
|
|
13950
14076
|
function connectionLines(running) {
|
|
13951
14077
|
if (running.state !== "answered") {
|
|
@@ -13973,8 +14099,8 @@ function folderTrustLine(root, options) {
|
|
|
13973
14099
|
if (!mcpNamesServer(root))
|
|
13974
14100
|
return void 0;
|
|
13975
14101
|
const configFolder = options.env["CLAUDE_CONFIG_DIR"];
|
|
13976
|
-
const stateFile =
|
|
13977
|
-
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync2, readFileSync:
|
|
14102
|
+
const stateFile = join14(configFolder !== void 0 && configFolder !== "" ? configFolder : options.home ?? homedir6(), CLAUDE_STATE_FILENAME);
|
|
14103
|
+
const trust = trustOfWorkingFolder(root, stateFile, options.platform, { lstatSync: lstatSync2, readFileSync: readFileSync13, realpathSync: realpathSync4 });
|
|
13978
14104
|
return trust === "not-trusted" ? { ok: true, text: `Folder trust: ${FOLDER_NOT_TRUSTED_SENTENCE}` } : void 0;
|
|
13979
14105
|
}
|
|
13980
14106
|
function signInCodeLine(root, running, now, options) {
|
|
@@ -13984,7 +14110,7 @@ function signInCodeLine(root, running, now, options) {
|
|
|
13984
14110
|
return void 0;
|
|
13985
14111
|
return {
|
|
13986
14112
|
ok: true,
|
|
13987
|
-
text: `Sign-in code: it ran out before it was confirmed. To get a new one, run ${
|
|
14113
|
+
text: `Sign-in code: it ran out before it was confirmed. To get a new one, run ${ownCommand()} here.`
|
|
13988
14114
|
};
|
|
13989
14115
|
}
|
|
13990
14116
|
var AGENT_NAMES = { "claude-code": "Claude Code", codex: "Codex" };
|
|
@@ -14033,9 +14159,9 @@ function installedVersion(root) {
|
|
|
14033
14159
|
}
|
|
14034
14160
|
function versionNoLine(installed, current) {
|
|
14035
14161
|
if (installed === null) {
|
|
14036
|
-
return `Up to date: no. This folder has no Halfcycle version recorded; the newest is ${current}. To set it up, run ${
|
|
14162
|
+
return `Up to date: no. This folder has no Halfcycle version recorded; the newest is ${current}. To set it up, run ${ownCommand()} here.`;
|
|
14037
14163
|
}
|
|
14038
|
-
return `Up to date: no. This folder has Halfcycle ${installed}; the newest is ${current}. To update, run ${
|
|
14164
|
+
return `Up to date: no. This folder has Halfcycle ${installed}; the newest is ${current}. To update, run ${ownCommand()} here.`;
|
|
14039
14165
|
}
|
|
14040
14166
|
var CODEX_EVENT_SNAKE = {
|
|
14041
14167
|
SessionStart: "session_start",
|
|
@@ -14047,12 +14173,12 @@ var CODEX_EVENT_SNAKE = {
|
|
|
14047
14173
|
SessionEnd: "session_end"
|
|
14048
14174
|
};
|
|
14049
14175
|
function codexLine(root, options) {
|
|
14050
|
-
const hooksPath =
|
|
14051
|
-
if (!
|
|
14176
|
+
const hooksPath = join14(root, CODEX_HOOKS_REL2);
|
|
14177
|
+
if (!existsSync10(hooksPath))
|
|
14052
14178
|
return void 0;
|
|
14053
14179
|
const positions = codexEntryPositions(hooksPath);
|
|
14054
14180
|
if (positions === void 0) {
|
|
14055
|
-
return { ok: false, text: `Codex: not complete. To repair it, run ${
|
|
14181
|
+
return { ok: false, text: `Codex: not complete. To repair it, run ${ownCommand()} here.` };
|
|
14056
14182
|
}
|
|
14057
14183
|
return codexApprovalStatus(root, positions, options) === "approved" ? { ok: true, text: "Codex: set up, and its checks are approved." } : {
|
|
14058
14184
|
ok: false,
|
|
@@ -14089,16 +14215,16 @@ function codexEntryPositions(hooksPath) {
|
|
|
14089
14215
|
}
|
|
14090
14216
|
function codexApprovalStatus(root, positions, options) {
|
|
14091
14217
|
const codexHome = options.env["CODEX_HOME"];
|
|
14092
|
-
const configPath =
|
|
14218
|
+
const configPath = join14(codexHome !== void 0 && codexHome !== "" ? codexHome : join14(homedir6(), ".codex"), "config.toml");
|
|
14093
14219
|
let text;
|
|
14094
14220
|
try {
|
|
14095
|
-
text =
|
|
14221
|
+
text = readFileSync13(configPath, "utf-8");
|
|
14096
14222
|
} catch {
|
|
14097
14223
|
return "not-approved";
|
|
14098
14224
|
}
|
|
14099
14225
|
let hooksPath;
|
|
14100
14226
|
try {
|
|
14101
|
-
hooksPath =
|
|
14227
|
+
hooksPath = join14(realpathSync4(root), CODEX_HOOKS_REL2);
|
|
14102
14228
|
} catch {
|
|
14103
14229
|
return "not-approved";
|
|
14104
14230
|
}
|
|
@@ -14128,10 +14254,10 @@ function naturalList(items) {
|
|
|
14128
14254
|
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
14129
14255
|
}
|
|
14130
14256
|
function readJson(path) {
|
|
14131
|
-
if (!
|
|
14257
|
+
if (!existsSync10(path))
|
|
14132
14258
|
return void 0;
|
|
14133
14259
|
try {
|
|
14134
|
-
return JSON.parse(
|
|
14260
|
+
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
14135
14261
|
} catch {
|
|
14136
14262
|
return void 0;
|
|
14137
14263
|
}
|
|
@@ -14142,8 +14268,8 @@ function isObject3(value) {
|
|
|
14142
14268
|
|
|
14143
14269
|
// dist/claude-version-probe.js
|
|
14144
14270
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
14145
|
-
import { existsSync as
|
|
14146
|
-
import { delimiter as delimiter2, join as
|
|
14271
|
+
import { existsSync as existsSync11, realpathSync as realpathSync5 } from "node:fs";
|
|
14272
|
+
import { delimiter as delimiter2, join as join15 } from "node:path";
|
|
14147
14273
|
function execVersionCommand(command, timeoutMs, deps) {
|
|
14148
14274
|
const platform6 = deps.platform ?? process.platform;
|
|
14149
14275
|
const exec = deps.exec ?? execFileSync3;
|
|
@@ -14186,7 +14312,7 @@ var INSTALL_METHOD_RULES = [
|
|
|
14186
14312
|
function probeClaudeCodeInstallMethod(deps = {}) {
|
|
14187
14313
|
const env = deps.env ?? process.env;
|
|
14188
14314
|
const platform6 = deps.platform ?? process.platform;
|
|
14189
|
-
const exists = deps.exists ??
|
|
14315
|
+
const exists = deps.exists ?? existsSync11;
|
|
14190
14316
|
const realpath = deps.realpath ?? ((file) => {
|
|
14191
14317
|
try {
|
|
14192
14318
|
return realpathSync5(file);
|
|
@@ -14198,7 +14324,7 @@ function probeClaudeCodeInstallMethod(deps = {}) {
|
|
|
14198
14324
|
const dirs = (env["PATH"] ?? "").split(delimiter2).filter((dir) => dir !== "");
|
|
14199
14325
|
for (const dir of dirs) {
|
|
14200
14326
|
for (const name of names) {
|
|
14201
|
-
const file =
|
|
14327
|
+
const file = join15(dir, name);
|
|
14202
14328
|
if (!exists(file))
|
|
14203
14329
|
continue;
|
|
14204
14330
|
const real = realpath(file);
|
|
@@ -14233,8 +14359,8 @@ function codexInstallLine(codex) {
|
|
|
14233
14359
|
}
|
|
14234
14360
|
|
|
14235
14361
|
// dist/build-record/write.js
|
|
14236
|
-
import { existsSync as
|
|
14237
|
-
import { basename as basename2, join as
|
|
14362
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, rmSync as rmSync6 } from "node:fs";
|
|
14363
|
+
import { basename as basename2, join as join16, resolve as resolve3, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
|
|
14238
14364
|
|
|
14239
14365
|
// dist/build-record/render-v2.js
|
|
14240
14366
|
var HIDDEN = /[\p{Cc}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/u;
|
|
@@ -14825,7 +14951,7 @@ function renderBuildRecordV2Markdown(record2) {
|
|
|
14825
14951
|
}
|
|
14826
14952
|
|
|
14827
14953
|
// dist/build-record/pair-io.js
|
|
14828
|
-
import { closeSync, openSync, readFileSync as
|
|
14954
|
+
import { closeSync, openSync, readFileSync as readFileSync14, renameSync as renameSync2, rmSync as rmSync5, writeSync as writeSync2 } from "node:fs";
|
|
14829
14955
|
function createExclusiveOnDisk(path, data) {
|
|
14830
14956
|
const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
14831
14957
|
const fd = openSync(path, "wx");
|
|
@@ -14856,7 +14982,7 @@ function isEexist(err) {
|
|
|
14856
14982
|
}
|
|
14857
14983
|
function readIfPresent(path) {
|
|
14858
14984
|
try {
|
|
14859
|
-
return
|
|
14985
|
+
return readFileSync14(path, "utf-8");
|
|
14860
14986
|
} catch {
|
|
14861
14987
|
return null;
|
|
14862
14988
|
}
|
|
@@ -14888,7 +15014,7 @@ function replaceOne(io, tmp, finalPath, text) {
|
|
|
14888
15014
|
}
|
|
14889
15015
|
|
|
14890
15016
|
// dist/build-record/write.js
|
|
14891
|
-
var BUILD_RECORD_DIR =
|
|
15017
|
+
var BUILD_RECORD_DIR = join16("docs", "build-records");
|
|
14892
15018
|
function assertInsideOutDir(outDir, candidate, identity) {
|
|
14893
15019
|
const rel = relative2(outDir, resolve3(candidate));
|
|
14894
15020
|
const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute3(rel) && rel === basename2(rel);
|
|
@@ -14926,8 +15052,8 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
14926
15052
|
}
|
|
14927
15053
|
const outDir = resolve3(repoRoot, BUILD_RECORD_DIR);
|
|
14928
15054
|
const segment = renderPhaseSegment(phase);
|
|
14929
|
-
const jsonPath =
|
|
14930
|
-
const mdPath =
|
|
15055
|
+
const jsonPath = join16(outDir, `${segment}.json`);
|
|
15056
|
+
const mdPath = join16(outDir, `${segment}.md`);
|
|
14931
15057
|
const jsonTmp = `${jsonPath}.tmp`;
|
|
14932
15058
|
const mdTmp = `${mdPath}.tmp`;
|
|
14933
15059
|
for (const candidate of [jsonPath, mdPath, jsonTmp, mdTmp]) {
|
|
@@ -14941,7 +15067,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
14941
15067
|
}
|
|
14942
15068
|
const existingJson = readIfPresent(jsonPath);
|
|
14943
15069
|
if (existingJson === null) {
|
|
14944
|
-
return { kind: "in-the-way", path:
|
|
15070
|
+
return { kind: "in-the-way", path: existsSync12(jsonPath) ? jsonPath : mdPath };
|
|
14945
15071
|
}
|
|
14946
15072
|
if (existingJson === jsonText) {
|
|
14947
15073
|
replaceOne(io, mdTmp, mdPath, mdText);
|
|
@@ -14968,21 +15094,21 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
|
|
|
14968
15094
|
import { relative as relative5 } from "node:path";
|
|
14969
15095
|
|
|
14970
15096
|
// dist/open-phase.js
|
|
14971
|
-
import { readFileSync as
|
|
14972
|
-
import { join as
|
|
15097
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
15098
|
+
import { join as join24 } from "node:path";
|
|
14973
15099
|
|
|
14974
15100
|
// dist/close-phase.js
|
|
14975
15101
|
import { execFile, execFileSync as execFileSync5 } from "node:child_process";
|
|
14976
|
-
import { readFileSync as
|
|
14977
|
-
import { join as
|
|
15102
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
15103
|
+
import { join as join23 } from "node:path";
|
|
14978
15104
|
|
|
14979
15105
|
// dist/plan.js
|
|
14980
|
-
import { existsSync as
|
|
14981
|
-
import { join as
|
|
15106
|
+
import { existsSync as existsSync14, readFileSync as readFileSync18 } from "node:fs";
|
|
15107
|
+
import { join as join22 } from "node:path";
|
|
14982
15108
|
|
|
14983
15109
|
// dist/draw/write.js
|
|
14984
|
-
import { closeSync as closeSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as
|
|
14985
|
-
import { dirname as
|
|
15110
|
+
import { closeSync as closeSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync9, openSync as openSync2, readFileSync as readFileSync15, readSync, realpathSync as realpathSync6, rmSync as rmSync7 } from "node:fs";
|
|
15111
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join17, relative as relative3, resolve as resolve4, sep } from "node:path";
|
|
14986
15112
|
var DOCS_DIR = "docs";
|
|
14987
15113
|
var DIAGRAMS_DIR = "diagrams";
|
|
14988
15114
|
var DIAGRAM_DIR_REL = `${DOCS_DIR}/${DIAGRAMS_DIR}`;
|
|
@@ -15038,9 +15164,9 @@ function embedImageLink(base, doc) {
|
|
|
15038
15164
|
}
|
|
15039
15165
|
function diagramPaths(repoRoot, base) {
|
|
15040
15166
|
const root = resolve4(repoRoot);
|
|
15041
|
-
const dir =
|
|
15167
|
+
const dir = join17(root, DOCS_DIR, DIAGRAMS_DIR);
|
|
15042
15168
|
const names = fileNames(base);
|
|
15043
|
-
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which,
|
|
15169
|
+
const file = Object.fromEntries(DIAGRAM_FILES.map((which) => [which, join17(dir, names[which])]));
|
|
15044
15170
|
return { repoRoot: root, dir, file };
|
|
15045
15171
|
}
|
|
15046
15172
|
function diagramRel(base, which) {
|
|
@@ -15075,7 +15201,7 @@ function checkDiagramPlace(paths) {
|
|
|
15075
15201
|
}
|
|
15076
15202
|
let ancestor = paths.dir;
|
|
15077
15203
|
while (lstatOrNull2(ancestor) === null) {
|
|
15078
|
-
const parent =
|
|
15204
|
+
const parent = dirname7(ancestor);
|
|
15079
15205
|
if (parent === ancestor)
|
|
15080
15206
|
break;
|
|
15081
15207
|
ancestor = parent;
|
|
@@ -15135,7 +15261,7 @@ function readExistingSet(paths) {
|
|
|
15135
15261
|
if (!stat.isFile())
|
|
15136
15262
|
return { ok: false, inTheWay: which, temp: false };
|
|
15137
15263
|
try {
|
|
15138
|
-
found[which] =
|
|
15264
|
+
found[which] = readFileSync15(paths.file[which]);
|
|
15139
15265
|
} catch {
|
|
15140
15266
|
return { ok: false, inTheWay: which, temp: false };
|
|
15141
15267
|
}
|
|
@@ -15313,10 +15439,10 @@ function isMissingRoute(method, status, body) {
|
|
|
15313
15439
|
}
|
|
15314
15440
|
|
|
15315
15441
|
// dist/change.js
|
|
15316
|
-
import { join as
|
|
15442
|
+
import { join as join20 } from "node:path";
|
|
15317
15443
|
|
|
15318
15444
|
// dist/cli-contract.js
|
|
15319
|
-
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"];
|
|
15445
|
+
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"];
|
|
15320
15446
|
var CONTRACTS = [
|
|
15321
15447
|
{ verb: "install", required: [], conditional: [] },
|
|
15322
15448
|
{ verb: "check-drift", required: [], conditional: [] },
|
|
@@ -15449,11 +15575,6 @@ var CONTRACTS = [
|
|
|
15449
15575
|
flag: "--description",
|
|
15450
15576
|
takesValue: true,
|
|
15451
15577
|
missingMessage: "halfcycle size sort: --description is required \u2014 one line, in your own words, saying what you want to build."
|
|
15452
|
-
},
|
|
15453
|
-
{
|
|
15454
|
-
flag: "--estimate",
|
|
15455
|
-
takesValue: true,
|
|
15456
|
-
missingMessage: "halfcycle size sort: --estimate is required \u2014 how long you expect it to take: hours, days or weeks."
|
|
15457
15578
|
}
|
|
15458
15579
|
],
|
|
15459
15580
|
conditional: []
|
|
@@ -15525,7 +15646,7 @@ function requirementMessage(verb, flag) {
|
|
|
15525
15646
|
function runnableInvocation(verb, phase) {
|
|
15526
15647
|
const contract = contractFor(verb);
|
|
15527
15648
|
const flags = (contract?.required ?? []).map((req) => `${req.flag} ${PLACEHOLDERS[req.flag] ?? '"\u2026"'}`).join(" ");
|
|
15528
|
-
return `${
|
|
15649
|
+
return `${ownCommand()} ${verb} ${quoteForShell(phase)}${flags ? ` ${flags}` : ""}`;
|
|
15529
15650
|
}
|
|
15530
15651
|
var PLACEHOLDERS = {
|
|
15531
15652
|
"--verdict": "<clean|defects>",
|
|
@@ -15562,12 +15683,12 @@ function placeholderRefusal(verb, tokens) {
|
|
|
15562
15683
|
|
|
15563
15684
|
// dist/system-report.js
|
|
15564
15685
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
15565
|
-
import { readFileSync as
|
|
15566
|
-
import { join as
|
|
15686
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
15687
|
+
import { join as join18 } from "node:path";
|
|
15567
15688
|
function readDescriptionFile(repoRoot) {
|
|
15568
15689
|
let text;
|
|
15569
15690
|
try {
|
|
15570
|
-
text =
|
|
15691
|
+
text = readFileSync16(join18(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8");
|
|
15571
15692
|
} catch (err) {
|
|
15572
15693
|
const code = err.code;
|
|
15573
15694
|
return code === "ENOENT" || code === "ENOTDIR" ? { kind: "missing" } : { kind: "unreadable" };
|
|
@@ -15702,7 +15823,7 @@ function readCredentialOrLine(repoRoot, deps, verb) {
|
|
|
15702
15823
|
} catch {
|
|
15703
15824
|
return {
|
|
15704
15825
|
ok: false,
|
|
15705
|
-
line: `halfcycle ${verb}: this project is not connected to Halfcycle on this machine. Run ${
|
|
15826
|
+
line: `halfcycle ${verb}: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
|
|
15706
15827
|
`
|
|
15707
15828
|
};
|
|
15708
15829
|
}
|
|
@@ -15791,8 +15912,8 @@ async function runSystem(repoRoot, deps) {
|
|
|
15791
15912
|
}
|
|
15792
15913
|
|
|
15793
15914
|
// dist/build-record/write-change.js
|
|
15794
|
-
import { existsSync as
|
|
15795
|
-
import { basename as basename3, isAbsolute as isAbsolute5, join as
|
|
15915
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10 } from "node:fs";
|
|
15916
|
+
import { basename as basename3, isAbsolute as isAbsolute5, join as join19, relative as relative4, resolve as resolve5 } from "node:path";
|
|
15796
15917
|
|
|
15797
15918
|
// dist/build-record/render-change.js
|
|
15798
15919
|
function counted2(n, one, many) {
|
|
@@ -15937,7 +16058,7 @@ function renderChangeRecordMarkdown(record2) {
|
|
|
15937
16058
|
}
|
|
15938
16059
|
|
|
15939
16060
|
// dist/build-record/write-change.js
|
|
15940
|
-
var CHANGE_RECORD_DIR =
|
|
16061
|
+
var CHANGE_RECORD_DIR = join19("docs", "build-records", "changes");
|
|
15941
16062
|
var InvalidChangeIdError = class extends Error {
|
|
15942
16063
|
id;
|
|
15943
16064
|
constructor(id) {
|
|
@@ -15957,8 +16078,8 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
15957
16078
|
if (!checked.success)
|
|
15958
16079
|
throw new InvalidChangeIdError(sortingId);
|
|
15959
16080
|
const outDir = resolve5(repoRoot, CHANGE_RECORD_DIR);
|
|
15960
|
-
const jsonPath =
|
|
15961
|
-
const mdPath =
|
|
16081
|
+
const jsonPath = join19(outDir, `${checked.data}.json`);
|
|
16082
|
+
const mdPath = join19(outDir, `${checked.data}.md`);
|
|
15962
16083
|
const mdTmp = `${mdPath}.tmp`;
|
|
15963
16084
|
for (const candidate of [jsonPath, mdPath, mdTmp]) {
|
|
15964
16085
|
assertInsideChangeDir(outDir, candidate, sortingId);
|
|
@@ -15971,7 +16092,7 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
15971
16092
|
}
|
|
15972
16093
|
const existingJson = readIfPresent(jsonPath);
|
|
15973
16094
|
if (existingJson === null) {
|
|
15974
|
-
return { kind: "in-the-way", path:
|
|
16095
|
+
return { kind: "in-the-way", path: existsSync13(jsonPath) ? jsonPath : mdPath };
|
|
15975
16096
|
}
|
|
15976
16097
|
if (existingJson !== jsonText)
|
|
15977
16098
|
return { kind: "in-the-way", path: jsonPath };
|
|
@@ -15980,9 +16101,9 @@ function writeChangeRecordPair(repoRoot, sortingId, rawRecord, record2, io = NOD
|
|
|
15980
16101
|
}
|
|
15981
16102
|
|
|
15982
16103
|
// dist/change.js
|
|
15983
|
-
var CHANGE_USAGE = `${
|
|
15984
|
-
${
|
|
15985
|
-
${
|
|
16104
|
+
var CHANGE_USAGE = `${ownCommand()} change sort --request "<one line>" --touches <path>\u2026 [--contract <from>:<to>]\u2026 [--stored-data] [--standing-rule] [--new-part] [--repo <root>]
|
|
16105
|
+
${ownCommand()} change up <mini|full> (--change <id> | --phase <phase>) [--repo <root>]
|
|
16106
|
+
${ownCommand()} change done <id> [--repo <root>]`;
|
|
15986
16107
|
function usage(line) {
|
|
15987
16108
|
return { ok: false, line: `${line}
|
|
15988
16109
|
Usage:
|
|
@@ -16179,7 +16300,7 @@ ${nextSayLine(sorted.next.say)}
|
|
|
16179
16300
|
`;
|
|
16180
16301
|
}
|
|
16181
16302
|
function changeDoneCommand(sortingId) {
|
|
16182
|
-
return `${
|
|
16303
|
+
return `${ownCommand()} change done ${quoteForShell(sortingId)}`;
|
|
16183
16304
|
}
|
|
16184
16305
|
function recordOutcomeLines(record2) {
|
|
16185
16306
|
switch (record2.outcome) {
|
|
@@ -16234,7 +16355,7 @@ function writePairLines(deps, repoRoot, sortingId, found) {
|
|
|
16234
16355
|
};
|
|
16235
16356
|
}
|
|
16236
16357
|
return {
|
|
16237
|
-
stdout: `[halfcycle] Its record is in ${
|
|
16358
|
+
stdout: `[halfcycle] Its record is in ${join20(CHANGE_RECORD_DIR, `${sortingId}.md`)}.
|
|
16238
16359
|
`,
|
|
16239
16360
|
stderr: "",
|
|
16240
16361
|
ok: true
|
|
@@ -16448,7 +16569,7 @@ async function runChangeDone(repoRoot, sortingId, deps) {
|
|
|
16448
16569
|
case "not-configured":
|
|
16449
16570
|
return {
|
|
16450
16571
|
stdout: "",
|
|
16451
|
-
stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${
|
|
16572
|
+
stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
|
|
16452
16573
|
`,
|
|
16453
16574
|
exitCode: 1
|
|
16454
16575
|
};
|
|
@@ -16473,24 +16594,24 @@ function runChange(repoRoot, args2, deps) {
|
|
|
16473
16594
|
}
|
|
16474
16595
|
|
|
16475
16596
|
// dist/shown-record.js
|
|
16476
|
-
import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as
|
|
16597
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16477
16598
|
import { platform as platform4 } from "node:os";
|
|
16478
|
-
import { join as
|
|
16599
|
+
import { join as join21 } from "node:path";
|
|
16479
16600
|
function updateShownRecord(engagementId, update, home) {
|
|
16480
16601
|
let temp = null;
|
|
16481
16602
|
try {
|
|
16482
16603
|
const dir = engagementStateDir(engagementId, home);
|
|
16483
|
-
const path =
|
|
16604
|
+
const path = join21(dir, SHOWN_PLANS_FILENAME);
|
|
16484
16605
|
let current;
|
|
16485
16606
|
try {
|
|
16486
|
-
current = parseShownRecord(
|
|
16607
|
+
current = parseShownRecord(readFileSync17(path, "utf-8"));
|
|
16487
16608
|
} catch {
|
|
16488
16609
|
current = { entries: [], reviews: [] };
|
|
16489
16610
|
}
|
|
16490
16611
|
const next = update(current);
|
|
16491
16612
|
const body = serialiseShownRecord(next);
|
|
16492
16613
|
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
16493
|
-
temp =
|
|
16614
|
+
temp = join21(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
|
|
16494
16615
|
writeFileSync8(temp, body, { mode: 384 });
|
|
16495
16616
|
if (platform4() !== "win32")
|
|
16496
16617
|
chmodSync3(temp, 384);
|
|
@@ -16508,7 +16629,7 @@ function updateShownRecord(engagementId, update, home) {
|
|
|
16508
16629
|
}
|
|
16509
16630
|
|
|
16510
16631
|
// dist/plan.js
|
|
16511
|
-
var PLAN_USAGE = `${
|
|
16632
|
+
var PLAN_USAGE = `${ownCommand()} plan <phase> [--show] [--repo <root>]`;
|
|
16512
16633
|
var PLAN_DIR_REL = "docs/phases";
|
|
16513
16634
|
function planFileRel(phase) {
|
|
16514
16635
|
return `${PLAN_DIR_REL}/${renderPhaseSegment(phase)}.plan.json`;
|
|
@@ -16567,7 +16688,7 @@ function readAndCheckPlan(repoRoot, phase, description, verb) {
|
|
|
16567
16688
|
const fileRel = planFileRel(phase);
|
|
16568
16689
|
let text;
|
|
16569
16690
|
try {
|
|
16570
|
-
text =
|
|
16691
|
+
text = readFileSync18(join22(repoRoot, fileRel), "utf-8");
|
|
16571
16692
|
} catch (err) {
|
|
16572
16693
|
const code = err.code;
|
|
16573
16694
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -16717,19 +16838,19 @@ function notAPhaseNameLine(verb, phase) {
|
|
|
16717
16838
|
`;
|
|
16718
16839
|
}
|
|
16719
16840
|
function removalOwedLines(verb, parts) {
|
|
16720
|
-
return parts.map((part) => `halfcycle ${verb}: docs/system.json still holds "${part.name}", which phase "${part.phase}"'s review ${part.act}. Take it out, run ${
|
|
16841
|
+
return parts.map((part) => `halfcycle ${verb}: docs/system.json still holds "${part.name}", which phase "${part.phase}"'s review ${part.act}. Take it out, run ${ownCommand()} system, then run this again.
|
|
16721
16842
|
`).join("");
|
|
16722
16843
|
}
|
|
16723
16844
|
function redrawLine(repoRoot, phase) {
|
|
16724
16845
|
let exists;
|
|
16725
16846
|
try {
|
|
16726
|
-
exists =
|
|
16847
|
+
exists = existsSync14(join22(repoRoot, DIAGRAM_DIR_REL, embedImageName(phaseDiagramBase(phase))));
|
|
16727
16848
|
} catch {
|
|
16728
16849
|
return null;
|
|
16729
16850
|
}
|
|
16730
16851
|
if (!exists)
|
|
16731
16852
|
return null;
|
|
16732
|
-
return `This phase's picture in your docs may show an earlier plan. Draw it again with: ${
|
|
16853
|
+
return `This phase's picture in your docs may show an earlier plan. Draw it again with: ${ownCommand()} draw phase ${quoteForShell(phase)}
|
|
16733
16854
|
`;
|
|
16734
16855
|
}
|
|
16735
16856
|
function movedUpLines(movedUp) {
|
|
@@ -16756,7 +16877,7 @@ function approvalLine2(phase, number, typedApprovalOffered, pinnedVersion, shown
|
|
|
16756
16877
|
return then(TYPED_APPROVAL_OFF_SENTENCE, "The project's owner approves it in the System view:");
|
|
16757
16878
|
}
|
|
16758
16879
|
if (!clientMeetsChatApprovalFloor(pinnedVersion)) {
|
|
16759
|
-
return then(
|
|
16880
|
+
return then(clientUpdateSentence(ownVersion()), "Or the project's owner approves it in the System view now:");
|
|
16760
16881
|
}
|
|
16761
16882
|
return then(`To approve it, the project's owner types \`approve ${phase}\` in their own interactive session ${HEADLESS_CANNOT_APPROVE}. Only the owner can.`, "They can also approve it in the System view:");
|
|
16762
16883
|
}
|
|
@@ -16845,7 +16966,7 @@ ${redraw}`;
|
|
|
16845
16966
|
}
|
|
16846
16967
|
|
|
16847
16968
|
// dist/review.js
|
|
16848
|
-
var REVIEW_USAGE = `${
|
|
16969
|
+
var REVIEW_USAGE = `${ownCommand()} review <phase> (--show | --answer <part> "<answer>") [--repo <root>]`;
|
|
16849
16970
|
function parseReviewArgs(argv) {
|
|
16850
16971
|
const positionals = [];
|
|
16851
16972
|
let repoRoot;
|
|
@@ -17064,7 +17185,7 @@ function rv8Line(view) {
|
|
|
17064
17185
|
}
|
|
17065
17186
|
return `halfcycle review: phase "${view.phase}" has no review open. Its review opens when its build is finished and the close step runs.`;
|
|
17066
17187
|
}
|
|
17067
|
-
var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${
|
|
17188
|
+
var RV9_LINE = `halfcycle review: this project is not signed in on this computer. ${hookRemedy(ownVersion())}`;
|
|
17068
17189
|
var RV10_LINE = "halfcycle review: Halfcycle could not be reached just now. Check this computer's connection, then run this again.";
|
|
17069
17190
|
var RV11_LINE = "halfcycle review: the list just shown could not be recorded on this computer, so a typed decision cannot use it. Run this again.";
|
|
17070
17191
|
function an1Line(name) {
|
|
@@ -17683,7 +17804,7 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
|
|
|
17683
17804
|
var PACING_FOLDER = "pacing";
|
|
17684
17805
|
function pacingFile(engagementId, name, home) {
|
|
17685
17806
|
try {
|
|
17686
|
-
return
|
|
17807
|
+
return join23(engagementStateDir(engagementId, home), PACING_FOLDER, name);
|
|
17687
17808
|
} catch {
|
|
17688
17809
|
return null;
|
|
17689
17810
|
}
|
|
@@ -17694,7 +17815,7 @@ function readPhaseLimitFile(engagementId, phase, home) {
|
|
|
17694
17815
|
return null;
|
|
17695
17816
|
let text;
|
|
17696
17817
|
try {
|
|
17697
|
-
text =
|
|
17818
|
+
text = readFileSync19(path, "utf-8");
|
|
17698
17819
|
} catch (err) {
|
|
17699
17820
|
return err.code === "ENOENT" ? "missing" : null;
|
|
17700
17821
|
}
|
|
@@ -17735,7 +17856,7 @@ function relayedChoiceLines(engagementId, phase, home) {
|
|
|
17735
17856
|
if (path === null)
|
|
17736
17857
|
return [];
|
|
17737
17858
|
try {
|
|
17738
|
-
const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(
|
|
17859
|
+
const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync19(path, "utf-8")));
|
|
17739
17860
|
if (!parsed.success || parsed.data.phase !== phase)
|
|
17740
17861
|
return [];
|
|
17741
17862
|
return parsed.data.entries.map((entry) => relayedChoiceLineOf(entry));
|
|
@@ -17748,7 +17869,7 @@ function readPhaseModels(engagementId, phase, openedAt, home) {
|
|
|
17748
17869
|
if (path === null)
|
|
17749
17870
|
return null;
|
|
17750
17871
|
try {
|
|
17751
|
-
const parsed = phaseModelsFileSchema.safeParse(JSON.parse(
|
|
17872
|
+
const parsed = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync19(path, "utf-8")));
|
|
17752
17873
|
if (!parsed.success || parsed.data.phase !== phase)
|
|
17753
17874
|
return null;
|
|
17754
17875
|
if (openedAt !== null && parsed.data.openedAt !== openedAt)
|
|
@@ -17758,8 +17879,8 @@ function readPhaseModels(engagementId, phase, openedAt, home) {
|
|
|
17758
17879
|
return null;
|
|
17759
17880
|
}
|
|
17760
17881
|
}
|
|
17761
|
-
function usageTokensOf(
|
|
17762
|
-
return
|
|
17882
|
+
function usageTokensOf(usage5) {
|
|
17883
|
+
return usage5.inputTokens + usage5.outputTokens + usage5.cacheWriteTokens + usage5.cacheReadTokens;
|
|
17763
17884
|
}
|
|
17764
17885
|
function tierFigureLines(work, expected, summary, models, details = false) {
|
|
17765
17886
|
const lines = [];
|
|
@@ -17851,12 +17972,12 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
17851
17972
|
if (pinned !== null)
|
|
17852
17973
|
assertEngagementId(pinned);
|
|
17853
17974
|
const fromStore = pinned === null ? null : readEngagementEnv(pinned, home);
|
|
17854
|
-
const looked = pinned === null ? `${
|
|
17975
|
+
const looked = pinned === null ? `${join24(repoRoot, ".halfcycle", "bundle.json")} (which names no engagement)` : engagementEnvPath(pinned, home);
|
|
17855
17976
|
const values = {};
|
|
17856
17977
|
for (const key of OPEN_PHASE_ENV_KEYS) {
|
|
17857
17978
|
const value = (env[key] ?? fromStore?.[key] ?? "").trim();
|
|
17858
17979
|
if (value === "") {
|
|
17859
|
-
throw new Error(`[halfcycle] ${key} is not set \u2014 it is neither in this shell's environment nor in ${looked}. That file is written by
|
|
17980
|
+
throw new Error(`[halfcycle] ${key} is not set \u2014 it is neither in this shell's environment nor in ${looked}. That file is written by \`${ownBareCommand()}\`; if this repo has no Halfcycle install yet, or this machine has never installed this engagement, run the installer here first.`);
|
|
17860
17981
|
}
|
|
17861
17982
|
values[key] = value;
|
|
17862
17983
|
}
|
|
@@ -17868,7 +17989,7 @@ function readRepoCredential(repoRoot, env = process.env, home) {
|
|
|
17868
17989
|
}
|
|
17869
17990
|
function readPinnedEngagementId(repoRoot) {
|
|
17870
17991
|
try {
|
|
17871
|
-
const pin = JSON.parse(
|
|
17992
|
+
const pin = JSON.parse(readFileSync20(join24(repoRoot, ".halfcycle", "bundle.json"), "utf-8"));
|
|
17872
17993
|
const id = pin[PIN_ENGAGEMENT_ID_FIELD];
|
|
17873
17994
|
return typeof id === "string" && id !== "" ? id : null;
|
|
17874
17995
|
} catch {
|
|
@@ -17890,7 +18011,7 @@ function writePhaseStamp(engagementId, phase, home) {
|
|
|
17890
18011
|
return { path, reason: err instanceof Error ? err.message : String(err) };
|
|
17891
18012
|
}
|
|
17892
18013
|
}
|
|
17893
|
-
async function openPhase(credential, phase, entry, home, repoRoot, sortingId, sizeId) {
|
|
18014
|
+
async function openPhase(credential, phase, entry, home, repoRoot, sortingId, sizeId, regression) {
|
|
17894
18015
|
const parsed = phaseEntryDecisionSchema.safeParse(entry);
|
|
17895
18016
|
if (!parsed.success) {
|
|
17896
18017
|
throw new Error(`[halfcycle] The entry decision is not a decision this platform accepts: ${parsed.error.issues.map((i) => i.message).join("; ")}.`);
|
|
@@ -17912,6 +18033,9 @@ async function openPhase(credential, phase, entry, home, repoRoot, sortingId, si
|
|
|
17912
18033
|
if (phase !== null && sizeId !== void 0) {
|
|
17913
18034
|
requestBody.sizeId = sizeId;
|
|
17914
18035
|
}
|
|
18036
|
+
if (phase !== null && regression === true) {
|
|
18037
|
+
requestBody.regression = true;
|
|
18038
|
+
}
|
|
17915
18039
|
const url = `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/phase`;
|
|
17916
18040
|
let res;
|
|
17917
18041
|
try {
|
|
@@ -18041,7 +18165,7 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
|
|
|
18041
18165
|
};
|
|
18042
18166
|
}
|
|
18043
18167
|
function refetchCommand(phase) {
|
|
18044
|
-
return `${
|
|
18168
|
+
return `${ownCommand()} build-record ${quoteForShell(phase)}`;
|
|
18045
18169
|
}
|
|
18046
18170
|
|
|
18047
18171
|
// dist/phase-work.js
|
|
@@ -18076,7 +18200,7 @@ async function readPhaseWork(credential, phase) {
|
|
|
18076
18200
|
}
|
|
18077
18201
|
|
|
18078
18202
|
// dist/draw/index.js
|
|
18079
|
-
var DRAW_USAGE = `${
|
|
18203
|
+
var DRAW_USAGE = `${ownCommand()} draw (architecture | phase <phase>) [--repo <root>]`;
|
|
18080
18204
|
function parseDrawArgs(argv) {
|
|
18081
18205
|
const positionals = [];
|
|
18082
18206
|
let repoRoot;
|
|
@@ -18352,9 +18476,9 @@ async function writeDrawOutcome(repoRoot, deps, outcome, target, warnings, canno
|
|
|
18352
18476
|
}
|
|
18353
18477
|
|
|
18354
18478
|
// dist/check-tasks.js
|
|
18355
|
-
import { readFileSync as
|
|
18356
|
-
import { join as
|
|
18357
|
-
var CHECK_TASKS_USAGE = `${
|
|
18479
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
18480
|
+
import { join as join25, resolve as resolve6 } from "node:path";
|
|
18481
|
+
var CHECK_TASKS_USAGE = `${ownCommand()} check-tasks <phase> [--with-task-list] [--repo <root>]`;
|
|
18358
18482
|
function parseCheckTasksArgs(argv) {
|
|
18359
18483
|
const positionals = [];
|
|
18360
18484
|
let repoRoot;
|
|
@@ -18394,7 +18518,7 @@ function issuePlace(rel, path) {
|
|
|
18394
18518
|
function readJsonFile(repoRoot, rel, parse) {
|
|
18395
18519
|
let text;
|
|
18396
18520
|
try {
|
|
18397
|
-
text =
|
|
18521
|
+
text = readFileSync21(resolve6(repoRoot, rel), "utf-8");
|
|
18398
18522
|
} catch (err) {
|
|
18399
18523
|
const code = err.code;
|
|
18400
18524
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18452,7 +18576,7 @@ function readPhasePlanParts(repoRoot, phase, description) {
|
|
|
18452
18576
|
const file = planFileRel(phase);
|
|
18453
18577
|
let text;
|
|
18454
18578
|
try {
|
|
18455
|
-
text =
|
|
18579
|
+
text = readFileSync21(join25(repoRoot, file), "utf-8");
|
|
18456
18580
|
} catch (err) {
|
|
18457
18581
|
const code = err.code;
|
|
18458
18582
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -18485,7 +18609,7 @@ function readPlanSpecFiles(repoRoot, plan) {
|
|
|
18485
18609
|
const rel = `${SPEC_FOLDER}/${slug}.md`;
|
|
18486
18610
|
let text;
|
|
18487
18611
|
try {
|
|
18488
|
-
text =
|
|
18612
|
+
text = readFileSync21(join25(repoRoot, rel), "utf-8");
|
|
18489
18613
|
} catch (err) {
|
|
18490
18614
|
const code = err.code;
|
|
18491
18615
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -18625,7 +18749,7 @@ async function runCheckTasksSized(repoRoot, phase, withTaskList, deps) {
|
|
|
18625
18749
|
}
|
|
18626
18750
|
|
|
18627
18751
|
// dist/check-criteria.js
|
|
18628
|
-
var CHECK_CRITERIA_USAGE = `${
|
|
18752
|
+
var CHECK_CRITERIA_USAGE = `${ownCommand()} check-criteria [<record file>] [--repo <root>]`;
|
|
18629
18753
|
function parseCheckCriteriaArgs(argv) {
|
|
18630
18754
|
const positionals = [];
|
|
18631
18755
|
let repoRoot;
|
|
@@ -18676,8 +18800,8 @@ function runCheckCriteria(repoRoot, file) {
|
|
|
18676
18800
|
}
|
|
18677
18801
|
|
|
18678
18802
|
// dist/check-report.js
|
|
18679
|
-
import { readFileSync as
|
|
18680
|
-
var CHECK_REPORT_USAGE = `${
|
|
18803
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
18804
|
+
var CHECK_REPORT_USAGE = `${ownCommand()} check-report <file | -> --head <sha>`;
|
|
18681
18805
|
function parseCheckReportArgs(argv) {
|
|
18682
18806
|
const positionals = [];
|
|
18683
18807
|
let head;
|
|
@@ -18712,7 +18836,7 @@ function runCheckReport(source, head, deps = {}) {
|
|
|
18712
18836
|
}
|
|
18713
18837
|
let body;
|
|
18714
18838
|
try {
|
|
18715
|
-
body = source === "-" ? (deps.readStdin ?? (() =>
|
|
18839
|
+
body = source === "-" ? (deps.readStdin ?? (() => readFileSync22(0, "utf-8")))() : readFileSync22(source, "utf-8");
|
|
18716
18840
|
} catch (err) {
|
|
18717
18841
|
const code = err.code;
|
|
18718
18842
|
const what = source === "-" ? "The report could not be read from standard input" : `${source} could not be read`;
|
|
@@ -18730,7 +18854,7 @@ function runCheckReport(source, head, deps = {}) {
|
|
|
18730
18854
|
}
|
|
18731
18855
|
|
|
18732
18856
|
// dist/ready.js
|
|
18733
|
-
var READY_USAGE = `${
|
|
18857
|
+
var READY_USAGE = `${ownCommand()} ready <phase> [--cap <n>] [--repo <root>]`;
|
|
18734
18858
|
var STATES_TEXT = buildTaskStateSchema.options.join(", ");
|
|
18735
18859
|
var READY_LINES = {
|
|
18736
18860
|
start: (id, title) => `Ready to start: ${named(id, title)}`,
|
|
@@ -18879,8 +19003,8 @@ function runReady(repoRoot, phase, cap) {
|
|
|
18879
19003
|
|
|
18880
19004
|
// dist/prove.js
|
|
18881
19005
|
import { spawn as spawn2 } from "node:child_process";
|
|
18882
|
-
var PROVE_USAGE = `${
|
|
18883
|
-
${
|
|
19006
|
+
var PROVE_USAGE = `${ownCommand()} prove <phase> <task> --label "<label>" --before -- <test command>
|
|
19007
|
+
${ownCommand()} prove <phase> <task> --label "<label>" --after [-- <test command>]
|
|
18884
19008
|
(or --change <id> in place of <phase> <task>; add --reason "<why>" to say why) [--repo <root>]`;
|
|
18885
19009
|
function splitProveInvocation(words) {
|
|
18886
19010
|
const dash = words.indexOf("--");
|
|
@@ -18955,9 +19079,9 @@ function runProveChild(binary, credential, repoRoot, words, deps = {}) {
|
|
|
18955
19079
|
}
|
|
18956
19080
|
|
|
18957
19081
|
// dist/uninstall.js
|
|
18958
|
-
import { existsSync as
|
|
19082
|
+
import { existsSync as existsSync15, lstatSync as lstatSync4, readFileSync as readFileSync23, readdirSync, rmSync as rmSync8, rmdirSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
18959
19083
|
import { homedir as homedir7 } from "node:os";
|
|
18960
|
-
import { dirname as
|
|
19084
|
+
import { dirname as dirname8, join as join26, relative as relative6, sep as sep2 } from "node:path";
|
|
18961
19085
|
var REMOVE_CREDENTIAL_FLAG = "--remove-credential";
|
|
18962
19086
|
function parseUninstallArgs(args2) {
|
|
18963
19087
|
let removeCredential = false;
|
|
@@ -18992,13 +19116,13 @@ function readMergedJson(targetRepo, rule) {
|
|
|
18992
19116
|
const { rel } = rule;
|
|
18993
19117
|
if (symlinkOnPath(targetRepo, rel))
|
|
18994
19118
|
return { state: "symlink" };
|
|
18995
|
-
const path =
|
|
19119
|
+
const path = join26(targetRepo, rel);
|
|
18996
19120
|
if (!lexists(path))
|
|
18997
19121
|
return { state: "absent" };
|
|
18998
19122
|
let text;
|
|
18999
19123
|
let value;
|
|
19000
19124
|
try {
|
|
19001
|
-
text =
|
|
19125
|
+
text = readFileSync23(path, "utf-8");
|
|
19002
19126
|
} catch {
|
|
19003
19127
|
text = "";
|
|
19004
19128
|
}
|
|
@@ -19011,29 +19135,29 @@ function readMergedJson(targetRepo, rule) {
|
|
|
19011
19135
|
return { state: "object", text, value };
|
|
19012
19136
|
if (!rule.holdsOurs(text))
|
|
19013
19137
|
return { state: "not-ours" };
|
|
19014
|
-
throw new Refusal(`${rel} is not a JSON object, so Halfcycle cannot take its own entries out of it and nothing was removed. Fix it or delete it, then run "${
|
|
19138
|
+
throw new Refusal(`${rel} is not a JSON object, so Halfcycle cannot take its own entries out of it and nothing was removed. Fix it or delete it, then run "${ownCommand()} uninstall" again.`);
|
|
19015
19139
|
}
|
|
19016
19140
|
function validate(options, rules) {
|
|
19017
19141
|
const { targetRepo, removeCredential, home } = options;
|
|
19018
|
-
const manifestPath =
|
|
19019
|
-
const pinPath =
|
|
19142
|
+
const manifestPath = join26(targetRepo, INSTALL_MANIFEST_REL);
|
|
19143
|
+
const pinPath = join26(targetRepo, BUNDLE_PIN_REL);
|
|
19020
19144
|
if (symlinkOnPath(targetRepo, INSTALL_MANIFEST_REL) || symlinkOnPath(targetRepo, BUNDLE_PIN_REL)) {
|
|
19021
19145
|
throw new Refusal(`.halfcycle, or a file Halfcycle keeps in it, is a symbolic link here, so nothing was followed and nothing was removed.`);
|
|
19022
19146
|
}
|
|
19023
19147
|
let manifest = null;
|
|
19024
19148
|
if (lexists(manifestPath)) {
|
|
19025
19149
|
try {
|
|
19026
|
-
manifest = parseManifest(
|
|
19150
|
+
manifest = parseManifest(readFileSync23(manifestPath, "utf-8"));
|
|
19027
19151
|
} catch (err) {
|
|
19028
19152
|
const why = err instanceof UnreadableManifestError ? err.message : "it could not be read";
|
|
19029
|
-
throw new Refusal(`${INSTALL_MANIFEST_REL} cannot be read (${why}), so nothing was removed. Restore it from git, or run "${
|
|
19153
|
+
throw new Refusal(`${INSTALL_MANIFEST_REL} cannot be read (${why}), so nothing was removed. Restore it from git, or run "${ownCommand()}" once to rewrite it, then run "${ownCommand()} uninstall" again.`);
|
|
19030
19154
|
}
|
|
19031
19155
|
}
|
|
19032
19156
|
let engagementId;
|
|
19033
19157
|
let pinReadable = true;
|
|
19034
19158
|
if (lexists(pinPath)) {
|
|
19035
19159
|
try {
|
|
19036
|
-
const pin = JSON.parse(
|
|
19160
|
+
const pin = JSON.parse(readFileSync23(pinPath, "utf-8"));
|
|
19037
19161
|
const id = isPlainObject4(pin) ? pin[PIN_ENGAGEMENT_ID_FIELD] : void 0;
|
|
19038
19162
|
engagementId = typeof id === "string" && id !== "" ? id : void 0;
|
|
19039
19163
|
} catch {
|
|
@@ -19057,8 +19181,8 @@ function validate(options, rules) {
|
|
|
19057
19181
|
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.`);
|
|
19058
19182
|
}
|
|
19059
19183
|
if (engagementId !== void 0) {
|
|
19060
|
-
const expectedParent =
|
|
19061
|
-
if (
|
|
19184
|
+
const expectedParent = join26(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
|
|
19185
|
+
if (dirname8(engagementStateDir(engagementId, home)) !== expectedParent) {
|
|
19062
19186
|
throw new Refusal(`the project id in ${BUNDLE_PIN_REL} does not name a folder under ~/.halfcycle/engagements. Nothing was removed.`);
|
|
19063
19187
|
}
|
|
19064
19188
|
}
|
|
@@ -19083,7 +19207,7 @@ function mergedFileRules(writeSet) {
|
|
|
19083
19207
|
function writeBack(targetRepo, rel, current, next, lists, name) {
|
|
19084
19208
|
if (next === current)
|
|
19085
19209
|
return;
|
|
19086
|
-
const path =
|
|
19210
|
+
const path = join26(targetRepo, rel);
|
|
19087
19211
|
try {
|
|
19088
19212
|
if (next === null) {
|
|
19089
19213
|
rmSync8(path);
|
|
@@ -19100,7 +19224,7 @@ function statusLineClaimedFromTree(targetRepo, writeSet) {
|
|
|
19100
19224
|
try {
|
|
19101
19225
|
if (symlinkOnPath(targetRepo, SETTINGS_REL))
|
|
19102
19226
|
return {};
|
|
19103
|
-
const value = JSON.parse(
|
|
19227
|
+
const value = JSON.parse(readFileSync23(join26(targetRepo, SETTINGS_REL), "utf-8"));
|
|
19104
19228
|
if (!isPlainObject4(value) || !("statusLine" in value))
|
|
19105
19229
|
return {};
|
|
19106
19230
|
const hash = canonicalSha256(value["statusLine"]);
|
|
@@ -19114,7 +19238,7 @@ function noRecord(targetRepo, writeSet) {
|
|
|
19114
19238
|
const claim = (rel, header, lines) => {
|
|
19115
19239
|
let text;
|
|
19116
19240
|
try {
|
|
19117
|
-
text = symlinkOnPath(targetRepo, rel) ? "" :
|
|
19241
|
+
text = symlinkOnPath(targetRepo, rel) ? "" : readFileSync23(join26(targetRepo, rel), "utf-8");
|
|
19118
19242
|
} catch {
|
|
19119
19243
|
text = "";
|
|
19120
19244
|
}
|
|
@@ -19355,7 +19479,7 @@ function lastOccurrence(text, block) {
|
|
|
19355
19479
|
}
|
|
19356
19480
|
function coveredPathExists(targetRepo, line) {
|
|
19357
19481
|
const path = line.trim().replace(/^\//, "").replace(/\/$/, "");
|
|
19358
|
-
return path !== "" && lexists(
|
|
19482
|
+
return path !== "" && lexists(join26(targetRepo, path));
|
|
19359
19483
|
}
|
|
19360
19484
|
function selfBuildMergedFileRules() {
|
|
19361
19485
|
return {
|
|
@@ -19369,7 +19493,7 @@ function removeSelfBuildOwnedFile(targetRepo, rel, lists, name) {
|
|
|
19369
19493
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19370
19494
|
return;
|
|
19371
19495
|
}
|
|
19372
|
-
const abs =
|
|
19496
|
+
const abs = join26(targetRepo, rel);
|
|
19373
19497
|
let isFile;
|
|
19374
19498
|
try {
|
|
19375
19499
|
isFile = lstatSync4(abs).isFile();
|
|
@@ -19393,10 +19517,10 @@ function removeSelfBuildCrewRoster(targetRepo, lists, name) {
|
|
|
19393
19517
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19394
19518
|
return;
|
|
19395
19519
|
}
|
|
19396
|
-
const abs =
|
|
19520
|
+
const abs = join26(targetRepo, rel);
|
|
19397
19521
|
let bytes;
|
|
19398
19522
|
try {
|
|
19399
|
-
bytes =
|
|
19523
|
+
bytes = readFileSync23(abs);
|
|
19400
19524
|
} catch {
|
|
19401
19525
|
return;
|
|
19402
19526
|
}
|
|
@@ -19420,10 +19544,10 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19420
19544
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
19421
19545
|
return;
|
|
19422
19546
|
}
|
|
19423
|
-
const abs =
|
|
19547
|
+
const abs = join26(targetRepo, rel);
|
|
19424
19548
|
let bytes;
|
|
19425
19549
|
try {
|
|
19426
|
-
bytes =
|
|
19550
|
+
bytes = readFileSync23(abs);
|
|
19427
19551
|
} catch {
|
|
19428
19552
|
return;
|
|
19429
19553
|
}
|
|
@@ -19445,7 +19569,7 @@ function removeSelfBuildCrewProfile(targetRepo, rel, text, lists, name) {
|
|
|
19445
19569
|
function removeSelfBuildEmptyDir(targetRepo, dir, lists, name) {
|
|
19446
19570
|
if (symlinkOnPath(targetRepo, dir))
|
|
19447
19571
|
return;
|
|
19448
|
-
const abs =
|
|
19572
|
+
const abs = join26(targetRepo, dir);
|
|
19449
19573
|
try {
|
|
19450
19574
|
if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
|
|
19451
19575
|
return;
|
|
@@ -19479,7 +19603,7 @@ function uninstallSelfBuild(options) {
|
|
|
19479
19603
|
throw err;
|
|
19480
19604
|
}
|
|
19481
19605
|
const { targetRepo } = options;
|
|
19482
|
-
const pinExisted = lexists(
|
|
19606
|
+
const pinExisted = lexists(join26(targetRepo, BUNDLE_PIN_REL));
|
|
19483
19607
|
const name = (list2, text, _path) => {
|
|
19484
19608
|
list2.push(text);
|
|
19485
19609
|
};
|
|
@@ -19561,7 +19685,7 @@ function uninstall(options) {
|
|
|
19561
19685
|
removeCodexConfigBlock(targetRepo, manifest.codexConfig, deleteCodexWhenEmpty(manifest.codexConfig), result, name);
|
|
19562
19686
|
if (holdsLocalAllowRecord(targetRepo)) {
|
|
19563
19687
|
removeLocalAllow(targetRepo);
|
|
19564
|
-
if (
|
|
19688
|
+
if (existsSync15(join26(targetRepo, LOCAL_SETTINGS_REL2))) {
|
|
19565
19689
|
name(result.restored, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
19566
19690
|
} else {
|
|
19567
19691
|
name(result.removed, LOCAL_SETTINGS_REL2, LOCAL_SETTINGS_REL2);
|
|
@@ -19604,7 +19728,7 @@ function uninstall(options) {
|
|
|
19604
19728
|
const gitattributes = manifest.gitattributes ?? { created: false, appended: [] };
|
|
19605
19729
|
trimAppendedFile(targetRepo, gitattributes, deleteWhenEmpty(gitattributes.created), gitattributesRules(writeSet), result, name);
|
|
19606
19730
|
const foundNothing = result.removed.length + result.restored.length + result.kept.length + result.failed.length === 0 && result.keptIgnoreLines.length === 0;
|
|
19607
|
-
if (checked.manifest === null && !lexists(
|
|
19731
|
+
if (checked.manifest === null && !lexists(join26(targetRepo, BUNDLE_PIN_REL)) && foundNothing) {
|
|
19608
19732
|
return { outcome: "not-installed", exitCode: 0, ...result, credential: nothingToSay };
|
|
19609
19733
|
}
|
|
19610
19734
|
const credential = handleCredential(options, checked);
|
|
@@ -19612,7 +19736,7 @@ function uninstall(options) {
|
|
|
19612
19736
|
if (result.failed.length === 0 && !credentialFailed) {
|
|
19613
19737
|
removeInstalledFile(targetRepo, BUNDLE_PIN_REL, recorded.get(BUNDLE_PIN_REL), writeSet, result, name);
|
|
19614
19738
|
try {
|
|
19615
|
-
rmSync8(
|
|
19739
|
+
rmSync8(join26(targetRepo, INSTALL_MANIFEST_REL), { force: true });
|
|
19616
19740
|
named2.add(INSTALL_MANIFEST_REL);
|
|
19617
19741
|
} catch {
|
|
19618
19742
|
name(result.failed, INSTALL_MANIFEST_REL, INSTALL_MANIFEST_REL);
|
|
@@ -19647,13 +19771,13 @@ function applyMergedJson(targetRepo, rule, file, deleteWhenEmpty, lists, name, e
|
|
|
19647
19771
|
return;
|
|
19648
19772
|
const empty = Object.keys(value).length === 0 && deleteWhenEmpty;
|
|
19649
19773
|
writeBack(targetRepo, rel, file.text, empty ? null : inLineEndingOf(file.text, serializeJson(value)), lists, name);
|
|
19650
|
-
if (adopted && lexists(
|
|
19774
|
+
if (adopted && lexists(join26(targetRepo, rel))) {
|
|
19651
19775
|
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);
|
|
19652
19776
|
}
|
|
19653
19777
|
}
|
|
19654
19778
|
function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, name) {
|
|
19655
19779
|
const rel = CODEX_CONFIG_REL2;
|
|
19656
|
-
const path =
|
|
19780
|
+
const path = join26(targetRepo, rel);
|
|
19657
19781
|
if (symlinkOnPath(targetRepo, rel)) {
|
|
19658
19782
|
if (lexists(path))
|
|
19659
19783
|
name(lists.leftAlone, `${rel} (a symbolic link, or inside one \u2014 not followed)`, rel);
|
|
@@ -19663,7 +19787,7 @@ function removeCodexConfigBlock(targetRepo, record2, deleteWhenEmpty, lists, nam
|
|
|
19663
19787
|
try {
|
|
19664
19788
|
if (!lstatSync4(path).isFile())
|
|
19665
19789
|
return;
|
|
19666
|
-
text =
|
|
19790
|
+
text = readFileSync23(path, "utf-8");
|
|
19667
19791
|
} catch {
|
|
19668
19792
|
return;
|
|
19669
19793
|
}
|
|
@@ -19698,7 +19822,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
19698
19822
|
name(lists.leftAlone, `${path} (a symbolic link, or inside one \u2014 not followed)`, path);
|
|
19699
19823
|
return;
|
|
19700
19824
|
}
|
|
19701
|
-
const abs =
|
|
19825
|
+
const abs = join26(targetRepo, path);
|
|
19702
19826
|
let isFile;
|
|
19703
19827
|
try {
|
|
19704
19828
|
isFile = lstatSync4(abs).isFile();
|
|
@@ -19712,7 +19836,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
19712
19836
|
if (!writeSet.ownedDirs.has(path.slice(0, path.lastIndexOf("/")))) {
|
|
19713
19837
|
let bytes;
|
|
19714
19838
|
try {
|
|
19715
|
-
bytes =
|
|
19839
|
+
bytes = readFileSync23(abs);
|
|
19716
19840
|
} catch {
|
|
19717
19841
|
name(lists.failed, path, path);
|
|
19718
19842
|
return;
|
|
@@ -19732,7 +19856,7 @@ function removeInstalledFile(targetRepo, path, entry, writeSet, lists, name) {
|
|
|
19732
19856
|
function removeEmptyDir(targetRepo, dir, writeSet, lists, name) {
|
|
19733
19857
|
if (!writeSet.dirs.has(dir) || symlinkOnPath(targetRepo, dir))
|
|
19734
19858
|
return;
|
|
19735
|
-
const abs =
|
|
19859
|
+
const abs = join26(targetRepo, dir);
|
|
19736
19860
|
try {
|
|
19737
19861
|
if (!lstatSync4(abs).isDirectory() || readdirSync(abs).length > 0)
|
|
19738
19862
|
return;
|
|
@@ -19751,10 +19875,10 @@ function trimAppendedFile(targetRepo, rec, deleteWhenEmpty, rules, lists, name)
|
|
|
19751
19875
|
name(lists.leftAlone, `${rel} (a symbolic link \u2014 not followed)`, rel);
|
|
19752
19876
|
return [];
|
|
19753
19877
|
}
|
|
19754
|
-
const path =
|
|
19878
|
+
const path = join26(targetRepo, rel);
|
|
19755
19879
|
let before;
|
|
19756
19880
|
try {
|
|
19757
|
-
before =
|
|
19881
|
+
before = readFileSync23(path, "utf-8");
|
|
19758
19882
|
} catch {
|
|
19759
19883
|
return [];
|
|
19760
19884
|
}
|
|
@@ -19798,16 +19922,16 @@ function filesUnder(root, rel) {
|
|
|
19798
19922
|
const walk = (dirRel) => {
|
|
19799
19923
|
let names;
|
|
19800
19924
|
try {
|
|
19801
|
-
if (!lstatSync4(
|
|
19925
|
+
if (!lstatSync4(join26(root, dirRel)).isDirectory())
|
|
19802
19926
|
return;
|
|
19803
|
-
names = readdirSync(
|
|
19927
|
+
names = readdirSync(join26(root, dirRel));
|
|
19804
19928
|
} catch {
|
|
19805
19929
|
return;
|
|
19806
19930
|
}
|
|
19807
19931
|
for (const entry of names.sort()) {
|
|
19808
19932
|
const childRel = `${dirRel}/${entry}`;
|
|
19809
19933
|
try {
|
|
19810
|
-
if (lstatSync4(
|
|
19934
|
+
if (lstatSync4(join26(root, childRel)).isDirectory())
|
|
19811
19935
|
walk(childRel);
|
|
19812
19936
|
else
|
|
19813
19937
|
out.push(childRel);
|
|
@@ -19846,13 +19970,13 @@ function renderUninstallReport(result, home) {
|
|
|
19846
19970
|
section("Kept, because it changed after Halfcycle wrote it \u2014 delete it yourself if you do not want it:", result.kept);
|
|
19847
19971
|
section('Kept in .gitignore, because the path each line covers still exists \u2014 without the line, "git add ." would commit it:', result.keptIgnoreLines);
|
|
19848
19972
|
section("Left alone \u2014 Halfcycle did not write these, or cannot tell that it did:", result.leftAlone);
|
|
19849
|
-
section(`Could not remove \u2014 fix the cause, then run "${
|
|
19973
|
+
section(`Could not remove \u2014 fix the cause, then run "${ownCommand()} uninstall" again to finish:`, result.failed);
|
|
19850
19974
|
const c = result.credential;
|
|
19851
19975
|
switch (c.kind) {
|
|
19852
19976
|
case "still-held":
|
|
19853
19977
|
out.push(`[halfcycle] This machine still holds this project's credential, at ${shownPath(c.dir, home)}.`);
|
|
19854
19978
|
out.push(" Every checkout and worktree of this project on this machine uses it.");
|
|
19855
|
-
out.push(` To remove it too: ${
|
|
19979
|
+
out.push(` To remove it too: ${ownCommand()} uninstall ${REMOVE_CREDENTIAL_FLAG}`);
|
|
19856
19980
|
break;
|
|
19857
19981
|
case "invalid-id":
|
|
19858
19982
|
out.push(`[halfcycle] This machine may still hold a credential for this project, but the project id in ${BUNDLE_PIN_REL} is not valid, so it is not shown as a path.`);
|
|
@@ -19860,7 +19984,7 @@ function renderUninstallReport(result, home) {
|
|
|
19860
19984
|
case "removed":
|
|
19861
19985
|
out.push(`[halfcycle] Removed this machine's credential for this project, at ${shownPath(c.dir, home)}.`);
|
|
19862
19986
|
out.push(" Every other checkout and worktree of this project on this machine used it too. Each stops reaching");
|
|
19863
|
-
out.push(` Halfcycle until \`${
|
|
19987
|
+
out.push(` Halfcycle until \`${ownCommand()}\` is run there again.`);
|
|
19864
19988
|
break;
|
|
19865
19989
|
case "not-held":
|
|
19866
19990
|
out.push(`[halfcycle] This machine holds no credential for this project (nothing at ${shownPath(c.dir, home)}).`);
|
|
@@ -19869,7 +19993,7 @@ function renderUninstallReport(result, home) {
|
|
|
19869
19993
|
out.push(`[halfcycle] No credential was removed: ${BUNDLE_PIN_REL} is missing, so there is no project id to find it by.`);
|
|
19870
19994
|
break;
|
|
19871
19995
|
case "failed":
|
|
19872
|
-
out.push(`[halfcycle] Could not remove this machine's credential for this project, at ${shownPath(c.dir, home)}: ${c.reason}. Run "${
|
|
19996
|
+
out.push(`[halfcycle] Could not remove this machine's credential for this project, at ${shownPath(c.dir, home)}: ${c.reason}. Run "${ownCommand()} uninstall ${REMOVE_CREDENTIAL_FLAG}" again to finish.`);
|
|
19873
19997
|
break;
|
|
19874
19998
|
case "nothing-to-say":
|
|
19875
19999
|
break;
|
|
@@ -19879,8 +20003,7 @@ function renderUninstallReport(result, home) {
|
|
|
19879
20003
|
}
|
|
19880
20004
|
|
|
19881
20005
|
// dist/banner-facts.js
|
|
19882
|
-
import {
|
|
19883
|
-
import { basename as basename4, join as join26, resolve as resolve7 } from "node:path";
|
|
20006
|
+
import { basename as basename4, resolve as resolve7 } from "node:path";
|
|
19884
20007
|
var BRAND_URL = "halfcycle.ai";
|
|
19885
20008
|
var TAGLINE = [
|
|
19886
20009
|
"Fewer cycles.",
|
|
@@ -19891,14 +20014,6 @@ var TAGLINE_NARROW = [
|
|
|
19891
20014
|
"The thinking moves earlier.",
|
|
19892
20015
|
"So does the finish."
|
|
19893
20016
|
];
|
|
19894
|
-
function bundleVersion() {
|
|
19895
|
-
try {
|
|
19896
|
-
const pkg = JSON.parse(readFileSync23(join26(BUNDLE_ROOT, "package.json"), "utf-8"));
|
|
19897
|
-
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
19898
|
-
} catch {
|
|
19899
|
-
return void 0;
|
|
19900
|
-
}
|
|
19901
|
-
}
|
|
19902
20017
|
function claudeCodeVersion(deps = {}) {
|
|
19903
20018
|
return probeClaudeCodeVersion(3e3, deps);
|
|
19904
20019
|
}
|
|
@@ -19987,8 +20102,8 @@ function safeBasename(dir) {
|
|
|
19987
20102
|
}
|
|
19988
20103
|
|
|
19989
20104
|
// dist/size.js
|
|
19990
|
-
var SIZE_USAGE = `${
|
|
19991
|
-
${
|
|
20105
|
+
var SIZE_USAGE = `${ownCommand()} size sort --description "<one line>" [--existing-code] [--sign-in] [--shared-data] [--paid-services] [--more-than-three-parts] [--repo <root>]
|
|
20106
|
+
${ownCommand()} size up <medium|full> [--repo <root>]`;
|
|
19992
20107
|
function usage2(line) {
|
|
19993
20108
|
return { ok: false, line: `${line}
|
|
19994
20109
|
Usage:
|
|
@@ -20019,7 +20134,6 @@ function parseSizeArgs(rest) {
|
|
|
20019
20134
|
};
|
|
20020
20135
|
if (form === "sort") {
|
|
20021
20136
|
let description = null;
|
|
20022
|
-
let estimate = null;
|
|
20023
20137
|
const facts = { existingCode: false, signIn: false, sharedData: false, paidServices: false, moreThanThreeParts: false };
|
|
20024
20138
|
for (let i = 0; i < words.length; i++) {
|
|
20025
20139
|
const w = words[i];
|
|
@@ -20029,9 +20143,7 @@ function parseSizeArgs(rest) {
|
|
|
20029
20143
|
return usage2(`halfcycle size sort: ${w} needs a value after it.`);
|
|
20030
20144
|
if (w === "--description")
|
|
20031
20145
|
description = v;
|
|
20032
|
-
else if (w === "--
|
|
20033
|
-
estimate = v;
|
|
20034
|
-
else
|
|
20146
|
+
else if (w === "--repo")
|
|
20035
20147
|
repoRoot = v;
|
|
20036
20148
|
i++;
|
|
20037
20149
|
} else if (FACT_SWITCHES[w] !== void 0) {
|
|
@@ -20042,10 +20154,7 @@ function parseSizeArgs(rest) {
|
|
|
20042
20154
|
}
|
|
20043
20155
|
if (description === null)
|
|
20044
20156
|
return usage2(missingRequirements("size", ["sort"])[0].message);
|
|
20045
|
-
|
|
20046
|
-
return usage2(`halfcycle size sort: --estimate says how long you expect it to take: ${PROJECT_SIZE_ESTIMATES.join(", ")}.`);
|
|
20047
|
-
}
|
|
20048
|
-
const request = projectSizeRequestSchema.safeParse({ description, estimate, ...facts });
|
|
20157
|
+
const request = projectSizeRequestSchema.safeParse({ description, ...facts });
|
|
20049
20158
|
if (!request.success) {
|
|
20050
20159
|
const n = request.error.issues.length;
|
|
20051
20160
|
return {
|
|
@@ -20123,7 +20232,7 @@ async function runSizeSort(repoRoot, args2, deps) {
|
|
|
20123
20232
|
} catch {
|
|
20124
20233
|
return {
|
|
20125
20234
|
stdout: "",
|
|
20126
|
-
stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${
|
|
20235
|
+
stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
|
|
20127
20236
|
`,
|
|
20128
20237
|
exitCode: 1
|
|
20129
20238
|
};
|
|
@@ -20171,7 +20280,7 @@ async function runSizeUp(repoRoot, args2, deps) {
|
|
|
20171
20280
|
} catch {
|
|
20172
20281
|
return {
|
|
20173
20282
|
stdout: "",
|
|
20174
|
-
stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${
|
|
20283
|
+
stderr: `halfcycle size: this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
|
|
20175
20284
|
`,
|
|
20176
20285
|
exitCode: 1
|
|
20177
20286
|
};
|
|
@@ -20201,11 +20310,134 @@ function runSize(repoRoot, args2, deps) {
|
|
|
20201
20310
|
return args2.form === "sort" ? runSizeSort(repoRoot, args2, deps) : runSizeUp(repoRoot, args2, deps);
|
|
20202
20311
|
}
|
|
20203
20312
|
|
|
20313
|
+
// dist/regression.js
|
|
20314
|
+
var REGRESSION_USAGE = `${ownCommand()} regression --every <1 to 20> [--repo <root>]`;
|
|
20315
|
+
function usage3(line) {
|
|
20316
|
+
return { ok: false, line: `${line}
|
|
20317
|
+
Usage:
|
|
20318
|
+
${REGRESSION_USAGE}
|
|
20319
|
+
`, exitCode: 2 };
|
|
20320
|
+
}
|
|
20321
|
+
function parseRegressionArgs(rest) {
|
|
20322
|
+
let every = null;
|
|
20323
|
+
let repoRoot;
|
|
20324
|
+
for (let i = 0; i < rest.length; i++) {
|
|
20325
|
+
const w = rest[i];
|
|
20326
|
+
if (w === "--every" || w === "--repo") {
|
|
20327
|
+
const v = rest[i + 1];
|
|
20328
|
+
if (v === void 0 || v.trim() === "" || w === "--repo" && v.startsWith("--")) {
|
|
20329
|
+
return usage3(`halfcycle regression: ${w} needs a value after it.`);
|
|
20330
|
+
}
|
|
20331
|
+
if (w === "--every")
|
|
20332
|
+
every = v;
|
|
20333
|
+
else
|
|
20334
|
+
repoRoot = v;
|
|
20335
|
+
i++;
|
|
20336
|
+
} else {
|
|
20337
|
+
return usage3(`halfcycle regression: "${w}" is not something this command takes.`);
|
|
20338
|
+
}
|
|
20339
|
+
}
|
|
20340
|
+
if (every === null)
|
|
20341
|
+
return usage3("halfcycle regression: say how many phases may close between regressions.");
|
|
20342
|
+
const number = /^-?\d+$/.test(every.trim()) ? Number(every) : Number.NaN;
|
|
20343
|
+
const parsed = regressionEveryRequestSchema.safeParse({ every: number });
|
|
20344
|
+
if (!parsed.success) {
|
|
20345
|
+
return { ok: false, line: `${REGRESSION_EVERY_RANGE_REFUSAL}
|
|
20346
|
+
`, exitCode: 2 };
|
|
20347
|
+
}
|
|
20348
|
+
return { ok: true, args: { request: parsed.data, ...repoRoot === void 0 ? {} : { repoRoot } } };
|
|
20349
|
+
}
|
|
20350
|
+
var COULD_NOT_REACH3 = "Halfcycle could not be reached. Nothing was changed; check this machine's connection, then run this again.";
|
|
20351
|
+
async function runRegression(repoRoot, args2, deps) {
|
|
20352
|
+
const p = "halfcycle regression:";
|
|
20353
|
+
let credential;
|
|
20354
|
+
try {
|
|
20355
|
+
credential = deps.readCredential(repoRoot);
|
|
20356
|
+
} catch {
|
|
20357
|
+
return {
|
|
20358
|
+
stdout: "",
|
|
20359
|
+
stderr: `${p} this project is not connected to Halfcycle on this machine. Run ${ownCommand()} here first, then run this again. Nothing was sent.
|
|
20360
|
+
`,
|
|
20361
|
+
exitCode: 1
|
|
20362
|
+
};
|
|
20363
|
+
}
|
|
20364
|
+
const url = `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/regression-every`;
|
|
20365
|
+
let status;
|
|
20366
|
+
let raw = null;
|
|
20367
|
+
try {
|
|
20368
|
+
const res = await deps.fetch(url, {
|
|
20369
|
+
method: "PUT",
|
|
20370
|
+
headers: { authorization: `Bearer ${credential.token}`, "content-type": "application/json" },
|
|
20371
|
+
body: JSON.stringify(args2.request)
|
|
20372
|
+
});
|
|
20373
|
+
status = res.status;
|
|
20374
|
+
const text = await res.text();
|
|
20375
|
+
try {
|
|
20376
|
+
raw = JSON.parse(text);
|
|
20377
|
+
} catch {
|
|
20378
|
+
raw = null;
|
|
20379
|
+
}
|
|
20380
|
+
} catch {
|
|
20381
|
+
return { stdout: "", stderr: `${p} ${COULD_NOT_REACH3}
|
|
20382
|
+
`, exitCode: 1 };
|
|
20383
|
+
}
|
|
20384
|
+
const answered2 = regressionEveryResponseSchema.safeParse(raw);
|
|
20385
|
+
if (status >= 200 && status < 300 && answered2.success) {
|
|
20386
|
+
const line = answered2.data.line === null ? "" : `[halfcycle] ${answered2.data.line}
|
|
20387
|
+
`;
|
|
20388
|
+
return {
|
|
20389
|
+
stdout: `[halfcycle] The number of phases between regressions is now ${answered2.data.every}.
|
|
20390
|
+
${line}`,
|
|
20391
|
+
stderr: "",
|
|
20392
|
+
exitCode: 0
|
|
20393
|
+
};
|
|
20394
|
+
}
|
|
20395
|
+
const wire = wireErrorSchema.safeParse(raw);
|
|
20396
|
+
const said = wire.success ? wire.data.message : `Halfcycle answered ${status}. Nothing was changed; run this again later.`;
|
|
20397
|
+
return { stdout: "", stderr: `${p} ${said}
|
|
20398
|
+
`, exitCode: 1 };
|
|
20399
|
+
}
|
|
20400
|
+
|
|
20401
|
+
// dist/crew.js
|
|
20402
|
+
var CREW_USAGE = `${ownCommand()} crew [--repo <root>]`;
|
|
20403
|
+
function usage4(line) {
|
|
20404
|
+
return { ok: false, line: `${line}
|
|
20405
|
+
Usage:
|
|
20406
|
+
${CREW_USAGE}
|
|
20407
|
+
`, exitCode: 2 };
|
|
20408
|
+
}
|
|
20409
|
+
function parseCrewArgs(rest) {
|
|
20410
|
+
let repoRoot;
|
|
20411
|
+
for (let i = 0; i < rest.length; i++) {
|
|
20412
|
+
const w = rest[i];
|
|
20413
|
+
if (w === "--repo") {
|
|
20414
|
+
const v = rest[i + 1];
|
|
20415
|
+
if (v === void 0 || v.trim() === "" || v.startsWith("--")) {
|
|
20416
|
+
return usage4("halfcycle crew: --repo needs a value after it.");
|
|
20417
|
+
}
|
|
20418
|
+
repoRoot = v;
|
|
20419
|
+
i++;
|
|
20420
|
+
} else {
|
|
20421
|
+
return usage4(`halfcycle crew: "${w}" is not something this command takes.`);
|
|
20422
|
+
}
|
|
20423
|
+
}
|
|
20424
|
+
return { ok: true, ...repoRoot === void 0 ? {} : { repoRoot } };
|
|
20425
|
+
}
|
|
20426
|
+
async function runCrew(repoRoot, deps) {
|
|
20427
|
+
const size = await readSizeOnce(repoRoot, deps);
|
|
20428
|
+
const members = crewOfProjectSize(size.kind === "small" ? "small" : void 0);
|
|
20429
|
+
const stdout = members.map((m) => `${m.callsign} ${m.role} ${m.tier}
|
|
20430
|
+
`).join("");
|
|
20431
|
+
const stderr = size.kind === "failed" ? `halfcycle crew: ${SIZE_READ_FAILED_LINE}
|
|
20432
|
+
` : "";
|
|
20433
|
+
return { stdout, stderr, exitCode: 0 };
|
|
20434
|
+
}
|
|
20435
|
+
|
|
20204
20436
|
// dist/pace.js
|
|
20205
20437
|
import { spawn as spawn3 } from "node:child_process";
|
|
20206
20438
|
import { readFileSync as readFileSync24 } from "node:fs";
|
|
20207
20439
|
import { join as join27 } from "node:path";
|
|
20208
|
-
var PACE_USAGE = `${
|
|
20440
|
+
var PACE_USAGE = `${ownCommand()} pace [--agent claude-code|codex] [--start <phase> [--agents <n>]] [--repo <root>]`;
|
|
20209
20441
|
function parsePaceArgs(argv) {
|
|
20210
20442
|
let repoRoot;
|
|
20211
20443
|
let phase;
|
|
@@ -20311,7 +20543,7 @@ function readPaceLine(stdout) {
|
|
|
20311
20543
|
return null;
|
|
20312
20544
|
}
|
|
20313
20545
|
}
|
|
20314
|
-
var PACE_NOT_CONNECTED_LINE = `halfcycle pace: this project is not signed in on this computer. ${
|
|
20546
|
+
var PACE_NOT_CONNECTED_LINE = `halfcycle pace: this project is not signed in on this computer. ${hookRemedy(ownVersion())}`;
|
|
20315
20547
|
function notPacedLine(reason) {
|
|
20316
20548
|
return reason === "not-configured" ? `${PACE_NOT_CONNECTED_LINE}
|
|
20317
20549
|
` : "halfcycle pace: Halfcycle could not read this computer's usage records just now. Run this again in a minute.\n";
|
|
@@ -20353,7 +20585,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
|
|
|
20353
20585
|
if (result.suggestion === void 0)
|
|
20354
20586
|
return { stdout: "", stderr: notPacedLine("unreadable"), exitCode: 0 };
|
|
20355
20587
|
const behind = promptScriptBehind((deps.readPromptScript ?? readPromptScript)(repoRoot));
|
|
20356
|
-
return { stdout: `${paceStartLinesOf(result.suggestion, behind).join("\n")}
|
|
20588
|
+
return { stdout: `${paceStartLinesOf(result.suggestion, behind, ownVersion()).join("\n")}
|
|
20357
20589
|
`, stderr: "", exitCode: 0 };
|
|
20358
20590
|
}
|
|
20359
20591
|
const share = result.keepFreePercent ?? KEEP_FREE_DEFAULT;
|
|
@@ -20379,7 +20611,7 @@ async function runPace(repoRoot, start, deps, agent = null) {
|
|
|
20379
20611
|
// dist/route.js
|
|
20380
20612
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
20381
20613
|
import { join as join28 } from "node:path";
|
|
20382
|
-
var ROUTE_USAGE = `${
|
|
20614
|
+
var ROUTE_USAGE = `${ownCommand()} route <phase> <task> [--agent claude-code|codex] [--repo <root>]`;
|
|
20383
20615
|
var ROUTE_WORK_READ_DEADLINE_MS = 5e3;
|
|
20384
20616
|
function parseRouteArgs(argv) {
|
|
20385
20617
|
const positionals = [];
|
|
@@ -20515,7 +20747,7 @@ import { spawnSync as spawnSync4 } from "node:child_process";
|
|
|
20515
20747
|
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";
|
|
20516
20748
|
import { homedir as homedir8 } from "node:os";
|
|
20517
20749
|
import { join as join29, resolve as resolve8 } from "node:path";
|
|
20518
|
-
var RESTORE_USAGE = `${
|
|
20750
|
+
var RESTORE_USAGE = `${ownCommand()} restore [<id>]`;
|
|
20519
20751
|
function parseRestoreArgs(rest) {
|
|
20520
20752
|
if (rest.length > 1)
|
|
20521
20753
|
return { ok: false };
|
|
@@ -20524,7 +20756,7 @@ function parseRestoreArgs(rest) {
|
|
|
20524
20756
|
return { ok: false };
|
|
20525
20757
|
return { ok: true, id: first };
|
|
20526
20758
|
}
|
|
20527
|
-
var LIST_COMMAND = `${
|
|
20759
|
+
var LIST_COMMAND = `${ownCommand()} restore`;
|
|
20528
20760
|
function runRestore(cwd, id, options = {}) {
|
|
20529
20761
|
const now = options.now ?? Date.now;
|
|
20530
20762
|
const top = git2(cwd, ["rev-parse", "--show-toplevel"]);
|
|
@@ -20534,7 +20766,7 @@ function runRestore(cwd, id, options = {}) {
|
|
|
20534
20766
|
const root = realOrSelf(top.text.trim());
|
|
20535
20767
|
const engagementId = pinnedEngagement(root);
|
|
20536
20768
|
if (engagementId === null) {
|
|
20537
|
-
return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${
|
|
20769
|
+
return refused2(`Halfcycle is not set up in this project, so it has saved nothing here. To set it up, run: ${ownCommand()}`);
|
|
20538
20770
|
}
|
|
20539
20771
|
const folder2 = join29(options.home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
|
|
20540
20772
|
const refs = savedRefs(root);
|
|
@@ -20929,10 +21161,10 @@ function realOrSelf(p) {
|
|
|
20929
21161
|
}
|
|
20930
21162
|
|
|
20931
21163
|
// dist/request-shapes-verb.js
|
|
20932
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
21164
|
+
import { chmodSync as chmodSync4, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
20933
21165
|
import { homedir as homedir9, platform as platform5 } from "node:os";
|
|
20934
21166
|
import { join as join30 } from "node:path";
|
|
20935
|
-
var REQUEST_SHAPES_USAGE = `${
|
|
21167
|
+
var REQUEST_SHAPES_USAGE = `${ownCommand()} request-shapes on|off|show|clear`;
|
|
20936
21168
|
var REQUEST_SHAPES_FORMS = ["on", "off", "show", "clear"];
|
|
20937
21169
|
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.";
|
|
20938
21170
|
var REQUEST_SHAPES_OFF_LINE = "Halfcycle has stopped writing down requests. What it wrote is still there: run request-shapes show to read it, or request-shapes clear to remove it.";
|
|
@@ -20976,7 +21208,7 @@ function formatLine(line) {
|
|
|
20976
21208
|
function showLog(logPath2) {
|
|
20977
21209
|
let text = "";
|
|
20978
21210
|
try {
|
|
20979
|
-
text =
|
|
21211
|
+
text = existsSync16(logPath2) ? readFileSync27(logPath2, "utf-8") : "";
|
|
20980
21212
|
} catch {
|
|
20981
21213
|
text = "";
|
|
20982
21214
|
}
|
|
@@ -21013,7 +21245,7 @@ function runRequestShapes(form, home = homedir9()) {
|
|
|
21013
21245
|
case "show":
|
|
21014
21246
|
return { stdout: showLog(logPath2), stderr: "", exitCode: 0 };
|
|
21015
21247
|
case "clear":
|
|
21016
|
-
if (
|
|
21248
|
+
if (existsSync16(logPath2)) {
|
|
21017
21249
|
writeFileSync10(logPath2, "", { mode: 384 });
|
|
21018
21250
|
ownerOnly(logPath2, 384);
|
|
21019
21251
|
}
|
|
@@ -21084,19 +21316,23 @@ function reportCodexVersion(deps = {}) {
|
|
|
21084
21316
|
function shortId(id) {
|
|
21085
21317
|
return id.length > 10 ? `${id.slice(0, 8)}\u2026` : id;
|
|
21086
21318
|
}
|
|
21087
|
-
var USAGE = ` ${
|
|
21319
|
+
var USAGE = ` ${ownCommand()} [install] [target-repo] [engagement-id] [self-build|client]
|
|
21088
21320
|
install into target (default: the current directory)
|
|
21089
21321
|
--yes / -y: use this machine's saved sign-in without being asked
|
|
21090
|
-
${
|
|
21091
|
-
${
|
|
21322
|
+
${ownCommand()} check-drift <target-repo>
|
|
21323
|
+
${ownCommand()} build-record <phase> [--repo <root>]
|
|
21092
21324
|
fetch a closed phase's Build Record and write it into docs/build-records/
|
|
21093
|
-
${
|
|
21325
|
+
${ownCommand()} open-phase <phase|--none> --actor "\u2026" (--evidence "\u2026" | --override --reason "\u2026" | --sorting <id> | --size <id>) [--repo <root>]
|
|
21094
21326
|
${SIZE_USAGE}
|
|
21095
21327
|
size a new project at its first sitting, or move its size up
|
|
21096
|
-
${
|
|
21328
|
+
${REGRESSION_USAGE}
|
|
21329
|
+
set how many phases may close between regression phases
|
|
21330
|
+
${CREW_USAGE}
|
|
21331
|
+
list the crew this project may use
|
|
21332
|
+
${ownCommand()} close-phase <phase> --verdict <clean|defects> --actor "\u2026" [--finding "\u2026"]\u2026 [--details] [--repo <root>]
|
|
21097
21333
|
[--thesis "\u2026"] [--outcome "\u2026"] [--deviation "\u2026"]\u2026
|
|
21098
21334
|
open the owner's review of the phase; it closes when the owner has decided every part
|
|
21099
|
-
${
|
|
21335
|
+
${ownCommand()} system [--repo <root>]
|
|
21100
21336
|
check docs/system.json, the description of your system, and send it to Halfcycle
|
|
21101
21337
|
${DRAW_USAGE}
|
|
21102
21338
|
draw the architecture picture, or one phase's plan, from docs/system.json into docs/diagrams/
|
|
@@ -21122,9 +21358,9 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
|
|
|
21122
21358
|
many may start right now
|
|
21123
21359
|
${ROUTE_USAGE}
|
|
21124
21360
|
say how to start a task of a phase's plan: which crew member, and on which tier
|
|
21125
|
-
${
|
|
21361
|
+
${ownCommand()} preview [folder]
|
|
21126
21362
|
show what setting up this folder would add, change, remove or leave alone; writes nothing
|
|
21127
|
-
${
|
|
21363
|
+
${ownCommand()} doctor
|
|
21128
21364
|
check this folder: set up, signed in, connected, checks running, up to date; says what to do for each one that is not; changes nothing
|
|
21129
21365
|
${REQUEST_SHAPES_USAGE}
|
|
21130
21366
|
write down the shape of every request this computer sends to Halfcycle, never what is in
|
|
@@ -21132,11 +21368,11 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
|
|
|
21132
21368
|
${RESTORE_USAGE}
|
|
21133
21369
|
list the copies Halfcycle saved before a change it let through, or bring one back; never
|
|
21134
21370
|
replaces a file or a line of work that exists now
|
|
21135
|
-
${
|
|
21371
|
+
${ownCommand()} allow [--remove]
|
|
21136
21372
|
let your coding agent work without asking on this computer, for tests, builds, installs, local
|
|
21137
21373
|
tools, files and local git; pushing, publishing, deploying, deleting and GitHub still ask;
|
|
21138
21374
|
--remove takes out what this added and keeps everything of yours
|
|
21139
|
-
${
|
|
21375
|
+
${ownCommand()} uninstall [${REMOVE_CREDENTIAL_FLAG}]
|
|
21140
21376
|
take out what the installer added to this project, keeping anything you changed
|
|
21141
21377
|
${REMOVE_CREDENTIAL_FLAG}: also remove this machine's credential for the project, which every
|
|
21142
21378
|
checkout and worktree of that project on this machine shares
|
|
@@ -21149,7 +21385,7 @@ var USAGE = ` ${HALFCYCLE_COMMAND} [install] [target-repo] [engagement-id] [sel
|
|
|
21149
21385
|
var RETIRED_VERBS = /* @__PURE__ */ new Map([
|
|
21150
21386
|
["ci", '"ci" was removed \u2014 CI setup is part of your project, not Halfcycle']
|
|
21151
21387
|
]);
|
|
21152
|
-
var PROJECT_RESET_LINE =
|
|
21388
|
+
var PROJECT_RESET_LINE = `To start your own project here, delete the .halfcycle folder and run "${ownBareCommand()}" again.`;
|
|
21153
21389
|
function selfBuildNotOwnerLine(account) {
|
|
21154
21390
|
return `Halfcycle's own project can only be joined by its owner. This computer is signed in as ${account}. Nothing was written.`;
|
|
21155
21391
|
}
|
|
@@ -21302,7 +21538,7 @@ ${USAGE}`);
|
|
|
21302
21538
|
if (cmd === "allow") {
|
|
21303
21539
|
const extra = rest.filter((word, i) => word !== "--remove" && word !== "--repo" && rest[i - 1] !== "--repo");
|
|
21304
21540
|
if (extra.length > 0) {
|
|
21305
|
-
process.stderr.write(`halfcycle allow: usage: ${
|
|
21541
|
+
process.stderr.write(`halfcycle allow: usage: ${ownCommand()} allow [--remove]
|
|
21306
21542
|
`);
|
|
21307
21543
|
process.exitCode = 2;
|
|
21308
21544
|
return;
|
|
@@ -21318,7 +21554,7 @@ ${USAGE}`);
|
|
|
21318
21554
|
if (installArm) {
|
|
21319
21555
|
const engagementIdArg = positionals[1];
|
|
21320
21556
|
const engagementTypeRaw = positionals[2] ?? "client";
|
|
21321
|
-
if (!
|
|
21557
|
+
if (!existsSync17(targetRepo)) {
|
|
21322
21558
|
process.stderr.write(`${folderMissingRefusal(targetRepo)}
|
|
21323
21559
|
`);
|
|
21324
21560
|
process.exit(1);
|
|
@@ -21544,18 +21780,18 @@ ${PROJECT_RESET_LINE}
|
|
|
21544
21780
|
`);
|
|
21545
21781
|
}
|
|
21546
21782
|
} else if (probe.slow === true) {
|
|
21547
|
-
process.stdout.write(`${SLOW_SERVICE_LINE}${
|
|
21783
|
+
process.stdout.write(`${SLOW_SERVICE_LINE}${doctorLine(ownVersion())}
|
|
21548
21784
|
`);
|
|
21549
21785
|
} else if (probe.credentialRefused) {
|
|
21550
21786
|
process.stdout.write(`[halfcycle] Method delivery (MCP): ${credential.mcpUrl} \u2014 ${probe.problem}.
|
|
21551
|
-
[halfcycle] The address is right, but Halfcycle commands will not work in this project until it accepts this machine's credential. Re-run "
|
|
21552
|
-
${
|
|
21787
|
+
[halfcycle] The address is right, but Halfcycle commands will not work in this project until it accepts this machine's credential. Re-run "${ownBareCommand()}" in this folder; if it says this again, the refusal is on Halfcycle's side, not in this project.
|
|
21788
|
+
${doctorLine(ownVersion())}
|
|
21553
21789
|
`);
|
|
21554
21790
|
} else {
|
|
21555
21791
|
const otherAddress = controlOriginConfirmed ? `The other address Halfcycle uses answered fine; this is the only one that did not. ` : `This run could not reach the Halfcycle service at all just now, so this may simply be offline. `;
|
|
21556
21792
|
process.stdout.write(`[halfcycle] Method delivery (MCP): ${credential.mcpUrl} \u2014 NOT reachable: ${probe.problem}.
|
|
21557
21793
|
[halfcycle] Halfcycle commands will not work in this project until that address answers. ${otherAddress}Run this installer again once the service is up.
|
|
21558
|
-
${
|
|
21794
|
+
${doctorLine(ownVersion())}
|
|
21559
21795
|
`);
|
|
21560
21796
|
}
|
|
21561
21797
|
}
|
|
@@ -21589,6 +21825,23 @@ ${DOCTOR_LINE}
|
|
|
21589
21825
|
if (result.notShared)
|
|
21590
21826
|
process.stdout.write(`${renderNotShared(result.notShared)}
|
|
21591
21827
|
`);
|
|
21828
|
+
const consoleUrl = credential?.consoleUrl ?? (reusedWithoutFreshAnswer ? readBundlePin(targetRepo)?.projectPage : void 0);
|
|
21829
|
+
if (consoleUrl !== void 0) {
|
|
21830
|
+
const colour = decideColour(currentEnvironment(quiet));
|
|
21831
|
+
process.stdout.write(`${ink("key", `[halfcycle] Your project's console: ${consoleUrl}`, colour)}
|
|
21832
|
+
`);
|
|
21833
|
+
}
|
|
21834
|
+
const methodVersion = credential?.methodVersion ?? (reusedWithoutFreshAnswer ? readBundlePin(targetRepo)?.methodVersion : void 0);
|
|
21835
|
+
if (methodVersion !== void 0) {
|
|
21836
|
+
process.stdout.write(`[halfcycle] Method version: ${methodVersion}
|
|
21837
|
+
`);
|
|
21838
|
+
}
|
|
21839
|
+
process.stdout.write(`[halfcycle] ${localAllowClosingLine(targetRepo, wantsLocalAllow)}
|
|
21840
|
+
`);
|
|
21841
|
+
if (checks.gitInstalled) {
|
|
21842
|
+
process.stdout.write(`[halfcycle] Git for Windows is installed. Open a new terminal before starting Claude Code.
|
|
21843
|
+
`);
|
|
21844
|
+
}
|
|
21592
21845
|
if (credential && isGitWorkTree(targetRepo)) {
|
|
21593
21846
|
process.stdout.write(`[halfcycle] Commit the files Halfcycle set up here, including ${BUNDLE_PIN_REL} and ${PROJECT_IDENTITY_REL}, so every copy of this project joins it and runs the same checks.
|
|
21594
21847
|
`);
|
|
@@ -21607,25 +21860,12 @@ ${DOCTOR_LINE}
|
|
|
21607
21860
|
process.stdout.write(`${codexLine2}
|
|
21608
21861
|
`);
|
|
21609
21862
|
}
|
|
21610
|
-
|
|
21611
|
-
if (consoleUrl !== void 0) {
|
|
21612
|
-
const colour = decideColour(currentEnvironment(quiet));
|
|
21613
|
-
process.stdout.write(`${ink("key", `[halfcycle] Your project's console: ${consoleUrl}`, colour)}
|
|
21614
|
-
`);
|
|
21615
|
-
}
|
|
21616
|
-
const methodVersion = credential?.methodVersion ?? (reusedWithoutFreshAnswer ? readBundlePin(targetRepo)?.methodVersion : void 0);
|
|
21617
|
-
if (methodVersion !== void 0) {
|
|
21618
|
-
process.stdout.write(`[halfcycle] Method version: ${methodVersion}
|
|
21619
|
-
`);
|
|
21620
|
-
}
|
|
21621
|
-
process.stdout.write(`[halfcycle] ${localAllowClosingLine(targetRepo, wantsLocalAllow)}
|
|
21863
|
+
process.stdout.write(`[halfcycle] Start Claude Code from this project folder: ${targetRepo}
|
|
21622
21864
|
`);
|
|
21623
|
-
if (
|
|
21624
|
-
process.stdout.write(`[halfcycle]
|
|
21865
|
+
if (credential) {
|
|
21866
|
+
process.stdout.write(`[halfcycle] After that, Halfcycle asks what to call your product and what you want to build, then sets the project up.
|
|
21625
21867
|
`);
|
|
21626
21868
|
}
|
|
21627
|
-
process.stdout.write(`[halfcycle] Start Claude Code from this project folder: ${targetRepo}
|
|
21628
|
-
`);
|
|
21629
21869
|
process.exit(0);
|
|
21630
21870
|
} catch (err) {
|
|
21631
21871
|
const stop = (message) => {
|
|
@@ -21641,7 +21881,7 @@ ${kept}`);
|
|
|
21641
21881
|
}
|
|
21642
21882
|
const fsCode = typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
21643
21883
|
if (fsCode !== void 0) {
|
|
21644
|
-
stop(`halfcycle: could not write to ${targetRepo} \u2014 check that you can write to this folder and run "
|
|
21884
|
+
stop(`halfcycle: could not write to ${targetRepo} \u2014 check that you can write to this folder and run "${ownBareCommand()}" again.`);
|
|
21645
21885
|
return;
|
|
21646
21886
|
}
|
|
21647
21887
|
stop(`halfcycle install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -21978,6 +22218,42 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
|
|
|
21978
22218
|
process.exitCode = run.exitCode;
|
|
21979
22219
|
return;
|
|
21980
22220
|
}
|
|
22221
|
+
if (cmd === "regression") {
|
|
22222
|
+
const parsed = parseRegressionArgs(rest);
|
|
22223
|
+
if (!parsed.ok) {
|
|
22224
|
+
process.stderr.write(parsed.line);
|
|
22225
|
+
process.exitCode = parsed.exitCode;
|
|
22226
|
+
return;
|
|
22227
|
+
}
|
|
22228
|
+
const run = await runRegression(parsed.args.repoRoot ?? process.cwd(), parsed.args, {
|
|
22229
|
+
readCredential: (root) => readRepoCredential(root),
|
|
22230
|
+
fetch: (input, init) => fetch(input, init)
|
|
22231
|
+
});
|
|
22232
|
+
if (run.stdout !== "")
|
|
22233
|
+
process.stdout.write(run.stdout);
|
|
22234
|
+
if (run.stderr !== "")
|
|
22235
|
+
process.stderr.write(run.stderr);
|
|
22236
|
+
process.exitCode = run.exitCode;
|
|
22237
|
+
return;
|
|
22238
|
+
}
|
|
22239
|
+
if (cmd === "crew") {
|
|
22240
|
+
const parsed = parseCrewArgs(rest);
|
|
22241
|
+
if (!parsed.ok) {
|
|
22242
|
+
process.stderr.write(parsed.line);
|
|
22243
|
+
process.exitCode = parsed.exitCode;
|
|
22244
|
+
return;
|
|
22245
|
+
}
|
|
22246
|
+
const run = await runCrew(parsed.repoRoot ?? process.cwd(), {
|
|
22247
|
+
readCredential: (root) => readRepoCredential(root),
|
|
22248
|
+
fetch: (input, init) => fetch(input, init)
|
|
22249
|
+
});
|
|
22250
|
+
if (run.stdout !== "")
|
|
22251
|
+
process.stdout.write(run.stdout);
|
|
22252
|
+
if (run.stderr !== "")
|
|
22253
|
+
process.stderr.write(run.stderr);
|
|
22254
|
+
process.exitCode = run.exitCode;
|
|
22255
|
+
return;
|
|
22256
|
+
}
|
|
21981
22257
|
if (cmd === "size") {
|
|
21982
22258
|
const parsed = parseSizeArgs(rest);
|
|
21983
22259
|
if (!parsed.ok) {
|
|
@@ -22002,8 +22278,8 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
|
|
|
22002
22278
|
const phaseArg = firstPositional(rest);
|
|
22003
22279
|
if (!toProjectScope && !phaseArg) {
|
|
22004
22280
|
process.stderr.write(`halfcycle open-phase: name the phase to open, or pass --none to return to project scope.
|
|
22005
|
-
${
|
|
22006
|
-
${
|
|
22281
|
+
${ownCommand()} open-phase <phase> --actor "you" --evidence "<what shows this phase is ready to start>"
|
|
22282
|
+
${ownCommand()} open-phase <phase> --actor "you" --override --reason "${REASON_PLACEHOLDER["open-phase"]}"
|
|
22007
22283
|
`);
|
|
22008
22284
|
process.exit(2);
|
|
22009
22285
|
return;
|
|
@@ -22055,9 +22331,20 @@ halfcycle build-record: until ${obtained.stampFailure.path} can be written, the
|
|
|
22055
22331
|
} else {
|
|
22056
22332
|
entry = { decision: "entry-condition-met", actor, evidence: flagValue(rest, "--evidence") ?? "" };
|
|
22057
22333
|
}
|
|
22334
|
+
const regressionArg = rest.includes("--regression");
|
|
22335
|
+
if (regressionArg && toProjectScope) {
|
|
22336
|
+
const refusal3 = openPhaseRequestSchema.safeParse({ phase: null, entry, regression: true });
|
|
22337
|
+
if (!refusal3.success) {
|
|
22338
|
+
const said = refusal3.error.issues.map((issue) => issue.message).join("; ");
|
|
22339
|
+
process.stderr.write(`halfcycle open-phase: --regression ${said}. Nothing was sent.
|
|
22340
|
+
`);
|
|
22341
|
+
process.exit(2);
|
|
22342
|
+
return;
|
|
22343
|
+
}
|
|
22344
|
+
}
|
|
22058
22345
|
try {
|
|
22059
22346
|
const credential = readRepoCredential(repoRoot);
|
|
22060
|
-
const opened = await openPhase(credential, toProjectScope ? null : phaseArg, entry, void 0, repoRoot, sortingArg, sizeArg);
|
|
22347
|
+
const opened = await openPhase(credential, toProjectScope ? null : phaseArg, entry, void 0, repoRoot, sortingArg, sizeArg, regressionArg);
|
|
22061
22348
|
process.stdout.write(`[halfcycle] ${describeOpen(opened)}
|
|
22062
22349
|
`);
|
|
22063
22350
|
const startCommitLine = describeStartCommit(opened.phase, opened.startCommitSent, opened.startCommit);
|
|
@@ -22123,8 +22410,8 @@ halfcycle open-phase: until ${opened.stampFailure.path} can be written, guard ev
|
|
|
22123
22410
|
const phaseArg = firstPositional(rest);
|
|
22124
22411
|
if (!phaseArg) {
|
|
22125
22412
|
process.stderr.write(`halfcycle close-phase: name the phase whose review you are opening.
|
|
22126
|
-
${
|
|
22127
|
-
${
|
|
22413
|
+
${ownCommand()} close-phase <phase> --verdict clean --actor "you"
|
|
22414
|
+
${ownCommand()} close-phase <phase> --verdict defects --actor "you" --finding "<what is still wrong>"
|
|
22128
22415
|
`);
|
|
22129
22416
|
process.exit(2);
|
|
22130
22417
|
return;
|
|
@@ -22334,7 +22621,7 @@ function firstPositional(argv) {
|
|
|
22334
22621
|
const arg = argv[i];
|
|
22335
22622
|
if (arg.startsWith("--")) {
|
|
22336
22623
|
const next = argv[i + 1];
|
|
22337
|
-
if (next !== void 0 && !next.startsWith("--"))
|
|
22624
|
+
if (arg !== "--regression" && next !== void 0 && !next.startsWith("--"))
|
|
22338
22625
|
i++;
|
|
22339
22626
|
continue;
|
|
22340
22627
|
}
|