halfcycle 0.4.18 → 0.5.0

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.
@@ -60,6 +60,32 @@ export interface SettingsRecord {
60
60
  readonly denyCreated: boolean;
61
61
  readonly statusLineSha256?: string;
62
62
  }
63
+ /**
64
+ * What the installer did to `.codex/hooks.json` (`codex-install-target` AC5): the
65
+ * settings record's hook half, with the same meaning field by field. OPTIONAL in the
66
+ * manifest, like `mcp`: a manifest an earlier version wrote has no such key, and it
67
+ * must still parse. `hookCommands` is written and, as for settings, never read to
68
+ * decide a removal: uninstall takes out every Codex command of ours by membership.
69
+ */
70
+ export interface CodexHooksRecord {
71
+ readonly created: boolean;
72
+ readonly adopted: boolean;
73
+ readonly hookCommands: readonly string[];
74
+ readonly eventsCreated: readonly string[];
75
+ readonly hooksCreated: boolean;
76
+ }
77
+ /**
78
+ * What the installer did to `.codex/config.toml` (`codex-install-target` AC5).
79
+ * `block` is the exact text the install put there, including the line ending and blank
80
+ * separator line it wrote before the opening marker when it appended one, as
81
+ * `.gitignore`'s record keeps its appended text. `created` is whether a run created
82
+ * the file. OPTIONAL in the manifest, like `codexHooks`: a manifest an earlier version
83
+ * wrote has no such key, and it must still parse.
84
+ */
85
+ export interface CodexConfigRecord {
86
+ readonly created: boolean;
87
+ readonly block: string;
88
+ }
63
89
  /** What the installer did to `.mcp.json`. `entrySha256` is the canonical hash of the
64
90
  * `halfcycle` server entry as last written; absent when no run has written one. */
65
91
  export interface McpRecord {
@@ -93,6 +119,8 @@ export interface InstallManifest {
93
119
  readonly leftAlone: readonly string[];
94
120
  readonly settings: SettingsRecord;
95
121
  readonly mcp?: McpRecord;
122
+ readonly codexHooks?: CodexHooksRecord;
123
+ readonly codexConfig?: CodexConfigRecord;
96
124
  readonly gitignore: GitignoreRecord;
97
125
  readonly gitattributes?: GitattributesRecord;
98
126
  }
@@ -180,6 +208,8 @@ export interface ManifestDraft {
180
208
  readonly createdDirs: Set<string>;
181
209
  settings: SettingsRecord | undefined;
182
210
  mcp: McpRecord | undefined;
211
+ codexHooks: CodexHooksRecord | undefined;
212
+ codexConfig: CodexConfigRecord | undefined;
183
213
  gitignore: GitignoreRecord | undefined;
184
214
  gitattributes: GitattributesRecord | undefined;
185
215
  }
@@ -221,6 +251,17 @@ export declare function recordDenyRetired(draft: ManifestDraft, hashes: readonly
221
251
  * does not.
222
252
  */
223
253
  export declare function recordMcp(draft: ManifestDraft, observed: McpRecord): void;
254
+ /**
255
+ * What this run observed about `.codex/hooks.json`, by the settings record's
256
+ * carry-forward rule (`recordSettings`): the two lists are unions, and `created`,
257
+ * `adopted` and `hooksCreated` keep the first value recorded.
258
+ */
259
+ export declare function recordCodexHooks(draft: ManifestDraft, observed: CodexHooksRecord): void;
260
+ /**
261
+ * What this run did to `.codex/config.toml`: `created` keeps its first value, and
262
+ * `block` is this run's (the caller composes it, separator included).
263
+ */
264
+ export declare function recordCodexConfig(draft: ManifestDraft, observed: CodexConfigRecord): void;
224
265
  /**
225
266
  * What this run did to `.gitignore`: `created` keeps its first value, and each of
226
267
  * `appended` is added after the elements already recorded (an adopted block from an
@@ -1 +1 @@
1
- {"version":3,"file":"install-manifest.d.ts","sourceRoot":"","sources":["../src/install-manifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install-manifest.d.ts","sourceRoot":"","sources":["../src/install-manifest.ts"],"names":[],"mappings":"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"}
@@ -45,8 +45,12 @@ export type PendingBytes = typeof PENDING;
45
45
  * Every kind of file the installer writes, each with its own preview words. Every member
46
46
  * of `closedWriteSet()` maps to one (`previewKindOf`), and a test asserts none is
47
47
  * missing, so a new installer file cannot ship without words for the preview.
48
+ *
49
+ * `codex-checks`, `codex-server`, `skill` and `crew-profile` land ahead of the Codex and
50
+ * crew-profile writers themselves (`codex-install-target`, `crew-agent-profiles`), so a
51
+ * later task plans a Codex or crew path with words already waiting for it.
48
52
  */
49
- export declare const PREVIEW_KINDS: readonly ["command", "check-script", "settings", "server-entry", "ignore-list", "line-endings", "halfcycle-folder", "runner", "captured-index", "old-sign-in"];
53
+ export declare const PREVIEW_KINDS: readonly ["command", "check-script", "settings", "server-entry", "ignore-list", "line-endings", "halfcycle-folder", "runner", "captured-index", "old-sign-in", "codex-checks", "codex-server", "skill", "crew-profile"];
50
54
  export type PreviewKind = (typeof PREVIEW_KINDS)[number];
51
55
  /** One path in the plan. `bytes` is `null` for `leave` and `remove`. */
52
56
  export interface InstallPlanEntry {
@@ -1 +1 @@
1
- {"version":3,"file":"install-plan.d.ts","sourceRoot":"","sources":["../src/install-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAYH,OAAO,EA4CL,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,KAAK,aAAa,EAKnB,MAAM,cAAc,CAAC;AAQtB,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,MAA0B,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,OAAO,OAAO,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,aAAa,gKAWhB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAAC;IAC7C,uGAAuG;IACvG,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;CAAE,GACnH;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAChD,yFAAyF;IACzF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAKD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CA6DpE,CAAC;AAoCH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGlE;AAYD;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,gBAAgB,CAAC;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAKD,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAwGpE;AAwJD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,CAqEjE"}
1
+ {"version":3,"file":"install-plan.d.ts","sourceRoot":"","sources":["../src/install-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAYH,OAAO,EAoDL,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,KAAK,aAAa,EAKnB,MAAM,cAAc,CAAC;AAQtB,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,MAA0B,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,OAAO,OAAO,CAAC;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,yNAehB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAAC;IAC7C,uGAAuG;IACvG,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;CAAE,GACnH;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAChD,yFAAyF;IACzF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAKD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAqFpE,CAAC;AAyCH,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAMlE;AAYD;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,gBAAgB,CAAC;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAKD,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAsHpE;AAoMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,CA0EjE"}
package/dist/install.d.ts CHANGED
@@ -4,8 +4,9 @@
4
4
  * Writes the client install surface (slash-command stubs, the vendored guard
5
5
  * binary, governance-hook wiring and the committed project identity) into a target
6
6
  * engagement repo — and the engagement's credential OUTSIDE it, at machine level
7
- * (T-09; see `engagement-credential.ts`). NO worker role files land
8
- * either — see the step-1 note in `install()` (FX-2, W3-F-27). NO method prose lands: the
7
+ * (T-09; see `engagement-credential.ts`). NO worker role files are copied
8
+ * either — see the step-1 note in `install()` (FX-2, W3-F-27); the crew profiles are
9
+ * generated from the roster (step 8c). NO method prose lands: the
9
10
  * stubs carry a description and a call, and the procedure arrives from the delivery
10
11
  * service at run time (INV-016). Reads the bundle version from
11
12
  * .claude-plugin/plugin.json and pins it in the target's .halfcycle/bundle.json.
@@ -21,10 +22,11 @@
21
22
  * (E1-T-09). The name is deliberately innocuous — it discloses nothing about
22
23
  * Halfcycle's scaffold contents.
23
24
  */
24
- import { BUNDLE_PIN_REL, MCP_REGISTRATION_REL, MCP_SERVER_KEY, SETTINGS_REL, WRITE_CAPABLE_TOOLS } from '@halfcycle/core';
25
+ import { BUNDLE_PIN_REL, MCP_REGISTRATION_REL, MCP_SERVER_KEY, SETTINGS_REL, WRITE_CAPABLE_TOOLS, type AgentId } from '@halfcycle/core';
26
+ import { type CrewGroup, type CrewMember, type CrewRole, type CrewTier } from '@halfcycle/events';
25
27
  import { type PlannedProjectIdentity } from './identity.js';
26
28
  import { lineKey, type InstallManifest, type ManifestFile } from './install-manifest.js';
27
- import { type ClaudeSettings, type StatusLineSetting } from './merge-settings.js';
29
+ import { type ClaudeSettings, type CodexHooksFile, type StatusLineSetting } from './merge-settings.js';
28
30
  export type EngagementType = 'self-build' | 'client';
29
31
  export interface BundlePin {
30
32
  version: string;
@@ -318,6 +320,35 @@ export interface InstallResult {
318
320
  * The install still succeeded; the caller says so in one line and changes nothing.
319
321
  */
320
322
  notShared: NotShared | null;
323
+ /**
324
+ * What setting up did for Codex (`codex-install-target`, *The approval line*). The
325
+ * CLI decides from it whether to print the one approval step Codex needs or a
326
+ * problem line instead.
327
+ */
328
+ codex: CodexInstallResult;
329
+ }
330
+ /**
331
+ * What became of Codex's checks file this run. `written`: created or changed;
332
+ * `current`: already held every check as this version writes it; `unreadable`: not a
333
+ * JSON object of hook lists, so it was left byte for byte; `link`: the file or a
334
+ * folder above it is a symbolic link, which is never followed.
335
+ */
336
+ export type CodexHooksOutcome = 'written' | 'current' | 'unreadable' | 'link';
337
+ /**
338
+ * What became of Codex's settings file this run (`codex-install-target`, *The approval
339
+ * line*). `written`: the block was added or replaced; `current`: the block already
340
+ * held exactly what this version writes; `conflict`: the file already names a server
341
+ * of that name, holds its servers in a form a new table cannot join, has a broken
342
+ * block, or could not be read, so it was left byte for byte; `refused-address`: the
343
+ * address failed the allowlist, so no block was written and any existing one was
344
+ * kept; `link`: the file or a folder above it is a symbolic link; `no-credential`:
345
+ * no server entry is written on this run at all.
346
+ */
347
+ export type CodexConfigOutcome = 'written' | 'current' | 'conflict' | 'refused-address' | 'link' | 'no-credential';
348
+ /** The Codex half of an install's result: one field per Codex file written. */
349
+ export interface CodexInstallResult {
350
+ readonly hooks: CodexHooksOutcome;
351
+ readonly config: CodexConfigOutcome;
321
352
  }
322
353
  /** The travel finding: one ignored file to name, its rule, and how many others. */
323
354
  export interface NotShared {
@@ -336,6 +367,62 @@ export interface NotShared {
336
367
  export declare function renderNotShared(finding: NotShared): string;
337
368
  /** The benign-named Zone-B root directory reserved in the install layout. */
338
369
  export declare const ZONE_B_DIR = ".workbench";
370
+ /**
371
+ * Codex's checks file (`codex-install-target` AC2). Its one home is the Codex profile
372
+ * in `@halfcycle/core` (`AGENT_PROFILES.codex.settingsFiles`, where Codex has exactly
373
+ * one hook file); read from there rather than typed a second time. Declared up here
374
+ * because `WRITE_ALLOWLIST` below is built at module load.
375
+ */
376
+ export declare const CODEX_HOOKS_REL: string;
377
+ /**
378
+ * Codex's project settings file, where its MCP servers are registered
379
+ * (`codex-install-target` AC3). Its one home is the Codex profile's MCP registration
380
+ * (`AGENT_PROFILES.codex.mcpRegistration`), read from there for the same reason as
381
+ * `CODEX_HOOKS_REL`, and declared up here for the same reason too.
382
+ */
383
+ export declare const CODEX_CONFIG_REL: string;
384
+ /** Claude Code's folder of agent profiles. */
385
+ export declare const CLAUDE_AGENTS_DIR = ".claude/agents";
386
+ /** Codex's folder of agent roles. */
387
+ export declare const CODEX_AGENTS_DIR = ".codex/agents";
388
+ /**
389
+ * A crew member's profile path for one agent, from a checked callsign. Exported ONLY so
390
+ * the check can be tested directly, as `skillRel` is.
391
+ *
392
+ * THE CASE (AC2, T-31's first step, measured 2026-10-31 on Claude Code 2.1.314): a
393
+ * profile whose file and `name` are an upper-case callsign loads under that name, case
394
+ * kept (the session's agent list showed it upper case beside a lower-case probe), and
395
+ * an agent call naming it in upper case started it. So Claude Code's file and name keep
396
+ * the callsign as written. Codex's are lower case (AC4): Codex refuses an upper-case
397
+ * task name (M2).
398
+ */
399
+ export declare function crewProfileRel(agent: AgentId, callsign: string): string;
400
+ /**
401
+ * THE SECOND OPERAND (AC2, INV-023): the profile's absolute path, after asserting that
402
+ * its parent is the agent's folder resolved ON ITS OWN. The joined result and the folder
403
+ * are two different computations, so a name that escaped the folder could not pass both
404
+ * this and the pattern. Exported ONLY so this check can be tested apart from the pattern.
405
+ */
406
+ export declare function crewProfileTarget(targetRepo: string, rel: string): string;
407
+ /**
408
+ * THE BODY (`crew-agent-profiles`, *The body*): built from the role and the tier ALONE,
409
+ * so two members sharing both share a body byte for byte, and nothing about the project
410
+ * or the method can reach it (INV-016). The callsign appears only in a profile's name
411
+ * and description.
412
+ */
413
+ export declare function crewProfileBody(role: CrewRole, group: CrewGroup, tier: CrewTier): string;
414
+ /** One generated profile: where it goes and its bytes. */
415
+ export interface CrewProfileFile {
416
+ readonly rel: string;
417
+ readonly text: string;
418
+ }
419
+ /**
420
+ * EVERY CREW PROFILE THIS VERSION WRITES, in roster order: Claude Code's for every
421
+ * member, then Codex's for every member outside the `reviewer` group (AC4: a Codex role
422
+ * cannot be made read-only, so no reviewer gets one). Pure; the one source of the paths
423
+ * the allowlist, the closed write set and the plan all read.
424
+ */
425
+ export declare function renderCrewProfiles(roster?: readonly CrewMember[]): CrewProfileFile[];
339
426
  /**
340
427
  * This package's own root, wherever it has been unpacked to.
341
428
  *
@@ -388,6 +475,15 @@ export { SETTINGS_REL, WRITE_CAPABLE_TOOLS };
388
475
  export declare const GITIGNORE_REL = ".gitignore";
389
476
  /** The attributes file the installer appends its line-ending rules to. */
390
477
  export declare const GITATTRIBUTES_REL = ".gitattributes";
478
+ /**
479
+ * A Codex skill's path in the client tree — Codex's twin of `commandStubRel`
480
+ * (`codex-install-target` AC4, T-14). INV-023: the name is checked BEFORE it
481
+ * reaches a path, because an unchecked `plugin.json` entry (a dot, a slash) could
482
+ * otherwise climb out of `.agents/skills/`. Exported ONLY so the check can be
483
+ * tested directly, as `writeOwned` is; every real caller inside this module goes
484
+ * through it via `planCodexSkills` and `closedWriteSet`.
485
+ */
486
+ export declare function skillRel(name: string): string;
391
487
  /**
392
488
  * The connection-headers helper an earlier version generated and `.mcp.json` named
393
489
  * (T-24, #446). RETIRED by `windows-client-seat`: Claude Code runs a `headersHelper`
@@ -408,6 +504,8 @@ export interface ClosedWriteSet {
408
504
  readonly ownedDirs: ReadonlySet<string>;
409
505
  /** Every hook command that is ours: `HALFCYCLE_HOOK_COMMANDS`, generated and retired. */
410
506
  readonly hookCommands: ReadonlySet<string>;
507
+ /** Every Codex hook command that is ours: the Codex profile's `commands`, generated and retired. */
508
+ readonly codexHookCommands: ReadonlySet<string>;
411
509
  /** The SHA-256 of every deny rule the installer adds. */
412
510
  readonly denyRuleSha256: ReadonlySet<string>;
413
511
  /** The `$schema` value the installer writes into `.claude/settings.json`. */
@@ -473,8 +571,9 @@ export declare const RETIRED_DENY_PATTERNS: readonly string[];
473
571
  * DERIVED, NOT LISTED. The command stubs come from `plugin.json`'s `commands[]`, the
474
572
  * same array `copyManifestCommands` copies from; the hook scripts from
475
573
  * `OWNED_GENERATED_HEADERS`, the table the owned-generated writes key on; the retired
476
- * headers helper from `RETIRED_WRITE_SET_FILES`; the rest from the named constants
477
- * their writers use. The directories are the
574
+ * headers helper from `RETIRED_WRITE_SET_FILES`; Codex's checks file and its commands
575
+ * from the Codex profile (`AGENT_PROFILES.codex`), the table `generateCodexHooksJson`
576
+ * writes from; the rest from the named constants their writers use. The directories are the
478
577
  * ancestors of every member, so a new file under a new directory brings its
479
578
  * directory with it. The writer and the remover therefore cannot hold two lists.
480
579
  */
@@ -535,6 +634,18 @@ type BytesAfter = (rel: string) => Uint8Array;
535
634
  * path in sorted order. Reads only: nothing here writes to the tree or to git.
536
635
  */
537
636
  declare function findNotShared(targetRepo: string, manifest: InstallManifest, collidedPaths: readonly string[]): NotShared | null;
637
+ /**
638
+ * The named body changes AC4 makes, from a command's own source body: the
639
+ * slash form of its own name becomes the bare name, the Claude Code trust
640
+ * sentence becomes its Codex twin, and — where the source carries it — the
641
+ * `/mcp`-reconnect fallback becomes "start a new Codex session" (M5). Throws
642
+ * when a source carries neither the slash form nor the trust sentence — the
643
+ * build fails rather than shipping a skill with Claude Code's own wording —
644
+ * and throws if `/mcp` survives the transform regardless of why. Exported
645
+ * ONLY so the refusal can be tested directly, on synthetic text, the way
646
+ * `writeOwned`'s path-scope guard is.
647
+ */
648
+ export declare function codexSkillBody(name: string, sourceBody: string): string;
538
649
  /** A planned command stub: a `PlannedFile` plus whether a collision is an edit of ours (E8). */
539
650
  export interface PlannedCommand extends PlannedFile {
540
651
  readonly editedCollided: boolean;
@@ -750,6 +861,24 @@ export declare function statusLineSetElsewhere(targetRepo: string, home?: string
750
861
  * protecting anyone: see DENY_PATTERNS for what these three do and do not cover.
751
862
  */
752
863
  export declare function generateSettingsJson(): string;
864
+ /**
865
+ * A FRESH `.codex/hooks.json` (`codex-install-target` AC2): the Codex profile's wiring
866
+ * table (`AGENT_PROFILES.codex.wiring`, `@halfcycle/core`), one group per row in table
867
+ * order, in Codex's documented shape — `{ "hooks": { <event>: [ { "matcher"?,
868
+ * "hooks": [ { "type": "command", "command", "timeout", "commandWindows"? } ] } ] } }`.
869
+ * Events appear in the order of their first row.
870
+ *
871
+ * NOTHING HERE IS DECIDED: every value is the profile's. This function only puts the
872
+ * rows into Codex's shape, so the table has one home and the file cannot drift from
873
+ * what the runner and the hosted check read.
874
+ *
875
+ * ITS BYTES ARE WHAT EVERY CODEX USER APPROVES (phase D2; measurement M1): Codex keys
876
+ * each approval on the hook's definition and position, so any change to these bytes
877
+ * makes every person approve the checks again. `test/codex-hooks.test.ts` pins
878
+ * them. A generated script's CONTENTS are not in here — each command runs a script by
879
+ * its path — so a script can change without asking anyone again.
880
+ */
881
+ export declare function generateCodexHooksJson(): string;
753
882
  /**
754
883
  * What the wrapper's fallback says when it stops a command (`risky-action-check`,
755
884
  * *User-facing behaviour*, decision 7): why the check is not running and the one
@@ -852,6 +981,142 @@ export declare function planLegacyCiStanzaRemoval(targetRepo: string): RetiredFi
852
981
  * on its next run and silently join a fresh credential.
853
982
  */
854
983
  export { MCP_REGISTRATION_REL, MCP_SERVER_KEY };
984
+ /**
985
+ * THE POSITIVE ADDRESS ALLOWLIST every address passes BEFORE it reaches the `url` line
986
+ * or Codex's headers command (`codex-install-target` AC3). Nothing else guarantees the
987
+ * address's characters: it is the origin control declares on the create answer, typed
988
+ * only as a string (`packages/events/src/credential-wire.ts`), plus `/mcp`
989
+ * (`mcpEndpointUrl`). Codex's command puts it inside a single-quoted string of a
990
+ * `node -e` program held to the character rule (`MCP_HEADERS_HELPER_COMMAND`'s
991
+ * docblock: no `"`, `$`, `%`, backtick, `\` or `!`), and the pattern admits none of
992
+ * those, nor `'`, whitespace, a control character or `%`-encoding. So a string from
993
+ * control can never close the quoting it sits in and become part of a command the
994
+ * agent runs.
995
+ *
996
+ * A POSITIVE pattern, never a list of refused characters: a character nobody thought
997
+ * of is refused by default. It also refuses an IPv6 literal and a host with `_`;
998
+ * accepted, because control's origins are DNS names. JavaScript's `$` matches only at
999
+ * the very end of the input (no `m` flag), so a trailing newline is refused too.
1000
+ */
1001
+ export declare const CODEX_ADDRESS_PATTERN: RegExp;
1002
+ /** Whether `address` may be written into Codex's settings and its headers command. */
1003
+ export declare function codexAddressAllowed(address: string): boolean;
1004
+ /**
1005
+ * THE COMMAND CODEX RUNS FOR THE CONNECTION HEADERS (`http_headers_helper`), the twin
1006
+ * of `MCP_HEADERS_HELPER_COMMAND` on measurement M5's `--url` arm
1007
+ * (`codex-install-target` AC3; `codex-client-seat` AC4). `null` when the address fails
1008
+ * `CODEX_ADDRESS_PATTERN`: that is the only way an address reaches the program, so no
1009
+ * caller can build a command around an unchecked one.
1010
+ *
1011
+ * A FUNCTION OF THE ADDRESS rather than a constant, because Codex clears the helper's
1012
+ * environment to eight names (M5), so no variable names the server: the program passes
1013
+ * it to the verb as `--url <address>`, and the address is per project. Everything else
1014
+ * is fixed text.
1015
+ *
1016
+ * WHAT DIFFERS FROM CLAUDE CODE'S PROGRAM, and why: IT NEVER EXITS NON-ZERO. Codex fails
1017
+ * the whole server on a non-zero exit or on stdout that is not one JSON object of
1018
+ * strings, and then never reaches delivery's sentence (M5). So:
1019
+ * - the runner's stdout is CAPTURED, not inherited, and printed only when the runner
1020
+ * exits 0; on any other exit, a signal, or a failure to start it, the placeholder
1021
+ * object is printed instead. stderr still passes through, so the runner's own
1022
+ * sentences reach the person;
1023
+ * - no runner at or above the session's folder keeps Claude Code's sentence on
1024
+ * stderr, then prints the placeholder object;
1025
+ * - a Node below the floor keeps the floor's sentence (`nodeFloorProgram`, given this
1026
+ * command's below-the-floor arm), then prints the placeholder object;
1027
+ * - anything that throws prints the placeholder object, and at most one object is
1028
+ * ever printed.
1029
+ * stdin is not given to the runner: the verb reads none.
1030
+ *
1031
+ * The character rule and the Node 10 parse rule hold for the whole written string,
1032
+ * asserted in `test/codex-config.test.ts` on what the installer writes, and the
1033
+ * command is run there against the real vendored binary.
1034
+ */
1035
+ export declare function codexMcpHeadersHelperCommand(address: string): string | null;
1036
+ /**
1037
+ * The opening and closing lines of the one block the installer owns in
1038
+ * `.codex/config.toml`. STABLE TEXT: no version, date or address, so every version
1039
+ * finds every earlier block. Changing either strands every block already written.
1040
+ */
1041
+ export declare const CODEX_CONFIG_OPEN_MARKER = "# >>> Halfcycle \u2014 generated by the Halfcycle installer; replaced on every install";
1042
+ export declare const CODEX_CONFIG_CLOSE_MARKER = "# <<< Halfcycle";
1043
+ /**
1044
+ * THE BLOCK, a pure generator (`codex-install-target`, *The `config.toml` block*): the
1045
+ * opening marker, `[mcp_servers.halfcycle]`, `url`, `http_headers_helper`, the closing
1046
+ * marker, joined with `\n` and no trailing newline (`reconcileCodexConfig` puts it in
1047
+ * the file's own line ending). `null` when `address` fails the allowlist, which is
1048
+ * checked here, before either value is built.
1049
+ *
1050
+ * NO APPROVAL SETTING, deliberately: the installer never commits a standing grant (the
1051
+ * operator's standing rule of 2026-08-10, recorded in `DENY_PATTERNS`' docblock). So no
1052
+ * `default_tools_approval_mode` or any other approval key is written, and headless
1053
+ * Codex cannot call Halfcycle's tools until the person chooses an approval mode
1054
+ * themselves (M5). A test asserts the parsed block holds exactly `url` and
1055
+ * `http_headers_helper`.
1056
+ */
1057
+ export declare function generateCodexConfigBlock(address: string): string | null;
1058
+ /**
1059
+ * THE SECOND KEY for removing a block (`codex-install-target` AC5): `block` (the
1060
+ * marker lines and what is between them, any line endings) regenerates byte for byte
1061
+ * from its own `url` line, with this version's headers command or a retired one. A
1062
+ * block anyone edited, or whose address no longer passes the allowlist, does not.
1063
+ *
1064
+ * `retired` is `RETIRED_CODEX_MCP_HEADERS_HELPERS` for every real caller; the
1065
+ * parameter exists so a test can show a block an older helper wrote is recognised
1066
+ * while that list is empty.
1067
+ */
1068
+ export declare function regeneratesCodexBlock(block: string, retired?: ReadonlyArray<(address: string) => string | null>): boolean;
1069
+ /** Why the reconciler left a file alone. */
1070
+ export type CodexConfigConflict = 'server-named' | 'broken-markers' | 'unreadable';
1071
+ /**
1072
+ * The reconciler's three results: the file's new text (with `appended`, the exact text
1073
+ * added at the end when the file held no block, `null` when a block was replaced in
1074
+ * place), `unchanged`, or a conflict. `block` is the block exactly as it sits in the
1075
+ * resulting file, its closing line's ending included when it has one: what the install
1076
+ * record keeps (`codexConfig.block`).
1077
+ */
1078
+ export type CodexConfigReconciled = {
1079
+ readonly kind: 'new';
1080
+ readonly text: string;
1081
+ readonly appended: string | null;
1082
+ readonly block: string;
1083
+ } | {
1084
+ readonly kind: 'unchanged';
1085
+ readonly block: string;
1086
+ } | {
1087
+ readonly kind: 'conflict';
1088
+ readonly reason: CodexConfigConflict;
1089
+ };
1090
+ /**
1091
+ * Why the installer must leave this `.codex/config.toml` text alone, or `null` when its
1092
+ * block may be written. Depends on the text alone, never on the address, so a preview
1093
+ * can say it before the address is known.
1094
+ */
1095
+ export declare function codexConfigConflict(existing: string): CodexConfigConflict | null;
1096
+ /** Whether `existing` holds exactly one well-formed block of ours. */
1097
+ export declare function holdsCodexBlock(existing: string): boolean;
1098
+ /**
1099
+ * The block's span in `text`, from the start of its opening line to the end of its
1100
+ * closing line's line ending (or of the text, when that line has none); `none` with no
1101
+ * block, `broken` for any broken-marker form (`locateCodexBlock`).
1102
+ */
1103
+ export declare function codexBlockSpan(text: string): {
1104
+ readonly start: number;
1105
+ readonly end: number;
1106
+ } | 'none' | 'broken';
1107
+ /**
1108
+ * THE RECONCILER, pure: the file's text (`null`: no file) and the block in, one of
1109
+ * three results out (`codex-install-target`, *The `config.toml` block*).
1110
+ * - No file, or an empty one: the block and a newline.
1111
+ * - No block: appended after a line ending (when the file lacks a final one) and one
1112
+ * blank separator line, the text the install record keeps (AC5).
1113
+ * - Our block: replaced in place, so a changed address or command reaches trees
1114
+ * installed before.
1115
+ * - A conflict (`codexConfigConflict`): nothing is written.
1116
+ * Every byte outside the block is kept, and the block takes the file's line ending
1117
+ * (`lineEndingOf`).
1118
+ */
1119
+ export declare function reconcileCodexConfig(existing: string | null, block: string): CodexConfigReconciled;
855
1120
  /**
856
1121
  * Delete the retired `.halfcycle/mcp-headers.sh` when, and only when, BOTH hold —
857
1122
  * the rule `removeLegacyCiStanza` follows, applied to this path
@@ -1459,7 +1724,8 @@ export declare function applyLegacyMigration(targetRepo: string, decided: Legacy
1459
1724
  * Install the bundle into a target engagement repo.
1460
1725
  *
1461
1726
  * Write order:
1462
- * 1. (nothing) — NO worker role files land in a client tree (FX-2, W3-F-27)
1727
+ * 1. (nothing) — NO worker role files are copied into a client tree (FX-2, W3-F-27);
1728
+ * the generated crew profiles are 8c
1463
1729
  * 2. commands → .claude/commands/ (one-screen stubs, no method prose)
1464
1730
  * 3. captured-payload fixtures store convention → test/fixtures/captured/
1465
1731
  * 4. vendored guard binary → .halfcycle/bin/bin.bundle.mjs (self-contained)
@@ -1474,6 +1740,8 @@ export declare function applyLegacyMigration(targetRepo: string, decided: Legacy
1474
1740
  * at machine level — T-09) + migration of any pre-T-09 in-tree credential
1475
1741
  * 8. .halfcycle/bundle.json pin
1476
1742
  * 8b. .halfcycle/crew.json — CREW_ROSTER, serialised verbatim (crew-roster-and-dispatch)
1743
+ * 8c. One profile per crew member → .claude/agents/<CALLSIGN>.md, and for every member
1744
+ * outside the reviewer group .codex/agents/<callsign>.toml (crew-agent-profiles)
1477
1745
  * 9. Bootstrap scan → .halfcycle/state.json (INV-021 — once, onboarding-time,
1478
1746
  * a no-op if the file already exists; never re-run on a re-install)
1479
1747
  * 10. .halfcycle/install-manifest.json — what this and every earlier install put
@@ -1504,6 +1772,8 @@ export interface InstallSteps {
1504
1772
  /** The write set's directories already there BEFORE anything is written. */
1505
1773
  readonly dirsBefore: ReadonlySet<string>;
1506
1774
  readonly commands: readonly PlannedCommand[];
1775
+ /** Codex's twin of `commands` (`codex-install-target` AC4, T-14). */
1776
+ readonly codexSkills: readonly PlannedCommand[];
1507
1777
  readonly vendored: PlannedFile;
1508
1778
  readonly settings: PlannedSettings;
1509
1779
  readonly hooks: readonly PlannedFile[];
@@ -1511,6 +1781,10 @@ export interface InstallSteps {
1511
1781
  readonly mcp: PlannedMcp;
1512
1782
  /** The retired connection-headers helper; `null` when no server entry is written. */
1513
1783
  readonly mcpHelper: RetiredFileRemoval | null;
1784
+ /** Codex's checks file (`codex-install-target` AC2), after the server entry. */
1785
+ readonly codexHooks: PlannedCodexHooks;
1786
+ /** Codex's settings file (`codex-install-target` AC3), after Codex's checks file. */
1787
+ readonly codexConfig: PlannedCodexConfig;
1514
1788
  readonly gitignore: PlannedMerge;
1515
1789
  readonly gitattributes: PlannedMerge;
1516
1790
  readonly identity: PlannedProjectIdentity;
@@ -1525,6 +1799,8 @@ export interface InstallSteps {
1525
1799
  /** `null` while the project id is pending (a preview). */
1526
1800
  readonly pin: PlannedFile | null;
1527
1801
  readonly crew: PlannedFile;
1802
+ /** One profile per crew member (`crew-agent-profiles`), after the crew roster. */
1803
+ readonly crewProfiles: readonly PlannedCommand[];
1528
1804
  readonly scan: {
1529
1805
  readonly text: string | null;
1530
1806
  };
@@ -1556,6 +1832,14 @@ export declare function planCommandStubs(targetRepo: string, previous: InstallMa
1556
1832
  version: string;
1557
1833
  commands: PlannedCommand[];
1558
1834
  };
1835
+ /**
1836
+ * 2b. Codex's twin of the command stubs — `.agents/skills/<name>/SKILL.md`, one
1837
+ * per `plugin.json` command (`codex-install-target` AC4, T-14). Same manifest,
1838
+ * same collision rule (`decideCollisionSafe`, `bytesAreOurs`): a developer's
1839
+ * differing file at the path is reported and kept, never overwritten. A declared
1840
+ * command whose source file is missing is a build error, exactly as for the stubs.
1841
+ */
1842
+ export declare function planCodexSkills(targetRepo: string, previous: InstallManifest | null, writeSet: ClosedWriteSet): PlannedCommand[];
1559
1843
  /** The install record an earlier install left here, or `null`. Reads only. */
1560
1844
  export { readPreviousManifest };
1561
1845
  /**
@@ -1590,8 +1874,106 @@ export declare function planHookScripts(targetRepo: string): PlannedFile[];
1590
1874
  export declare function planMcpRegistration(targetRepo: string, credential: DeviceCredential | 'pending' | undefined): PlannedMcp;
1591
1875
  /** The write half of the server entry, through `writeAllowlisted`. */
1592
1876
  export declare function putPlannedMcp(targetRepo: string, planned: PlannedMcp): void;
1877
+ /**
1878
+ * True when `rel`, or any folder on the way to it below `root`, is a symbolic link.
1879
+ * Stops at the first part that does not exist. THE ONE TEST both verbs apply before
1880
+ * touching a merged file: install leaves such a file alone (`planCodexHooks`), and
1881
+ * uninstall does not follow one (`uninstall.ts`, `applyMergedJson`).
1882
+ */
1883
+ export declare function symlinkOnPath(root: string, rel: string): boolean;
1884
+ /**
1885
+ * Codex's checks file as planned (`codex-install-target` AC2). `before` is the file's
1886
+ * text (`null`: absent) and `existing` its parsed value when it could be read; `text`
1887
+ * is what goes there when `outcome` is `written`, and `null` otherwise.
1888
+ */
1889
+ export interface PlannedCodexHooks {
1890
+ readonly outcome: CodexHooksOutcome;
1891
+ readonly before: string | null;
1892
+ readonly existing: Record<string, unknown> | null;
1893
+ readonly generated: CodexHooksFile;
1894
+ readonly text: string | null;
1895
+ }
1896
+ /**
1897
+ * 6c. `.codex/hooks.json` — Codex's checks, MERGED beside a developer's own entries and
1898
+ * never moving one of ours (`codex-install-target` AC2; `mergeCodexHooks`). Planned
1899
+ * after the server entry and before `.gitignore`, so no Claude Code write moves.
1900
+ *
1901
+ * Two arms leave the file byte for byte and say why, rather than refusing the install
1902
+ * as an unreadable `.claude/settings.json` does: Codex is the second agent, and a
1903
+ * project's Claude Code setup should not wait on it.
1904
+ * - `link`: the file, or a folder above it, is a symbolic link (`symlinkOnPath`).
1905
+ * - `unreadable`: not valid JSON; a root or `hooks` value that is not an object; an
1906
+ * event Halfcycle writes whose value is not a list; or a `.codex` that is not a
1907
+ * folder, so nothing could be written under it.
1908
+ *
1909
+ * A file whose parsed value already equals the merge's result is `current` and keeps
1910
+ * its bytes, whatever its formatting: whitespace and key order are not part of what
1911
+ * Codex approves.
1912
+ */
1913
+ export declare function planCodexHooks(targetRepo: string): PlannedCodexHooks;
1914
+ /** The write half of Codex's checks file, through `writeAllowlisted`. */
1915
+ export declare function putPlannedCodexHooks(targetRepo: string, planned: PlannedCodexHooks): void;
1916
+ /**
1917
+ * Codex's settings file as planned (`codex-install-target` AC3). `outcome` is
1918
+ * `pending` only in a preview, whose address is still to come. `before` is the file's
1919
+ * text when it was read (`null`: absent, or not read). `text` is what goes there when
1920
+ * `outcome` is `written`; `appended` is the exact text added at the end of a file that
1921
+ * held no block (`null` otherwise, a created file included). `blockKept` is true when
1922
+ * the address was refused and the file already held a block of ours, which is kept.
1923
+ */
1924
+ export interface PlannedCodexConfig {
1925
+ readonly outcome: CodexConfigOutcome | 'pending';
1926
+ readonly before: string | null;
1927
+ readonly text: string | null;
1928
+ readonly appended: string | null;
1929
+ /** The block as it sits in the file after this run, when `written` or `current`. */
1930
+ readonly inFile: string | null;
1931
+ readonly conflict: CodexConfigConflict | null;
1932
+ readonly blockKept: boolean;
1933
+ }
1934
+ /**
1935
+ * 6d. `.codex/config.toml` — Codex's server entry, one marked block beside whatever
1936
+ * the developer's file holds (`codex-install-target` AC3; `reconcileCodexConfig`).
1937
+ * Planned after Codex's checks file and before `.gitignore`, so no Claude Code write
1938
+ * moves. Decided in this order, each arm leaving the file byte for byte:
1939
+ * 1. no credential: `.mcp.json` is not written, so neither is this (`no-credential`);
1940
+ * 2. the file, or a folder above it, is a symbolic link (`link`), never followed;
1941
+ * 3. it cannot be read byte for byte (`conflict`, `unreadable`);
1942
+ * 4. a preview: whatever does not need the address is decided, the rest `pending`;
1943
+ * 5. the address fails `CODEX_ADDRESS_PATTERN` (`refused-address`), BEFORE any block
1944
+ * is built, so no part of a refused address is written; a block already there is
1945
+ * kept, and the report names it;
1946
+ * 6. the reconciler: a conflict, `current`, or the new text (`written`).
1947
+ *
1948
+ * The address is `mcpEndpointUrl` of the credential's MCP origin: the value `.mcp.json`
1949
+ * gets (`planMcpRegistration`), from the one function that composes it.
1950
+ */
1951
+ export declare function planCodexConfig(targetRepo: string, credential: DeviceCredential | 'pending' | undefined): PlannedCodexConfig;
1952
+ /** The write half of Codex's settings file, through `writeAllowlisted`. */
1953
+ export declare function putPlannedCodexConfig(targetRepo: string, planned: PlannedCodexConfig): void;
1593
1954
  /** 8b. The crew roster, owned by path (see `writeCrewRoster`). */
1594
1955
  export declare function planCrewRoster(targetRepo: string): PlannedFile;
1956
+ /**
1957
+ * 8c. One profile per crew member (`crew-agent-profiles` AC2–AC6): Claude Code's for
1958
+ * every member, Codex's for every member outside the `reviewer` group, from
1959
+ * `renderCrewProfiles`, the one generator.
1960
+ *
1961
+ * OWNED BY EVIDENCE, AND THE EVIDENCE IS THE BYTES (`decideCollisionSafe`, `bytesAreOurs`),
1962
+ * the rule the command stubs and the Codex skills use: a differing file is replaced only
1963
+ * when it holds exactly what an earlier install recorded or what this version writes, so
1964
+ * a developer's own file at one of these names, or one of ours they edited, is kept and
1965
+ * named. `decideOwnedGenerated`'s header-line evidence is NOT used here, deliberately: a
1966
+ * profile's line under its opening `---` is `name: <callsign>`, which a developer's own
1967
+ * profile of the same name carries too, so that rule would read theirs as ours and
1968
+ * overwrite it.
1969
+ *
1970
+ * A LINK IS NEVER FOLLOWED, AND A FILE IN A FOLDER'S PLACE IS NEVER MOVED: a profile path
1971
+ * that is, or sits under, a symbolic link, or whose folder is taken by a file (a `.codex`
1972
+ * that is a file, say), is left as it is and named, so a linked `.claude/agents` cannot
1973
+ * carry a write out of the project and the rest of the install still finishes. Both name
1974
+ * checks (`crewProfileRel`, `crewProfileTarget`) run before any read.
1975
+ */
1976
+ export declare function planCrewProfiles(targetRepo: string, previous: InstallManifest | null, writeSet: ClosedWriteSet): PlannedCommand[];
1595
1977
  /** 10. The install record, owned by path, decided once its bytes are known. */
1596
1978
  export declare function planInstallRecord(targetRepo: string, text: string): PlannedFile;
1597
1979
  export { writeBundlePin, planBundlePin, writeEngagementCredential, findNotShared };
@@ -1636,9 +2018,12 @@ export type { MutableReport, CopyOutcome, BytesAfter };
1636
2018
  export declare const SELF_BUILD_GITIGNORE_REL = ".halfcycle/.gitignore";
1637
2019
  /**
1638
2020
  * The repo paths the self-build install may write, and the only ones (`self-build-install`
1639
- * AC4). ONE HOME: the install mode below writes exactly these, and the monorepo
1640
- * uninstall takes back exactly these, so the two cannot disagree. Every entry sits under
1641
- * a `WRITE_ALLOWLIST` prefix. The credential is not here because it is not in the repo.
2021
+ * AC4, amended 2026-10-31 by `T-32` per `crew-agent-profiles` AC7: the self-build set gains
2022
+ * every crew profile `renderCrewProfiles` names, Claude Code's and Codex's alike, so this
2023
+ * repository's own coordinator has one to start each callsign by). ONE HOME: the install
2024
+ * mode below writes exactly these, and the monorepo uninstall takes back exactly these, so
2025
+ * the two cannot disagree. Every entry sits under a `WRITE_ALLOWLIST` prefix. The credential
2026
+ * is not here because it is not in the repo.
1642
2027
  */
1643
2028
  export declare const SELF_BUILD_WRITE_SET: readonly string[];
1644
2029
  export interface SelfBuildInstallOptions {