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.
@@ -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 into the
73
- * committed `.halfcycle/bundle.json` (`writeBundlePin` in `install.ts`) — the id and
74
- * never the email, because that file is committed into the client's repository. A
75
- * wrong-identity install is then discoverable afterwards, from the repository, by the
76
- * person whose repository it is. Printing is not recording.
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 (`isNonInteractive`, whose
79
- * one home is `device-signin.ts` — the same question the browser handoff already asks).
80
- * It is never inferred from anything else.
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 this install acted as, which is the
135
- * copy a support conversation can actually be pointed at once the terminal is gone.
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 — `writeBundlePin` records it
231
- * in `.halfcycle/bundle.json`, which outlives the scrollback — and `--verbose` puts it
232
- * back on screen for the run that wants it.
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
- * `confirmed` (onboarding fix wave T-16, #1567) is false only for the re-use arm's
242
- * offline report: `askAccount` was asked and got no response at all, so "no email on
243
- * record for it" would be a claim this run never actually confirmed, printed beside a
244
- * raw account id with nothing true to say about why there is no email. `false` takes
245
- * neither branch below and says plainly that this run could not confirm who signed in.
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, confirmed?: boolean): string;
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkGG;AAGH,OAAO,EAAmD,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAI1G,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAKtG,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,6CAA6C;AAC7C,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,gBAAgB,GAAG,MAAM,CAAC;AAErE,6FAA6F;AAC7F,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,8DAA8D;IAC9D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gFAAgF;IAChF,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAeD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,CAAC;IAC/C;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,aAAa,CAAC,CAiBxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAEtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,OAAO,UAAQ,EACf,SAAS,UAAO,GACf,MAAM,CAaR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E;AAsCD;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAK7D;AAgKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAO3F;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAuD7B"}
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 the FOLDER was not set up, never that no project was
347
- * created: an answer lost after control created the project is exactly the case this
348
- * route makes recoverable, so "nothing was created" could be false. A re-run gets
349
- * that project back.
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;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"}
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"}
@@ -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. Nothing was created.
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
- /** True iff this run has no human at the keyboard. */
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":"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"}
1
+ {"version":3,"file":"device-signin.d.ts","sourceRoot":"","sources":["../src/device-signin.ts"],"names":[],"mappings":"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"}
@@ -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
  /**
@@ -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';
@@ -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;AAC9E,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"}
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) => halfcycleCommands.has(h.command));
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] ?? []).filter((entry) => !isHalfcycleEntry(entry, halfcycleCommands));
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 outcome = writeCollisionSafe(destFile, targetRepo, content);
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, "utf-8");
2050
- return current === content ? "skipped" : "collided";
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: "https://json.schemastore.org/claude-code-settings.json",
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
- if (line === header) {
2457
- if (i > 0 && lines[i - 1] === "")
2458
- claimed.push("");
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 (line.trim() === "") {
2491
+ } else if (key.trim() === "") {
2462
2492
  underHeader = false;
2463
- } else if (underHeader && claimable.has(line)) {
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 suffix = (existing.endsWith("\n") ? "" : "\n") + `
2488
- ${header}
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 separator = existing === "" ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
2525
- const suffix = separator + [GITATTRIBUTES_HEADER, ...toAdd].join("\n") + "\n";
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, accountId, methodVersion, writtenPaths) {
2582
+ function writeBundlePin(targetRepoRoot, version, engagementId, engagementType, createdByAccountId, methodVersion, writtenPaths) {
2546
2583
  const previous = previousPinForEngagement(targetRepoRoot, engagementId);
2547
- const carried = nonEmpty(accountId) ?? nonEmpty(previous?.accountId);
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 ? { accountId: carried } : {},
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, accountId } = options;
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, accountId, credential?.methodVersion, report.writtenPaths);
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,