halfcycle 0.4.1 → 0.4.3
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/README.md +1 -1
- package/bin/bin.bundle.mjs +51 -24
- package/dist/claude-version-probe.d.ts.map +1 -1
- package/dist/cli.js +197 -98
- package/dist/cli.js.map +1 -1
- package/dist/confirm-identity.d.ts +31 -36
- package/dist/confirm-identity.d.ts.map +1 -1
- package/dist/create-engagement.d.ts +6 -4
- package/dist/create-engagement.d.ts.map +1 -1
- package/dist/device-signin.d.ts +35 -2
- package/dist/device-signin.d.ts.map +1 -1
- package/dist/identity.d.ts +7 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -25
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +89 -11
- package/dist/install.d.ts.map +1 -1
- package/dist/merge-settings.d.ts +35 -16
- package/dist/merge-settings.d.ts.map +1 -1
- package/dist/own-engagement.d.ts +8 -7
- package/dist/own-engagement.d.ts.map +1 -1
- package/dist/pre-create-checks.d.ts +27 -0
- package/dist/pre-create-checks.d.ts.map +1 -1
- package/dist/uninstall.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -69,15 +69,17 @@
|
|
|
69
69
|
* silence back permanently and for every future question this CLI asks.
|
|
70
70
|
*
|
|
71
71
|
* So what this population gets instead is a record that does not live in scrollback.
|
|
72
|
-
* The account id settled on here is RETURNED to the caller, which writes it
|
|
73
|
-
* committed `.halfcycle/bundle.json`
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
72
|
+
* The account id settled on here is RETURNED to the caller, which on a CREATE writes it
|
|
73
|
+
* into the committed `.halfcycle/bundle.json` as `createdByAccountId` (`writeBundlePin`
|
|
74
|
+
* in `install.ts`; only the create writes it — E1, #1598) — the id and never the email,
|
|
75
|
+
* because that file is committed into the client's repository. A wrong-identity install
|
|
76
|
+
* is then discoverable afterwards, from the repository, by the person whose repository
|
|
77
|
+
* it is. Printing is not recording.
|
|
77
78
|
*
|
|
78
|
-
* The measurement is a TTY plus the absence of a CI marker
|
|
79
|
-
* one home is `device-signin.ts
|
|
80
|
-
*
|
|
79
|
+
* The measurement is `isInteractiveRun` (a TTY plus the absence of a CI marker), whose
|
|
80
|
+
* one home is `device-signin.ts`. It is never inferred from anything else. (The
|
|
81
|
+
* browser handoff does NOT ask it: that refuses only a CI run, since the person
|
|
82
|
+
* finishes in the browser, not at this terminal.)
|
|
81
83
|
*
|
|
82
84
|
* ---------------------------------------------------------------------------
|
|
83
85
|
* ASKING IS BEST-EFFORT ABOUT THE LABEL AND NEVER ABOUT THE GATE. `GET /account` may be
|
|
@@ -131,8 +133,9 @@ export interface ConfirmIdentityDeps extends ResolveOptions {
|
|
|
131
133
|
* every run and it is the wrong thing to put beside the value the reader is being
|
|
132
134
|
* asked to check: a person cannot verify a uuid, and thirty-eight characters of one
|
|
133
135
|
* pushed the email they CAN verify leftward into a longer line. It is not lost —
|
|
134
|
-
* `.halfcycle/bundle.json` records the account
|
|
135
|
-
* copy a support conversation can
|
|
136
|
+
* `.halfcycle/bundle.json` records the account that created the project, which is
|
|
137
|
+
* the copy a support conversation can be pointed at once the terminal is gone.
|
|
138
|
+
* The account a later run acted as is not recorded there (E1, #1598).
|
|
136
139
|
*/
|
|
137
140
|
verbose?: boolean;
|
|
138
141
|
/**
|
|
@@ -227,9 +230,10 @@ export declare function describeAccount(serviceUrl: string, credential: string,
|
|
|
227
230
|
* and a human needs the email. The first half of that is true and the conclusion was
|
|
228
231
|
* wrong: this label's reader is a person deciding whether a project should belong to
|
|
229
232
|
* this account, they cannot verify a uuid, and a uuid printed beside the one value they
|
|
230
|
-
* CAN verify buries it. The id has a better home already —
|
|
231
|
-
* in `.halfcycle/bundle.json`, which outlives the
|
|
232
|
-
* back on screen for the run that
|
|
233
|
+
* CAN verify buries it. The creating account's id has a better home already —
|
|
234
|
+
* `writeBundlePin` records it in `.halfcycle/bundle.json`, which outlives the
|
|
235
|
+
* scrollback — and `--verbose` puts this run's id back on screen for the run that
|
|
236
|
+
* wants it.
|
|
233
237
|
*
|
|
234
238
|
* WITH NO EMAIL the id is all there is, and it is printed: an unnamed account named by
|
|
235
239
|
* its row is better than an unnamed account.
|
|
@@ -238,29 +242,20 @@ export declare function describeAccount(serviceUrl: string, credential: string,
|
|
|
238
242
|
* signed in as'` is a true sentence about an unnamed account; `'unknown'` printed where
|
|
239
243
|
* a name goes reads as a name.
|
|
240
244
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
245
|
+
* THE CALLER DECIDES WHETHER TO PRINT AT ALL, not this function. `bin.ts`'s final
|
|
246
|
+
* report (E8, the wording batch, onboarding fix wave 2) only calls this when `GET
|
|
247
|
+
* /account` actually answered with an account — a network error, a timeout, a 5xx
|
|
248
|
+
* and a reached-but-unidentifiable answer (a 401/403) are all withheld there, rather
|
|
249
|
+
* than printed through a `confirmed` flag here (onboarding fix wave T-16, #1567's
|
|
250
|
+
* original shape, since removed: the flag's one caller was that same report line,
|
|
251
|
+
* and the line it produced — "this run could not reach the service to confirm who
|
|
252
|
+
* that is" — is now simply never printed, which is the more honest of the two). A
|
|
253
|
+
* `storedAccountId` with no `identity` can still reach the "no email" branch below
|
|
254
|
+
* through `confirm-identity.ts`'s OWN prompt line, a different surface with a
|
|
255
|
+
* different, still-true claim: a stored or environment credential's cached id,
|
|
256
|
+
* named while this run's own fresh identity read is still pending.
|
|
246
257
|
*/
|
|
247
|
-
export declare function accountLabel(identity: AccountIdentity | undefined, storedAccountId: string | undefined, verbose?: boolean
|
|
248
|
-
/**
|
|
249
|
-
* Whether this run has a human at a keyboard to ask — the ONE measurement this module
|
|
250
|
-
* uses, so a caller outside `confirmActingIdentity` reads the same fact rather than
|
|
251
|
-
* assuming one.
|
|
252
|
-
*
|
|
253
|
-
* A TTY plus the absence of a CI marker (`isNonInteractive`, whose one home is
|
|
254
|
-
* `device-signin.ts` — the same question the browser handoff already asks). Never
|
|
255
|
-
* inferred from anything else — see this module's own docblock. Walk 5 (2026-09-08)
|
|
256
|
-
* found `identitySwitchRemedy` called with no measurement at all (an implicit "assume
|
|
257
|
-
* interactive"), printing "answer \"s\" when asked" into a run that had, moments
|
|
258
|
-
* earlier in the SAME output, already told the reader there was no terminal to ask at.
|
|
259
|
-
* Exported so `identitySwitchRemedy`'s callers outside `confirmActingIdentity` compute
|
|
260
|
-
* the same fact this function's own `interactive` local does, rather than a second,
|
|
261
|
-
* possibly drifting copy of this expression.
|
|
262
|
-
*/
|
|
263
|
-
export declare function isInteractiveRun(env?: NodeJS.ProcessEnv): boolean;
|
|
258
|
+
export declare function accountLabel(identity: AccountIdentity | undefined, storedAccountId: string | undefined, verbose?: boolean): string;
|
|
264
259
|
/**
|
|
265
260
|
* Read one line from the terminal. Kept out of the decision so the decision is testable.
|
|
266
261
|
* Also the Git for Windows offer's prompt (`pre-create-checks.ts`), so the terminal is
|
|
@@ -326,7 +321,7 @@ export declare function identitySwitchRemedy(home: string | undefined, interacti
|
|
|
326
321
|
*
|
|
327
322
|
* Returns the credential to use, which is a DIFFERENT one from the one passed in when
|
|
328
323
|
* the person switched account, and which carries the ACCOUNT ID this run acts as so the
|
|
329
|
-
* caller can record it on disk (`writeBundlePin`). Throws `SignInRefused` when they
|
|
324
|
+
* caller can record it on disk when it creates the project (`writeBundlePin`). Throws `SignInRefused` when they
|
|
330
325
|
* stop, which the CLI already prints as a refusal rather than as a crash: nothing has
|
|
331
326
|
* been created and nothing has been written at the point this is called, and that is
|
|
332
327
|
* what makes stopping a real option rather than an apology.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-identity.d.ts","sourceRoot":"","sources":["../src/confirm-identity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"confirm-identity.d.ts","sourceRoot":"","sources":["../src/confirm-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -343,10 +343,12 @@ export declare function joinEngagement(baseUrl: string, engagementId: string, cr
|
|
|
343
343
|
* A project that belongs to another account is a `CreateEngagementRefused` carrying
|
|
344
344
|
* the declared not-this-account `reason`; telling it apart is `own-engagement.ts`'s.
|
|
345
345
|
*
|
|
346
|
-
* Its failure sentences say
|
|
347
|
-
* created: an answer lost after control created the project is exactly
|
|
348
|
-
* route makes recoverable, so "nothing was created" could be false.
|
|
349
|
-
* that
|
|
346
|
+
* Its failure sentences say Halfcycle did not finish setting up the folder, never that
|
|
347
|
+
* no project was created: an answer lost after control created the project is exactly
|
|
348
|
+
* the case this route makes recoverable, so "nothing was created" could be false. Nor
|
|
349
|
+
* that the folder "was not set up" (FW-1): its id file is already on disk, and the
|
|
350
|
+
* walks read that sentence as false. A re-run gets that project back, which `bin.ts`
|
|
351
|
+
* says once after every stopped install (`KEPT_FOLDER_ID_LINE`).
|
|
350
352
|
*
|
|
351
353
|
* @param derivedName The guessed name (`deriveProjectName`), or `''` when there is none —
|
|
352
354
|
* the field is required, and control names an empty one by default.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-engagement.d.ts","sourceRoot":"","sources":["../src/create-engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAKH;;oCAEoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;;;;OAkBG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,6CAA6C;aAC7B,MAAM,EAAE,MAAM;IAC9B,8DAA8D;aAC9C,aAAa,EAAE,MAAM;IACrC,+DAA+D;aAC/C,SAAS,EAAE,MAAM,GAAG,SAAS;IAC7C;;;;;;;;;;;;OAYG;aACa,MAAM,EAAE,MAAM,GAAG,SAAS;;IAnB1C,6CAA6C;IAC7B,MAAM,EAAE,MAAM;IAC9B,8DAA8D;IAC9C,aAAa,EAAE,MAAM;IACrC,+DAA+D;IAC/C,SAAS,EAAE,MAAM,GAAG,SAAS;IAC7C;;;;;;;;;;;;OAYG;IACa,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM;IAMjB;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,WAAW,IAAI,OAAO,CAGzB;CACF;AA8ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED
|
|
1
|
+
{"version":3,"file":"create-engagement.d.ts","sourceRoot":"","sources":["../src/create-engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAKH;;oCAEoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;;;;OAkBG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,6CAA6C;aAC7B,MAAM,EAAE,MAAM;IAC9B,8DAA8D;aAC9C,aAAa,EAAE,MAAM;IACrC,+DAA+D;aAC/C,SAAS,EAAE,MAAM,GAAG,SAAS;IAC7C;;;;;;;;;;;;OAYG;aACa,MAAM,EAAE,MAAM,GAAG,SAAS;;IAnB1C,6CAA6C;IAC7B,MAAM,EAAE,MAAM;IAC9B,8DAA8D;IAC9C,aAAa,EAAE,MAAM;IACrC,+DAA+D;IAC/C,SAAS,EAAE,MAAM,GAAG,SAAS;IAC7C;;;;;;;;;;;;OAYG;IACa,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM;IAMjB;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,WAAW,IAAI,OAAO,CAGzB;CACF;AA8ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAgB5B"}
|
package/dist/device-signin.d.ts
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* ---------------------------------------------------------------------------
|
|
40
40
|
* FOUR WAYS THIS ENDS, and each is a different sentence and a non-zero exit:
|
|
41
41
|
*
|
|
42
|
-
* `declined` — a human refused at the page.
|
|
42
|
+
* `declined` — a human refused at the page.
|
|
43
43
|
* `expired` — the code's window closed on CONTROL's clock before anyone acted.
|
|
44
44
|
* `unknown` — the server is not waiting on this code (never minted, or spent).
|
|
45
45
|
* unreachable — the plane stopped answering mid-poll. This one is OURS to word,
|
|
@@ -95,6 +95,15 @@ export declare const SIGN_IN_TIMEOUT_MESSAGE: string;
|
|
|
95
95
|
* burns the job's time and then reports something that reads like a platform
|
|
96
96
|
* outage. It refuses immediately instead, and names the ONE thing a workflow can
|
|
97
97
|
* actually do.
|
|
98
|
+
*
|
|
99
|
+
* "Non-interactive" here is a CI run (`isNonInteractive`), not `isInteractiveRun`: a
|
|
100
|
+
* piped stdin with no `CI` still gets the browser sign-in, because that is how an
|
|
101
|
+
* agent runs the first install (its shell has no terminal; the person at the screen
|
|
102
|
+
* finishes in the browser). Onboarding fix wave FW-1 tried gating this on
|
|
103
|
+
* `isInteractiveRun` and reverted it (FW-1a). The sentence says nothing about what
|
|
104
|
+
* was or was not created: on the create arm the folder id is already on disk, and
|
|
105
|
+
* `bin.ts` says the one true thing about that after every refusal
|
|
106
|
+
* (`KEPT_FOLDER_ID_LINE`).
|
|
98
107
|
*/
|
|
99
108
|
export declare const SIGN_IN_NON_INTERACTIVE_MESSAGE: string;
|
|
100
109
|
/** Everything this module touches that a test may need to hold still. */
|
|
@@ -274,8 +283,32 @@ export interface BrowserOpenAttempt {
|
|
|
274
283
|
* unchanged.
|
|
275
284
|
*/
|
|
276
285
|
export declare function openBrowser(url: string, env?: NodeJS.ProcessEnv, os?: string, spawnImpl?: typeof spawn): Promise<BrowserOpenAttempt>;
|
|
277
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* True iff the environment marks this as a CI run — the browser sign-in's gate, and one
|
|
288
|
+
* half of `isInteractiveRun`.
|
|
289
|
+
*/
|
|
278
290
|
export declare function isNonInteractive(env?: NodeJS.ProcessEnv): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Whether this run has a human at a keyboard to ask — THE ONE MEASUREMENT, read by
|
|
293
|
+
* every caller that needs to ASK a question: the identity confirmation
|
|
294
|
+
* (`confirm-identity.ts`) and the Git for Windows offer (`pre-create-checks.ts`).
|
|
295
|
+
*
|
|
296
|
+
* A TTY plus the absence of a CI marker (`isNonInteractive`). Never inferred from
|
|
297
|
+
* anything else. Walk 5 (2026-09-08) found `identitySwitchRemedy` called with no
|
|
298
|
+
* measurement at all (an implicit "assume interactive"), printing "answer \"s\" when
|
|
299
|
+
* asked" into a run that had, moments earlier in the SAME output, already told the
|
|
300
|
+
* reader there was no terminal to ask at.
|
|
301
|
+
*
|
|
302
|
+
* NOT the browser sign-in's gate. `signIn` asks nothing at the terminal — the person
|
|
303
|
+
* finishes in the browser — so it refuses only a CI run (`isNonInteractive`). An agent
|
|
304
|
+
* running the first install through its shell has no terminal on stdin, and its
|
|
305
|
+
* person signs in at the browser; gating `signIn` on this would refuse that install
|
|
306
|
+
* (onboarding fix wave FW-1a reverted exactly that).
|
|
307
|
+
*
|
|
308
|
+
* It lives here, beside `isNonInteractive`, so `confirm-identity.ts` (which imports
|
|
309
|
+
* this module) and `pre-create-checks.ts` read one definition, not two copies.
|
|
310
|
+
*/
|
|
311
|
+
export declare function isInteractiveRun(env?: NodeJS.ProcessEnv): boolean;
|
|
279
312
|
/** `POST /auth/device/start` — mint a code. Throws `SignInRefused` on a refusal. */
|
|
280
313
|
export declare function startDeviceAuth(serviceUrl: string): Promise<DeviceAuthStartResponse>;
|
|
281
314
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-signin.d.ts","sourceRoot":"","sources":["../src/device-signin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAKL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAG3B;;;;;;;;GAQG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAEpB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM;CAKlB;AAED,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,
|
|
1
|
+
{"version":3,"file":"device-signin.d.ts","sourceRoot":"","sources":["../src/device-signin.ts"],"names":[],"mappings":"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"}
|
package/dist/identity.d.ts
CHANGED
|
@@ -74,6 +74,13 @@ export declare function readRootCommit(targetRepoRoot: string): string | null;
|
|
|
74
74
|
* AC8) — outside a repository there is nothing to commit them to.
|
|
75
75
|
*/
|
|
76
76
|
export declare function isGitWorkTree(targetRepoRoot: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Can this machine run `git` at all? Asked only once `isGitWorkTree` has said no,
|
|
79
|
+
* because `git()` above answers `null` for "not a repository" and for "no git" alike —
|
|
80
|
+
* and the advice differs: `git init` cannot help someone who has no Git (the macOS and
|
|
81
|
+
* Linux walks' B5, onboarding fix wave FW-1).
|
|
82
|
+
*/
|
|
83
|
+
export declare function gitIsInstalled(): boolean;
|
|
77
84
|
/** Read the `origin` remote URL. */
|
|
78
85
|
export declare function readRemote(targetRepoRoot: string): string | null;
|
|
79
86
|
/**
|
package/dist/identity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,qBAAqB,QAGF,CAAC;AAEjC,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AA6BD,gFAAgF;AAChF,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,UAAU,CAItD;AAqBD,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,oCAAoC;AACpC,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrE;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,CAyBvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAazG;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAE1E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAiBtE"}
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,qBAAqB,QAGF,CAAC;AAEjC,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AA6BD,gFAAgF;AAChF,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,UAAU,CAItD;AAqBD,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED,oCAAoC;AACpC,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrE;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,CAyBvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAazG;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAE1E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAiBtE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* INV-001: imports only from @halfcycle/core, @halfcycle/events, or Node builtins.
|
|
10
10
|
*/
|
|
11
|
-
export { install, checkDrift, readBundlePin, ZONE_B_DIR } from './install.js';
|
|
11
|
+
export { install, checkDrift, readBundlePin, pinCreatedByAccountId, ZONE_B_DIR } from './install.js';
|
|
12
12
|
export type { InstallOptions, InstallResult, BundlePin, EngagementType, DeviceCredential } from './install.js';
|
|
13
13
|
export { mintOrReadIdentity, readRootCommit, readRemote } from './identity.js';
|
|
14
14
|
export type { ProjectIdentity } from './identity.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrG,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACnF,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAyBhE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnG,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAI1F,cAAc,kBAAkB,CAAC;AAKjC,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1650,8 +1650,20 @@ function projectPageUrl(engagementId) {
|
|
|
1650
1650
|
}
|
|
1651
1651
|
|
|
1652
1652
|
// dist/merge-settings.js
|
|
1653
|
+
function isHalfcycleCommand(hook, halfcycleCommands) {
|
|
1654
|
+
if (typeof hook !== "object" || hook === null)
|
|
1655
|
+
return false;
|
|
1656
|
+
const command = hook.command;
|
|
1657
|
+
return typeof command === "string" && halfcycleCommands.has(command);
|
|
1658
|
+
}
|
|
1653
1659
|
function isHalfcycleEntry(entry, halfcycleCommands) {
|
|
1654
|
-
return entry.hooks.some((h) =>
|
|
1660
|
+
return entry.hooks.some((h) => isHalfcycleCommand(h, halfcycleCommands));
|
|
1661
|
+
}
|
|
1662
|
+
function withoutHalfcycleCommands(entry, halfcycleCommands) {
|
|
1663
|
+
if (!isHalfcycleEntry(entry, halfcycleCommands))
|
|
1664
|
+
return entry;
|
|
1665
|
+
const remaining = entry.hooks.filter((h) => !isHalfcycleCommand(h, halfcycleCommands));
|
|
1666
|
+
return remaining.length > 0 ? { ...entry, hooks: remaining } : null;
|
|
1655
1667
|
}
|
|
1656
1668
|
function mergeSettings(existing, generated) {
|
|
1657
1669
|
const merged = { ...existing };
|
|
@@ -1670,7 +1682,7 @@ function mergeSettings(existing, generated) {
|
|
|
1670
1682
|
const mergedHooks = {};
|
|
1671
1683
|
const events = /* @__PURE__ */ new Set([...Object.keys(existingHooks), ...Object.keys(generatedHooks)]);
|
|
1672
1684
|
for (const event of events) {
|
|
1673
|
-
const developerEntries = (existingHooks[event] ?? []).
|
|
1685
|
+
const developerEntries = (existingHooks[event] ?? []).flatMap((entry) => withoutHalfcycleCommands(entry, halfcycleCommands) ?? []);
|
|
1674
1686
|
const halfcycleEntries = generatedHooks[event] ?? [];
|
|
1675
1687
|
mergedHooks[event] = [...developerEntries, ...halfcycleEntries];
|
|
1676
1688
|
}
|
|
@@ -1846,6 +1858,7 @@ function closedWriteSet() {
|
|
|
1846
1858
|
ownedDirs,
|
|
1847
1859
|
hookCommands: new Set(HALFCYCLE_HOOK_COMMANDS),
|
|
1848
1860
|
denyRuleSha256: new Set([...DENY_PATTERNS, ...RETIRED_DENY_PATTERNS].map((rule) => sha256Hex(rule))),
|
|
1861
|
+
settingsSchema: SETTINGS_SCHEMA,
|
|
1849
1862
|
gitignoreLines: /* @__PURE__ */ new Set([GITIGNORE_HEADER, ...REQUIRED_GITIGNORE_ENTRIES, LEGACY_ENV_LOCAL_REL]),
|
|
1850
1863
|
gitattributesLines: /* @__PURE__ */ new Set([
|
|
1851
1864
|
GITATTRIBUTES_HEADER,
|
|
@@ -1867,6 +1880,11 @@ function sha256OfWhatThisVersionWrites(commands) {
|
|
|
1867
1880
|
fromFile(CAPTURED_INDEX_REL, capturedIndexScaffold());
|
|
1868
1881
|
return out;
|
|
1869
1882
|
}
|
|
1883
|
+
function bytesAreOurs(path, bytes, recorded, writeSet) {
|
|
1884
|
+
const hash = sha256Hex(bytes);
|
|
1885
|
+
const recordedHash = recorded !== void 0 && "sha256" in recorded ? recorded.sha256 : void 0;
|
|
1886
|
+
return hash === recordedHash || hash === writeSet.generatedSha256.get(path);
|
|
1887
|
+
}
|
|
1870
1888
|
function readPreviousManifest(targetRepo) {
|
|
1871
1889
|
try {
|
|
1872
1890
|
return parseManifest(readFileSync4(join5(targetRepo, INSTALL_MANIFEST_REL), "utf-8"));
|
|
@@ -2015,7 +2033,7 @@ function readPluginManifest() {
|
|
|
2015
2033
|
const raw = readFileSync4(manifestPath, "utf-8");
|
|
2016
2034
|
return JSON.parse(raw);
|
|
2017
2035
|
}
|
|
2018
|
-
function copyManifestCommands(manifest, targetRepo, report, draft) {
|
|
2036
|
+
function copyManifestCommands(manifest, targetRepo, report, draft, writeSet) {
|
|
2019
2037
|
for (const cmd of manifest.commands ?? []) {
|
|
2020
2038
|
const srcFile = join5(BUNDLE_ROOT, ".claude-plugin", cmd.path);
|
|
2021
2039
|
if (!existsSync3(srcFile)) {
|
|
@@ -2024,8 +2042,13 @@ function copyManifestCommands(manifest, targetRepo, report, draft) {
|
|
|
2024
2042
|
const rel = commandStubRel(cmd.name);
|
|
2025
2043
|
const destFile = join5(targetRepo, rel);
|
|
2026
2044
|
const content = readFileSync4(srcFile, "utf-8");
|
|
2027
|
-
const
|
|
2045
|
+
const recorded = draft.files.get(rel);
|
|
2046
|
+
const { outcome, replacedExisting } = writeCollisionSafe(destFile, targetRepo, content, (current) => bytesAreOurs(rel, current, recorded, writeSet));
|
|
2028
2047
|
record(report, outcome, rel);
|
|
2048
|
+
if (replacedExisting)
|
|
2049
|
+
report.replacedPaths.push(rel);
|
|
2050
|
+
if (outcome === "collided" && recorded !== void 0)
|
|
2051
|
+
report.editedCollidedPaths.push(rel);
|
|
2029
2052
|
noteFile(draft, targetRepo, rel, outcome);
|
|
2030
2053
|
}
|
|
2031
2054
|
}
|
|
@@ -2040,18 +2063,23 @@ function writeAllowlisted(targetAbsPath, targetRepoRoot, content, writtenPaths)
|
|
|
2040
2063
|
writeFileSync4(targetAbsPath, content, "utf-8");
|
|
2041
2064
|
writtenPaths.push(rel);
|
|
2042
2065
|
}
|
|
2043
|
-
function writeCollisionSafe(targetAbsPath, targetRepoRoot, content) {
|
|
2066
|
+
function writeCollisionSafe(targetAbsPath, targetRepoRoot, content, isOurs) {
|
|
2044
2067
|
const rel = relative(targetRepoRoot, targetAbsPath).replace(/\\/g, "/");
|
|
2045
2068
|
if (!isAllowlisted(rel)) {
|
|
2046
2069
|
throw new Error(`[halfcycle] Write-allowlist violation: attempted to write "${rel}". Only the method surface and scaffolding paths are writable.`);
|
|
2047
2070
|
}
|
|
2071
|
+
let replacedExisting = false;
|
|
2048
2072
|
if (existsSync3(targetAbsPath)) {
|
|
2049
|
-
const current = readFileSync4(targetAbsPath
|
|
2050
|
-
|
|
2073
|
+
const current = readFileSync4(targetAbsPath);
|
|
2074
|
+
if (current.toString("utf-8") === content)
|
|
2075
|
+
return { outcome: "skipped", replacedExisting: false };
|
|
2076
|
+
if (!isOurs(current))
|
|
2077
|
+
return { outcome: "collided", replacedExisting: false };
|
|
2078
|
+
replacedExisting = true;
|
|
2051
2079
|
}
|
|
2052
2080
|
mkdirSync4(dirname3(targetAbsPath), { recursive: true });
|
|
2053
2081
|
writeFileSync4(targetAbsPath, content, "utf-8");
|
|
2054
|
-
return "written";
|
|
2082
|
+
return { outcome: "written", replacedExisting };
|
|
2055
2083
|
}
|
|
2056
2084
|
var INSTALLER_OWNED_DIR = ".halfcycle/";
|
|
2057
2085
|
function writeOwned(targetAbsPath, targetRepoRoot, content) {
|
|
@@ -2144,6 +2172,7 @@ function settingsParseRefusal(targetRepo, err) {
|
|
|
2144
2172
|
}
|
|
2145
2173
|
return `${file} could not be read as JSON${where} \u2014 fix it and run "npx halfcycle" again.`;
|
|
2146
2174
|
}
|
|
2175
|
+
var SETTINGS_SCHEMA = "https://json.schemastore.org/claude-code-settings.json";
|
|
2147
2176
|
function generateSettingsJson() {
|
|
2148
2177
|
const hooks = {};
|
|
2149
2178
|
for (const { event, matcher, timeout, command } of HALFCYCLE_HOOK_WIRING) {
|
|
@@ -2153,7 +2182,7 @@ function generateSettingsJson() {
|
|
|
2153
2182
|
});
|
|
2154
2183
|
}
|
|
2155
2184
|
const settings = {
|
|
2156
|
-
$schema:
|
|
2185
|
+
$schema: SETTINGS_SCHEMA,
|
|
2157
2186
|
permissions: {
|
|
2158
2187
|
// deny ONLY. See DENY_PATTERNS — an allow list evaporates under bypass mode
|
|
2159
2188
|
// and is the machine owner's grant to make, not ours.
|
|
@@ -2453,19 +2482,27 @@ function adoptBlockUnderHeader(before, header, claimable) {
|
|
|
2453
2482
|
let underHeader = false;
|
|
2454
2483
|
const lines = before.split("\n");
|
|
2455
2484
|
for (const [i, line] of lines.entries()) {
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2485
|
+
const key = lineKey(line);
|
|
2486
|
+
if (key === header) {
|
|
2487
|
+
if (i > 0 && lineKey(lines[i - 1]) === "")
|
|
2488
|
+
claimed.push(lines[i - 1]);
|
|
2459
2489
|
claimed.push(line);
|
|
2460
2490
|
underHeader = true;
|
|
2461
|
-
} else if (
|
|
2491
|
+
} else if (key.trim() === "") {
|
|
2462
2492
|
underHeader = false;
|
|
2463
|
-
} else if (underHeader && claimable.has(
|
|
2493
|
+
} else if (underHeader && claimable.has(key)) {
|
|
2464
2494
|
claimed.push(line);
|
|
2465
2495
|
}
|
|
2466
2496
|
}
|
|
2467
2497
|
return claimed.length > 0 ? claimed.join("\n") + "\n" : "";
|
|
2468
2498
|
}
|
|
2499
|
+
function lineKey(line) {
|
|
2500
|
+
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
2501
|
+
}
|
|
2502
|
+
function lineEndingOf(text) {
|
|
2503
|
+
const at = text.indexOf("\n");
|
|
2504
|
+
return at > 0 && text[at - 1] === "\r" ? "\r\n" : "\n";
|
|
2505
|
+
}
|
|
2469
2506
|
function gitignoreCovers(content) {
|
|
2470
2507
|
const lines = new Set(content.split("\n").map((l) => l.trim()));
|
|
2471
2508
|
return REQUIRED_GITIGNORE_ENTRIES.every((entry) => lines.has(entry));
|
|
@@ -2484,9 +2521,8 @@ function reconcileGitignore(targetRepo) {
|
|
|
2484
2521
|
return "skipped";
|
|
2485
2522
|
const existingLines = new Set(existing.split("\n").map((l) => l.trim()));
|
|
2486
2523
|
const toAdd = REQUIRED_GITIGNORE_ENTRIES.filter((entry) => !existingLines.has(entry));
|
|
2487
|
-
const
|
|
2488
|
-
|
|
2489
|
-
` + toAdd.join("\n") + "\n";
|
|
2524
|
+
const eol = lineEndingOf(existing);
|
|
2525
|
+
const suffix = (existing.endsWith("\n") ? "" : eol) + eol + [header, ...toAdd].join(eol) + eol;
|
|
2490
2526
|
writeFileSync4(gitignorePath, existing + suffix, "utf-8");
|
|
2491
2527
|
return "written";
|
|
2492
2528
|
} catch {
|
|
@@ -2521,8 +2557,9 @@ function reconcileGitattributes(targetRepo) {
|
|
|
2521
2557
|
const toAdd = rules.filter((rule) => !present.has(rule));
|
|
2522
2558
|
if (toAdd.length === 0)
|
|
2523
2559
|
return "skipped";
|
|
2524
|
-
const
|
|
2525
|
-
const
|
|
2560
|
+
const eol = lineEndingOf(existing);
|
|
2561
|
+
const separator = existing === "" ? "" : existing.endsWith("\n") ? eol : eol + eol;
|
|
2562
|
+
const suffix = separator + [GITATTRIBUTES_HEADER, ...toAdd].join(eol) + eol;
|
|
2526
2563
|
writeAllowlisted(path, targetRepo, existing + suffix, routed);
|
|
2527
2564
|
return "written";
|
|
2528
2565
|
} catch {
|
|
@@ -2542,9 +2579,9 @@ function previousPinForEngagement(targetRepoRoot, engagementId) {
|
|
|
2542
2579
|
function nonEmpty(value) {
|
|
2543
2580
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
2544
2581
|
}
|
|
2545
|
-
function writeBundlePin(targetRepoRoot, version, engagementId, engagementType,
|
|
2582
|
+
function writeBundlePin(targetRepoRoot, version, engagementId, engagementType, createdByAccountId, methodVersion, writtenPaths) {
|
|
2546
2583
|
const previous = previousPinForEngagement(targetRepoRoot, engagementId);
|
|
2547
|
-
const carried =
|
|
2584
|
+
const carried = pinCreatedByAccountId(previous) ?? nonEmpty(createdByAccountId);
|
|
2548
2585
|
const carriedMethodVersion = nonEmpty(methodVersion) ?? nonEmpty(previous?.methodVersion);
|
|
2549
2586
|
const installedAt = nonEmpty(previous?.installedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2550
2587
|
const projectPage = projectPageUrl(engagementId);
|
|
@@ -2553,7 +2590,7 @@ function writeBundlePin(targetRepoRoot, version, engagementId, engagementType, a
|
|
|
2553
2590
|
engagementId,
|
|
2554
2591
|
engagementType,
|
|
2555
2592
|
installedAt,
|
|
2556
|
-
...carried !== void 0 ? {
|
|
2593
|
+
...carried !== void 0 ? { createdByAccountId: carried } : {},
|
|
2557
2594
|
...carriedMethodVersion !== void 0 ? { methodVersion: carriedMethodVersion } : {},
|
|
2558
2595
|
...projectPage !== void 0 ? { projectPage } : {}
|
|
2559
2596
|
};
|
|
@@ -2570,6 +2607,9 @@ function writeCrewRoster(targetRepoRoot, report) {
|
|
|
2570
2607
|
const crewPath = join5(targetRepoRoot, CREW_ROSTER_REL);
|
|
2571
2608
|
return recordOwned(report, crewPath, targetRepoRoot, rendered, CREW_ROSTER_REL);
|
|
2572
2609
|
}
|
|
2610
|
+
function pinCreatedByAccountId(pin) {
|
|
2611
|
+
return nonEmpty(pin?.createdByAccountId) ?? nonEmpty(pin?.accountId);
|
|
2612
|
+
}
|
|
2573
2613
|
function readBundlePin(targetRepoRoot) {
|
|
2574
2614
|
const pinPath = join5(targetRepoRoot, BUNDLE_PIN_REL);
|
|
2575
2615
|
if (!existsSync3(pinPath))
|
|
@@ -2670,7 +2710,7 @@ function migrateLegacyEnvLocal(targetRepo, stored) {
|
|
|
2670
2710
|
}
|
|
2671
2711
|
}
|
|
2672
2712
|
async function install(options) {
|
|
2673
|
-
const { targetRepo, engagementId, engagementType, credential, home,
|
|
2713
|
+
const { targetRepo, engagementId, engagementType, credential, home, createdByAccountId } = options;
|
|
2674
2714
|
assertEngagementId(engagementId);
|
|
2675
2715
|
if (!existsSync3(targetRepo)) {
|
|
2676
2716
|
throw new Error(`[halfcycle] Target repo does not exist: ${targetRepo}`);
|
|
@@ -2681,12 +2721,13 @@ async function install(options) {
|
|
|
2681
2721
|
skippedPaths: [],
|
|
2682
2722
|
mergedPaths: [],
|
|
2683
2723
|
collidedPaths: [],
|
|
2724
|
+
editedCollidedPaths: [],
|
|
2684
2725
|
replacedPaths: []
|
|
2685
2726
|
};
|
|
2686
2727
|
const writeSet = closedWriteSet();
|
|
2687
2728
|
const draft = startManifest(readPreviousManifest(targetRepo), (path) => writeSet.files.has(path), (path) => writeSet.dirs.has(path));
|
|
2688
2729
|
const dirsBefore = new Set([...writeSet.dirs].filter((dir) => existsSync3(join5(targetRepo, dir))));
|
|
2689
|
-
copyManifestCommands(manifest, targetRepo, report, draft);
|
|
2730
|
+
copyManifestCommands(manifest, targetRepo, report, draft, writeSet);
|
|
2690
2731
|
const vendoredBinSrc = resolveVendoredBinary();
|
|
2691
2732
|
const vendoredBinDest = join5(targetRepo, ".halfcycle", "bin", "bin.bundle.mjs");
|
|
2692
2733
|
const vendoredOutcome = recordOwned(report, vendoredBinDest, targetRepo, readFileSync4(vendoredBinSrc, "utf-8"), VENDORED_BIN_REL);
|
|
@@ -2808,7 +2849,7 @@ async function install(options) {
|
|
|
2808
2849
|
case "absent":
|
|
2809
2850
|
break;
|
|
2810
2851
|
}
|
|
2811
|
-
writeBundlePin(targetRepo, manifest.version, engagementId, engagementType,
|
|
2852
|
+
writeBundlePin(targetRepo, manifest.version, engagementId, engagementType, createdByAccountId, credential?.methodVersion, report.writtenPaths);
|
|
2812
2853
|
noteFile(draft, targetRepo, BUNDLE_PIN_REL, "written");
|
|
2813
2854
|
noteFile(draft, targetRepo, CREW_ROSTER_REL, writeCrewRoster(targetRepo, report));
|
|
2814
2855
|
const scanResult = runBootstrapScan(targetRepo);
|
|
@@ -2828,6 +2869,7 @@ async function install(options) {
|
|
|
2828
2869
|
skippedPaths: report.skippedPaths,
|
|
2829
2870
|
mergedPaths: report.mergedPaths,
|
|
2830
2871
|
collidedPaths: report.collidedPaths,
|
|
2872
|
+
editedCollidedPaths: report.editedCollidedPaths,
|
|
2831
2873
|
replacedPaths: report.replacedPaths,
|
|
2832
2874
|
bootstrapScanRan: scanResult.ran,
|
|
2833
2875
|
legacyCredentialKept: migration.outcome === "kept" ? { keys: migration.unbacked, missing: migration.missingForAdoption } : null,
|
|
@@ -4077,6 +4119,7 @@ export {
|
|
|
4077
4119
|
mergeWithRecords,
|
|
4078
4120
|
mintOrReadIdentity,
|
|
4079
4121
|
obtainBuildRecord,
|
|
4122
|
+
pinCreatedByAccountId,
|
|
4080
4123
|
questionsToPut,
|
|
4081
4124
|
readBundlePin,
|
|
4082
4125
|
readRemote,
|