halfcycle 0.4.18 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +6 -3
- package/bin/bin.bundle.mjs +1558 -630
- package/bin/pre-tool-use-filter.sh +12 -0
- package/dist/banner-facts.d.ts +11 -1
- package/dist/banner-facts.d.ts.map +1 -1
- package/dist/bin.js +0 -0
- package/dist/check-tasks.d.ts +23 -11
- package/dist/check-tasks.d.ts.map +1 -1
- package/dist/claude-version-probe.d.ts +27 -3
- package/dist/claude-version-probe.d.ts.map +1 -1
- package/dist/cli.js +1439 -105
- package/dist/cli.js.map +3 -3
- package/dist/close-phase.d.ts.map +1 -1
- package/dist/codex-install-lines.d.ts +72 -0
- package/dist/codex-install-lines.d.ts.map +1 -0
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/index.js +1077 -24
- package/dist/index.js.map +3 -3
- package/dist/install-manifest.d.ts +41 -0
- package/dist/install-manifest.d.ts.map +1 -1
- package/dist/install-plan.d.ts +5 -1
- package/dist/install-plan.d.ts.map +1 -1
- package/dist/install.d.ts +395 -10
- package/dist/install.d.ts.map +1 -1
- package/dist/merge-settings.d.ts +56 -0
- package/dist/merge-settings.d.ts.map +1 -1
- package/dist/node-floor.d.ts +12 -3
- package/dist/node-floor.d.ts.map +1 -1
- package/dist/review.d.ts +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/toml-lines.d.ts +59 -0
- package/dist/toml-lines.d.ts.map +1 -0
- package/dist/uninstall.d.ts +3 -1
- package/dist/uninstall.d.ts.map +1 -1
- package/package.json +13 -12
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,+CAA+C;AAC/C,eAAO,MAAM,oBAAoB,qCAAqC,CAAC;AAEvE,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,kCAAkC,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;oFACoF;AACpF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,8CAA8C;AAC9C,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAEjD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,uBAAuB,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,CAAC;CAC9C;AAMD,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAG7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASrD;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAOD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"install-manifest.d.ts","sourceRoot":"","sources":["../src/install-manifest.ts"],"names":[],"mappings":"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"}
|
package/dist/install-plan.d.ts
CHANGED
|
@@ -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,
|
|
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
|
|
8
|
-
* either — see the step-1 note in `install()` (FX-2, W3-F-27)
|
|
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`;
|
|
477
|
-
*
|
|
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
|
|
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
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
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 {
|