@iamdevlinph/codex-kit 1.0.11 → 1.0.13

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/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ This license applies only to files included in the published
2
+ @iamdevlinph/codex-kit npm package. It does not apply to repository-only files.
3
+
4
+ ISC License
5
+
6
+ Copyright (c) 2026 Devlin Pajaron
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted, provided that the above
10
+ copyright notice and this permission notice appear in all copies.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
13
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
14
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
15
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
17
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -5,6 +5,7 @@ Portable Codex setup for new devices and multiple projects. It provides:
5
5
  - three automatically routed roles plus a manual quick implementer
6
6
  - automatic global role routing
7
7
  - a reusable, stack-neutral `AGENTS.md` template
8
+ - a package-owned `codex-kit-reconcile-agents` skill for semantic reconciliation
8
9
  - safe commands for global setup, project synchronization, and reconciliation
9
10
 
10
11
  The package contains no credentials. Global installation does not modify the
@@ -28,9 +29,13 @@ that they run the installed `${CODEX_HOME:-~/.codex}/codex-kit/routing-hook.js`.
28
29
  For one-off use without a global installation, prefix a command with
29
30
  `pnpm dlx @iamdevlinph/codex-kit@latest`.
30
31
 
31
- `global install` copies reusable agents to `${CODEX_HOME:-~/.codex}`, maintains
32
- a marked routing section in the global `AGENTS.md`, and adds package-owned
33
- handlers to the global `hooks.json` without replacing existing hooks.
32
+ `global install` copies reusable agents and the
33
+ `skills/codex-kit-reconcile-agents` skill to `${CODEX_HOME:-~/.codex}`, maintains
34
+ the package routing section in global `AGENTS.md`, and adds package-owned
35
+ handlers to `hooks.json` without replacing existing hooks. `global list` shows
36
+ the reconciliation skill's ownership status. Install and uninstall preserve
37
+ modified or user-owned skill files using the same backup/restore semantics as
38
+ other package files.
34
39
 
35
40
  The Sol root plans, routes, coordinates, and validates. On every prompt, the
36
41
  routing hook supplies the current `SUBAGENT_ROUTING.md`; the root classifies the
@@ -41,6 +46,18 @@ read-only checks, documentation, bookkeeping, and clear changes spanning up to
41
46
  roughly three files. Automatic delegation is reserved for broad discovery,
42
47
  large multi-file implementation or debugging, and high-risk review.
43
48
 
49
+ ## Available subagents
50
+
51
+ | Subagent | Routing | Model and effort | Used for |
52
+ | --- | --- | --- | --- |
53
+ | `code-explorer` | Automatic | `gpt-5.6-terra`, medium | Read-only broad repository discovery, contract tracing, and multi-file searches |
54
+ | `implementer` | Automatic | `gpt-5.6-luna`, high | Large behavior changes, non-obvious debugging, migrations, and substantial tests |
55
+ | `code-reviewer` | Automatic | `gpt-5.6-sol`, high | Read-only review of security-sensitive, architectural, public-API, concurrency, migration, or difficult-to-validate changes |
56
+ | `quick-implementer` | Manual only | `gpt-5.6-luna`, medium | Small, mechanical, well-specified changes limited to one or two files |
57
+
58
+ The root orchestrator is not a subagent. It owns planning, routing, integration,
59
+ and final validation.
60
+
44
61
  For a substantial task that splits into genuinely independent slices, the root
45
62
  may run multiple `implementer` instances concurrently. Each receives exclusive
46
63
  file or module ownership and separate validation scope. Work that shares types,
@@ -69,11 +86,6 @@ codex-kit global configure \
69
86
  --plan-reasoning-effort high
70
87
  ```
71
88
 
72
- Automatic role models are tuned by task shape: Terra-medium performs broad
73
- repository exploration, Luna-high performs large implementation slices, and
74
- Sol-high reviews consequential changes. Luna-medium `quick-implementer` remains
75
- manual-only.
76
-
77
89
  Delegation is time-bounded. The root waits once for up to 60 seconds, requests
78
90
  one progress update, and then enforces a three-minute read/review/manual-quick
79
91
  deadline or five-minute implementation deadline. Validation commands that make
@@ -101,8 +113,8 @@ pnpm dlx @iamdevlinph/codex-kit@latest global list
101
113
  ```
102
114
 
103
115
  The summary shows the Codex home, orchestrator, normal and Plan-mode reasoning
104
- effort, routing-file and routing-hook status, and installed custom agents without
105
- dumping unrelated configuration.
116
+ effort, routing-file and routing-hook status, reconciliation-skill status, and
117
+ installed custom agents without dumping unrelated configuration.
106
118
 
107
119
  Uninstall package-managed global files:
108
120
 
@@ -174,13 +186,14 @@ pnpm dlx @iamdevlinph/codex-kit@latest project sync
174
186
  codex-kit project status
175
187
  ```
176
188
 
177
- `project sync` never edits `AGENTS.md` or project skills. It prints a prompt
178
- asking Codex to merge only applicable reusable changes while preserving local
179
- adaptations. Reconciliation keeps always-on rules in `AGENTS.md` and may create
180
- or update task-specific workflows under `.agents/skills` when the updated
181
- template warrants them. Critical safety and authorization rules remain in
182
- `AGENTS.md`, and speculative skills are avoided. After reviewing the semantic
183
- merge and any skill changes, record the applied template hash:
189
+ `project sync` never edits `AGENTS.md` or project skills. It routes Codex to the
190
+ global `codex-kit-reconcile-agents` skill, which inspects project state and
191
+ existing skills, merges only applicable reusable changes, preserves local
192
+ adaptations and organization, and may create or update a concrete conditional
193
+ workflow under `.agents/skills`. Critical safety, authorization, secrets,
194
+ database, deployment, and destructive-operation rules remain in `AGENTS.md`;
195
+ do not copy the complete template or introduce managed markers. After semantic
196
+ reconciliation and validation, record the applied template hash:
184
197
 
185
198
  ```sh
186
199
  codex-kit project mark-applied
@@ -231,8 +244,9 @@ standard-library modules.
231
244
 
232
245
  ## License
233
246
 
234
- `UNLICENSED`. Public availability on npm does not grant permission to reuse or
235
- redistribute the package beyond applicable law and npm's service terms.
247
+ Files included in the published `@iamdevlinph/codex-kit` npm package are
248
+ licensed under the [ISC License](LICENSE). Repository-only files remain
249
+ proprietary and are not covered by that license.
236
250
 
237
251
  ## References
238
252
 
@@ -1,8 +1,7 @@
1
1
  # Shared Agent Defaults
2
2
 
3
3
  Reusable defaults for coding agents. Adapt stack details, commands, paths,
4
- product context, and local conventions in the project-specific section outside
5
- the managed markers.
4
+ product context, and local conventions in the project's active `AGENTS.md`.
6
5
 
7
6
  ## Context And Instruction Scope
8
7
 
@@ -40,16 +39,18 @@ the managed markers.
40
39
  - `TEMPLATE_AGENTS.md` is a staged reference copy of codex-kit's reusable
41
40
  instructions, not active project guidance. Active repository guidance lives
42
41
  in `AGENTS.md` and applicable project skills under `.agents/skills`.
43
- - When `codex-kit project status` reports `reconciliation required`, compare the
44
- template reference with `AGENTS.md`, apply relevant reusable changes, preserve
45
- project-specific rules, reconcile applicable skills, then run
46
- `codex-kit project mark-applied`.
42
+ - When the template is refreshed, `codex-kit project status` reports
43
+ `reconciliation required`, or a user requests template sync or mark-applied,
44
+ use the global `$codex-kit-reconcile-agents` skill. It compares the template
45
+ with `AGENTS.md`, project state, and existing skills; preserves local rules;
46
+ applies only relevant reusable changes; validates them; and marks applied
47
+ only after success.
47
48
  - Keep project-specific rules out of `TEMPLATE_AGENTS.md`. Treat local template
48
49
  edits as candidates that must be generalized and promoted to the canonical
49
50
  `assets/TEMPLATE_AGENTS.md` in the codex-kit repository. Updating `AGENTS.md`
50
51
  does not update either template automatically.
51
- - Keep project-specific context outside the managed `AGENTS.md` block; keep only
52
- reusable cross-project rules in the packaged template.
52
+ - Keep project-specific context out of the packaged template; keep only
53
+ reusable cross-project rules here.
53
54
  - When a request introduces a reusable workflow preference, convention, agent
54
55
  behavior, tooling default, or safety rule, tell the user it appears
55
56
  template-level and update the current project's active instructions when
@@ -66,26 +67,16 @@ the managed markers.
66
67
  - Merge template updates into other projects without overwriting project-specific
67
68
  context.
68
69
 
69
- ## Template Sync Prompt
70
-
71
- When `codex-kit project sync` stages `TEMPLATE_AGENTS.md` beside an existing
72
- unmanaged `AGENTS.md`, use this prompt:
73
-
74
- ```txt
75
- Convert this repository to the codex-kit managed AGENTS.md layout. Put the exact
76
- contents of TEMPLATE_AGENTS.md between
77
- <!-- BEGIN codex-kit:shared-template --> and
78
- <!-- END codex-kit:shared-template -->. Preserve every repository-specific
79
- instruction from the current AGENTS.md after the managed block under
80
- # Project-Specific Instructions, remove only duplicate shared rules, and do not
81
- change project behavior. Review existing project skills under .agents/skills and
82
- move conditionally relevant, repeatable procedures out of AGENTS.md only when a
83
- skill is warranted. Preserve critical safety and authorization rules in
84
- AGENTS.md, preserve existing relevant skills, avoid speculative skills, and
85
- validate any skill you create or modify. Afterward, summarize what was
86
- preserved, what moved into a skill, and whether any local rules appear
87
- template-worthy.
88
- ```
70
+ ## Template reconciliation
71
+
72
+ Do not copy the complete template into `AGENTS.md`, replace it wholesale, or
73
+ introduce managed markers. If a legacy shared-template marker is present,
74
+ preserve local content and reconcile its meaning safely without creating new
75
+ markers. Summarize added, updated, skipped, adapted, and skill-moved guidance,
76
+ including local/template conflicts and any generalized template-worthy
77
+ promotion. Keep critical safety, authorization, secrets, database, deployment,
78
+ and destructive-operation rules always-on in `AGENTS.md`; extract only concrete
79
+ conditional procedures into validated project skills.
89
80
 
90
81
  ## Core Behavior
91
82
 
@@ -122,6 +113,8 @@ template-worthy.
122
113
  this rule. If automated coverage is impractical, explain why and perform the
123
114
  strongest targeted verification available.
124
115
  - Run the relevant focused tests after changing tested behavior.
116
+ - When adding or updating dependencies, pin exact versions rather than ranges.
117
+ With pnpm, use `pnpm add -E` (`--save-exact`).
125
118
  - Do not change dependencies, global tools, or the environment by default.
126
119
  - Do not run local or remote database inspection, generation, migration, or SQL
127
120
  commands unless the task requires them.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: codex-kit-reconcile-agents
3
+ description: Reconcile a refreshed TEMPLATE_AGENTS.md with a project's AGENTS.md and applicable skills while preserving local adaptations. Use when project status requires reconciliation, the template was refreshed, or the user requests an agent-template sync or mark-applied.
4
+ ---
5
+
6
+ # Reconcile codex-kit agent guidance
7
+
8
+ ## Procedure
9
+
10
+ 1. Inspect `AGENTS.md`, `TEMPLATE_AGENTS.md`, `.codex-kit-state.json`, the
11
+ project's existing `.agents/skills`, and `codex-kit project status`. If a
12
+ codex-kit template backup exists, compare it with the refreshed template to
13
+ isolate the actual template change.
14
+ 2. Preserve the existing `AGENTS.md` organization and all project-specific
15
+ adaptations. Merge only reusable template guidance that applies to this
16
+ repository; report conflicts between local and template rules, and do not
17
+ replace `AGENTS.md` wholesale or copy the complete template into it.
18
+ 3. Keep critical always-on safety, authorization, secrets, database,
19
+ deployment, and destructive-operation rules in `AGENTS.md`.
20
+ 4. Extract only concrete, conditional, repeatable project procedures into a
21
+ focused skill under `.agents/skills/<skill-name>/SKILL.md`. Preserve relevant
22
+ existing skills, use valid YAML frontmatter, and do not create speculative
23
+ skills or duplicate detailed instructions.
24
+ 5. Review the final instruction diff for preserved local rules, duplicates, and
25
+ unintended template edits. Validate every created or modified project skill
26
+ with an available skill validator and run the repository's documented checks.
27
+ Do not mark the template applied until reconciliation and validation succeed.
28
+ 6. Run `codex-kit project mark-applied` only after successful reconciliation and
29
+ validation, then confirm `codex-kit project status` is up to date.
30
+ 7. Summarize added, updated, skipped, adapted, and skill-moved guidance, with
31
+ reasons. Identify genuinely reusable, generalized changes that should be
32
+ promoted to codex-kit's canonical template and report the target section and
33
+ wording.
34
+
35
+ If legacy `codex-kit:shared-template` markers are present, preserve local
36
+ content and reconcile their meaning semantically. Do not add, recreate, or
37
+ depend on managed markers, and do not discard local adaptations.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Reconcile Codex Agent Guidance"
3
+ short_description: "Semantically reconcile templates and project skills"
4
+ default_prompt: "Use $codex-kit-reconcile-agents to reconcile this project's agent guidance."
package/bin/codex-kit.js CHANGED
@@ -1,799 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
3
- import { createHash } from "node:crypto";
4
- import { spawnSync } from "node:child_process";
5
- import { homedir } from "node:os";
6
- import { dirname, join, resolve } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- const isRecord = (value) => typeof value === "object" && value !== null;
9
- const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
- const ASSETS = join(ROOT, "assets");
11
- const AGENTS_DIR = join(ASSETS, "agents");
12
- const ROUTING_FILE = join(ASSETS, "SUBAGENT_ROUTING.md");
13
- const ROUTING_HOOK_FILE = join(ROOT, "bin", "routing-hook.js");
14
- const TEMPLATE_FILE = join(ASSETS, "TEMPLATE_AGENTS.md");
15
- const PACKAGE = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
16
- const GLOBAL_BEGIN = "<!-- BEGIN codex-kit:subagent-routing -->";
17
- const GLOBAL_END = "<!-- END codex-kit:subagent-routing -->";
18
- const PROJECT_BEGIN = "<!-- BEGIN codex-kit:shared-template -->";
19
- const PROJECT_END = "<!-- END codex-kit:shared-template -->";
20
- const STATE_FILE = ".codex-kit-state.json";
21
- const PROJECT_STATE_FILE = ".codex-kit-state.json";
22
- const REGISTRY = PACKAGE.publishConfig?.registry ?? "https://registry.npmjs.org";
23
- const DEFAULT_ORCHESTRATOR = "gpt-5.6-sol";
24
- const DEFAULT_REASONING_EFFORT = "low";
25
- const DEFAULT_PLAN_REASONING_EFFORT = "high";
26
- const sha256 = (data) => createHash("sha256").update(data).digest("hex");
27
- const read = (file) => readFileSync(file);
28
- const readText = (file) => readFileSync(file, "utf8");
29
- function timestamp() {
30
- return new Date().toISOString().replace(/[-:TZ.]/g, "");
31
- }
32
- function backup(file) {
33
- if (!existsSync(file))
34
- return null;
35
- let destination = `${file}.codex-kit.bak-${timestamp()}`;
36
- let suffix = 1;
37
- while (existsSync(destination))
38
- destination = `${file}.codex-kit.bak-${timestamp()}-${suffix++}`;
39
- copyFileSync(file, destination);
40
- console.log(`backup: ${destination}`);
41
- return destination;
42
- }
43
- function write(file, data) {
44
- mkdirSync(dirname(file), { recursive: true });
45
- const temporary = `${file}.codex-kit.tmp-${process.pid}`;
46
- writeFileSync(temporary, data);
47
- renameSync(temporary, file);
48
- }
49
- function readJsonObject(file) {
50
- if (!existsSync(file))
51
- return {};
52
- try {
53
- const value = JSON.parse(readText(file));
54
- if (isRecord(value))
55
- return value;
56
- }
57
- catch {
58
- // Use the actionable error below for invalid JSON and non-object roots.
59
- }
60
- throw new Error(`${file} must contain a JSON object; fix or move it before installing.`);
61
- }
62
- function shellQuote(value) {
63
- return `'${value.replaceAll("'", `'"'"'`)}'`;
64
- }
65
- function hookCommands(file) {
66
- return {
67
- command: `/usr/bin/env node ${shellQuote(file)}`,
68
- commandWindows: `node ${JSON.stringify(file)}`,
69
- };
70
- }
71
- function removeHookHandlers(root, state) {
72
- const hooks = root.hooks;
73
- if (!isRecord(hooks))
74
- return;
75
- for (const [event, groupsValue] of Object.entries(hooks)) {
76
- if (!Array.isArray(groupsValue))
77
- continue;
78
- const groups = groupsValue.flatMap((groupValue) => {
79
- if (!isRecord(groupValue) || !Array.isArray(groupValue.hooks))
80
- return [groupValue];
81
- const handlers = groupValue.hooks.filter((handler) => {
82
- if (!isRecord(handler))
83
- return true;
84
- return handler.command !== state.command && handler.commandWindows !== state.commandWindows;
85
- });
86
- return handlers.length ? [{ ...groupValue, hooks: handlers }] : [];
87
- });
88
- if (groups.length)
89
- hooks[event] = groups;
90
- else
91
- delete hooks[event];
92
- }
93
- }
94
- function installRoutingHooks(home, previous) {
95
- const target = join(home, "hooks.json");
96
- const hookFile = join(home, "codex-kit", "routing-hook.js");
97
- const commands = hookCommands(hookFile);
98
- const created = previous?.created ?? !existsSync(target);
99
- const root = readJsonObject(target);
100
- if (previous)
101
- removeHookHandlers(root, previous);
102
- const hooks = isRecord(root.hooks) ? root.hooks : {};
103
- root.hooks = hooks;
104
- const handler = {
105
- type: "command",
106
- command: commands.command,
107
- commandWindows: commands.commandWindows,
108
- timeout: 5,
109
- };
110
- const promptGroups = Array.isArray(hooks.UserPromptSubmit) ? hooks.UserPromptSubmit : [];
111
- hooks.UserPromptSubmit = [
112
- ...promptGroups,
113
- { hooks: [{ ...handler, statusMessage: "Loading subagent routing" }] },
114
- ];
115
- const startGroups = Array.isArray(hooks.SubagentStart) ? hooks.SubagentStart : [];
116
- hooks.SubagentStart = [
117
- ...startGroups,
118
- { hooks: [{ ...handler, statusMessage: "Briefing delegated worker" }] },
119
- ];
120
- const updated = `${JSON.stringify(root, null, 2)}\n`;
121
- const original = existsSync(target) ? readText(target) : "";
122
- if (updated !== original) {
123
- backup(target);
124
- write(target, updated);
125
- console.log(`updated: ${target}`);
126
- }
127
- return { target, ...commands, created };
128
- }
129
- function uninstallRoutingHooks(state) {
130
- if (!existsSync(state.target)) {
131
- console.warn(`preserved missing hooks file: ${state.target}`);
132
- return;
133
- }
134
- const root = readJsonObject(state.target);
135
- removeHookHandlers(root, state);
136
- const hooks = root.hooks;
137
- if (isRecord(hooks) && !Object.keys(hooks).length)
138
- delete root.hooks;
139
- backup(state.target);
140
- if (state.created && !Object.keys(root).length)
141
- rmSync(state.target);
142
- else
143
- write(state.target, `${JSON.stringify(root, null, 2)}\n`);
144
- console.log(`removed managed routing hooks from: ${state.target}`);
145
- }
146
- function loadState(home) {
147
- const file = join(home, STATE_FILE);
148
- if (!existsSync(file))
149
- return { version: PACKAGE.version, files: {} };
150
- try {
151
- const state = JSON.parse(readText(file));
152
- return isRecord(state) && isRecord(state.files)
153
- ? state
154
- : { version: PACKAGE.version, files: {} };
155
- }
156
- catch {
157
- throw new Error(`${file} is not valid JSON; move it aside before reinstalling.`);
158
- }
159
- }
160
- function saveState(home, state) {
161
- write(join(home, STATE_FILE), `${JSON.stringify(state, null, 2)}\n`);
162
- }
163
- function topLevelConfigEntries(contents) {
164
- const entries = new Map();
165
- let inTable = false;
166
- for (const line of contents.split("\n")) {
167
- if (/^\s*\[/.test(line)) {
168
- inTable = true;
169
- continue;
170
- }
171
- if (inTable)
172
- continue;
173
- const match = /^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(line);
174
- const key = match?.[2];
175
- const value = match?.[3];
176
- if (key && value !== undefined && !entries.has(key))
177
- entries.set(key, { value, line });
178
- }
179
- return entries;
180
- }
181
- function tomlString(value) {
182
- return JSON.stringify(value);
183
- }
184
- function setTopLevelConfig(contents, desired) {
185
- const lines = contents.split("\n");
186
- const seen = new Set();
187
- let firstTable = lines.length;
188
- for (let index = 0; index < lines.length; index++) {
189
- const line = lines[index];
190
- if (line !== undefined && /^\s*\[/.test(line)) {
191
- firstTable = index;
192
- break;
193
- }
194
- }
195
- for (let index = 0; index < firstTable; index++) {
196
- const line = lines[index];
197
- if (line === undefined)
198
- continue;
199
- const match = /^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(line);
200
- const key = match?.[2];
201
- if (!match || !key || seen.has(key))
202
- continue;
203
- lines[index] = `${match[1]}${key} = ${tomlString(desired[key])}`;
204
- seen.add(key);
205
- }
206
- const missing = Object.keys(desired)
207
- .filter((key) => !seen.has(key))
208
- .map((key) => `${key} = ${tomlString(desired[key])}`);
209
- if (missing.length)
210
- lines.splice(0, 0, ...missing, "");
211
- return lines.join("\n");
212
- }
213
- function restoreTopLevelConfig(contents, config) {
214
- const desired = config.desired ?? {};
215
- const previous = config.previous ?? {};
216
- const lines = contents.split("\n");
217
- let firstTable = lines.length;
218
- for (let index = 0; index < lines.length; index++) {
219
- const line = lines[index];
220
- if (line !== undefined && /^\s*\[/.test(line)) {
221
- firstTable = index;
222
- break;
223
- }
224
- }
225
- const restored = new Set();
226
- for (let index = 0; index < firstTable; index++) {
227
- const line = lines[index];
228
- if (line === undefined)
229
- continue;
230
- const match = /^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(line);
231
- const key = match?.[2];
232
- if (!match || !key || restored.has(key))
233
- continue;
234
- if (match[3] !== tomlString(desired[key]))
235
- continue;
236
- const prior = previous[key];
237
- if (prior?.present) {
238
- lines[index] = `${match[1]}${key} = ${prior.value}`;
239
- }
240
- else {
241
- lines.splice(index, 1);
242
- index--;
243
- firstTable--;
244
- }
245
- restored.add(key);
246
- }
247
- if (Object.values(previous).some((entry) => entry && !entry.present) && lines[0] === "")
248
- lines.shift();
249
- return lines.join("\n");
250
- }
251
- function loadProjectState(cwd) {
252
- const file = join(cwd, PROJECT_STATE_FILE);
253
- if (!existsSync(file))
254
- return { version: 1, template: {} };
255
- try {
256
- const state = JSON.parse(readText(file));
257
- return isRecord(state) && isRecord(state.template)
258
- ? state
259
- : { version: 1, template: {} };
260
- }
261
- catch {
262
- throw new Error(`${file} is not valid JSON; move it aside before syncing.`);
263
- }
264
- }
265
- function saveProjectState(cwd, state) {
266
- write(join(cwd, PROJECT_STATE_FILE), `${JSON.stringify(state, null, 2)}\n`);
267
- }
268
- function templatePrompt() {
269
- return `Template reference updated. Ask Codex:
2
+ import{realpathSync as $e}from"node:fs";import{resolve as io}from"node:path";import{fileURLToPath as ao}from"node:url";import{copyFileSync as Ve,existsSync as m,mkdirSync as We,readdirSync as j,rmSync as T,statSync as ge}from"node:fs";import{join as d}from"node:path";import{createHash as Te}from"node:crypto";import{copyFileSync as Oe,existsSync as H,mkdirSync as _e,readFileSync as Q,renameSync as Ce,writeFileSync as je}from"node:fs";import{dirname as Le}from"node:path";var k=o=>typeof o=="object"&&o!==null,S=o=>Te("sha256").update(o).digest("hex"),b=o=>Q(o),g=o=>Q(o,"utf8");function E(o){if(!H(o))return null;let e=()=>new Date().toISOString().replace(/[-:TZ.]/g,""),t=`${o}.codex-kit.bak-${e()}`,n=1;for(;H(t);)t=`${o}.codex-kit.bak-${e()}-${n++}`;return Oe(o,t),console.log(`backup: ${t}`),t}function u(o,e){_e(Le(o),{recursive:!0});let t=`${o}.codex-kit.tmp-${process.pid}`;je(t,e),Ce(t,o)}function K(o){if(!H(o))return{};try{let e=JSON.parse(g(o));if(k(e))return e}catch{}throw new Error(`${o} must contain a JSON object; fix or move it before installing.`)}import{readFileSync as Ne}from"node:fs";import{dirname as Ge,join as $,resolve as Re}from"node:path";import{fileURLToPath as Ie}from"node:url";var M=Re(Ge(Ie(import.meta.url)),".."),N=$(M,"assets"),F=$(N,"agents"),ee=$(N,"skills"),h="codex-kit-reconcile-agents",oe=$(ee,h,"SKILL.md"),te=$(ee,h,"agents","openai.yaml"),U=$(N,"SUBAGENT_ROUTING.md"),ne=$(M,"bin","routing-hook.js"),D=$(N,"TEMPLATE_AGENTS.md"),p=JSON.parse(Ne($(M,"package.json"),"utf8")),J=p.publishConfig?.registry??"https://registry.npmjs.org";import{existsSync as ie,rmSync as Ke}from"node:fs";import{join as Me}from"node:path";import{copyFileSync as Pe,existsSync as G,rmSync as He}from"node:fs";import{join as re}from"node:path";var _=".codex-kit-state.json";function O(o){let e=re(o,_);if(!G(e))return{version:p.version,files:{}};try{let t=JSON.parse(g(e));return k(t)&&k(t.files)?t:{version:p.version,files:{}}}catch{throw new Error(`${e} is not valid JSON; move it aside before reinstalling.`)}}function R(o,e){u(re(o,_),`${JSON.stringify(e,null,2)}
3
+ `)}function B(o,e,t,n,r){let i=b(o),s=S(i),a=n.files[t];if(!G(e))return u(e,i),console.log(`installed: ${e}`),{target:e,hash:s,ownership:"created",backup:null};let c=S(b(e));if(c===s)return console.log(`unchanged: ${e}`),a??{target:e,hash:s,ownership:"preexisting",backup:null};let f=a&&a.target===e&&a.ownership!=="preexisting"&&a.hash===c;if(!f&&!r)return console.warn(`preserved modified or pre-existing file: ${e} (use --force to replace)`),a??null;let l=E(e);return u(e,i),console.log(`updated: ${e}`),{target:e,hash:s,ownership:f?a.ownership:"replaced",backup:f?a.backup:l}}function se(o){let{target:e}=o;!G(e)||S(b(e))!==o.hash?console.warn(`preserved modified or missing file: ${e}`):o.ownership==="created"?(He(e),console.log(`removed: ${e}`)):o.ownership==="replaced"&&o.backup&&G(o.backup)?(Pe(o.backup,e),console.log(`restored: ${e}`)):console.log(`preserved pre-existing file: ${e}`)}function I(o){let e=new Map,t=!1;for(let n of o.split(`
4
+ `)){if(/^\s*\[/.test(n)){t=!0;continue}if(t)continue;let r=/^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(n),i=r?.[2],s=r?.[3];i&&s!==void 0&&!e.has(i)&&e.set(i,{value:s,line:n})}return e}var C=o=>JSON.stringify(o);function Fe(o,e){let t=o.split(`
5
+ `),n=new Set,r=t.findIndex(s=>/^\s*\[/.test(s));r<0&&(r=t.length);for(let s=0;s<r;s++){let a=/^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(t[s]??""),c=a?.[2];!a||!c||n.has(c)||(t[s]=`${a[1]}${c} = ${C(e[c])}`,n.add(c))}let i=Object.keys(e).filter(s=>!n.has(s)).map(s=>`${s} = ${C(e[s])}`);return i.length&&t.splice(0,0,...i,""),t.join(`
6
+ `)}function Ue(o,e){let t=o.split(`
7
+ `),n=t.findIndex(i=>/^\s*\[/.test(i));n<0&&(n=t.length);let r=new Set;for(let i=0;i<n;i++){let s=/^(\s*)(model|model_reasoning_effort|plan_mode_reasoning_effort)\s*=\s*(.*?)\s*$/.exec(t[i]??""),a=s?.[2];if(!s||!a||r.has(a)||s[3]!==C(e.desired[a]))continue;let c=e.previous[a];c?.present?t[i]=`${s[1]}${a} = ${c.value}`:(t.splice(i,1),i--,n--),r.add(a)}return Object.values(e.previous).some(i=>i&&!i.present)&&t[0]===""&&t.shift(),t.join(`
8
+ `)}function V(o){let e=o.codexHome,t=Me(e,"config.toml"),n={model:o.orchestrator,model_reasoning_effort:o.reasoningEffort,plan_mode_reasoning_effort:o.planReasoningEffort},r=O(e),i=ie(t)?g(t):"",s=I(i);if(r.config?.target===t&&Object.entries(r.config.desired).some(([l,x])=>s.get(l)?.value!==C(x))&&!o.force){console.warn(`preserved modified config: ${t} (use --force to replace)`);return}let a=Fe(i,n);a!==i?(E(t),u(t,a),console.log(`configured orchestrator: ${t}`)):console.log(`unchanged: ${t}`);let c=r.config?.previous??{};for(let f of Object.keys(n)){if(f in c)continue;let l=s.get(f);c[f]=l?{present:!0,value:l.value}:{present:!1}}r.version=p.version,r.config={target:t,desired:n,previous:c},R(e,r),console.log(`Orchestrator: ${n.model}`),console.log(`Reasoning effort: ${n.model_reasoning_effort}`),console.log(`Plan mode reasoning effort: ${n.plan_mode_reasoning_effort}`)}function ae(o){if(!ie(o.target)){console.warn(`preserved missing config: ${o.target}`);return}let e=g(o.target),t=I(e);if(Object.entries(o.desired).some(([i,s])=>t.get(i)?.value!==C(s))){console.warn(`preserved modified config: ${o.target}`);return}let r=Ue(e,o);r!==e&&(E(o.target),r.trim()?u(o.target,r):Ke(o.target),console.log(`restored config: ${o.target}`))}import{existsSync as W,rmSync as De}from"node:fs";import{join as le}from"node:path";var Je=o=>`'${o.replaceAll("'",`'"'"'`)}'`,Be=o=>({command:`/usr/bin/env node ${Je(o)}`,commandWindows:`node ${JSON.stringify(o)}`});function ce(o,e){let t=o.hooks;if(k(t))for(let[n,r]of Object.entries(t)){if(!Array.isArray(r))continue;let i=r.flatMap(s=>{if(!k(s)||!Array.isArray(s.hooks))return[s];let a=s.hooks.filter(c=>!k(c)||c.command!==e.command&&c.commandWindows!==e.commandWindows);return a.length?[{...s,hooks:a}]:[]});i.length?t[n]=i:delete t[n]}}function fe(o,e){let t=le(o,"hooks.json"),n=Be(le(o,"codex-kit","routing-hook.js")),r=e?.created??!W(t),i=K(t);e&&ce(i,e);let s=k(i.hooks)?i.hooks:{};i.hooks=s;let a={type:"command",...n,timeout:5};s.UserPromptSubmit=[...Array.isArray(s.UserPromptSubmit)?s.UserPromptSubmit:[],{hooks:[{...a,statusMessage:"Loading subagent routing"}]}],s.SubagentStart=[...Array.isArray(s.SubagentStart)?s.SubagentStart:[],{hooks:[{...a,statusMessage:"Briefing delegated worker"}]}];let c=`${JSON.stringify(i,null,2)}
9
+ `,f=W(t)?g(t):"";return c!==f&&(E(t),u(t,c),console.log(`updated: ${t}`)),{target:t,...n,created:r}}function de(o){if(!W(o.target)){console.warn(`preserved missing hooks file: ${o.target}`);return}let e=K(o.target);ce(e,o),k(e.hooks)&&!Object.keys(e.hooks).length&&delete e.hooks,E(o.target),o.created&&!Object.keys(e).length?De(o.target):u(o.target,`${JSON.stringify(e,null,2)}
10
+ `),console.log(`removed managed routing hooks from: ${o.target}`)}var z="<!-- BEGIN codex-kit:subagent-routing -->",pe="<!-- END codex-kit:subagent-routing -->",ze=(o,e,t)=>`${e}
11
+ ${o.trimEnd()}
12
+ ${t}`;function qe(o,e,t,n){let r=o.indexOf(t),i=o.indexOf(n);if(r>=0!=i>=0||r>=0&&i<r)throw new Error(`Malformed managed block: expected both ${t} and ${n}.`);let s=ze(e,t,n);return r>=0?`${o.slice(0,r)}${s}${o.slice(i+n.length)}`:`${o.trimEnd()}${o.trim()?`
270
13
 
271
- The project's TEMPLATE_AGENTS.md was refreshed from codex-kit. Compare it with
272
- AGENTS.md and merge only new or changed reusable guidelines that apply to this
273
- repository.
14
+ `:""}${s}
15
+ `}function Xe(o,e,t){let n=o.indexOf(e),r=o.indexOf(t);if(n<0&&r<0)return o;if(n<0||r<n)throw new Error("Malformed managed block in AGENTS.md.");let i=o.slice(0,n).trimEnd(),s=o.slice(r+t.length).trimStart();return`${i}${i&&s?`
274
16
 
275
- Keep durable, always-applicable repository rules in AGENTS.md. When the updated
276
- template identifies a repeatable, task-specific procedure, inspect existing
277
- project skills under .agents/skills and create or update a skill only when it
278
- would reduce conditional detail in AGENTS.md. Preserve existing relevant skills
279
- and avoid duplicating detailed instructions between AGENTS.md and SKILL.md.
17
+ `:""}${s}${i||s?`
18
+ `:""}`}function me(o){let e=o.codexHome;We(e,{recursive:!0});let t=O(e),n={version:p.version,files:{},globalAgents:null,hooks:null,config:t.config??null};for(let f of j(F).filter(l=>l.endsWith(".toml")).sort()){let l=`agents/${f}`,x=B(d(F,f),d(e,"agents",f),l,t,o.force);x&&(n.files[l]=x)}let r=[[U,d(e,"SUBAGENT_ROUTING.md"),"routing"],[oe,d(e,"skills",h,"SKILL.md"),`skills/${h}/SKILL.md`],[te,d(e,"skills",h,"agents","openai.yaml"),`skills/${h}/agents/openai.yaml`],[ne,d(e,"codex-kit","routing-hook.js"),"routing-hook"]];for(let[f,l,x]of r){let L=B(f,l,x,t,o.force);L&&(n.files[x]=L)}for(let[f,l]of Object.entries(t.files))f in n.files||(!m(l.target)||S(b(l.target))!==l.hash?(console.warn(`preserved stale modified or missing file: ${l.target}`),n.files[f]=l):l.ownership==="created"?(T(l.target),console.log(`removed stale: ${l.target}`)):l.ownership==="replaced"&&l.backup&&m(l.backup)&&(Ve(l.backup,l.target),console.log(`restored stale: ${l.target}`)));let i=d(e,"AGENTS.md"),s=m(i)?g(i):"",a=qe(s,g(U),z,pe);a!==s&&(E(i),u(i,a),console.log(`updated: ${i}`)),n.globalAgents={target:i},n.hooks=fe(e,t.hooks),R(e,n);let c=d(e,"agents","commit-pusher.toml");m(c)&&console.warn(`warning: existing unmanaged commit-pusher remains at ${c}`),console.log(`Codex kit ${p.version} installed under ${e}`)}function ue(o){let e=o.codexHome,t=d(e,"config.toml"),n=d(e,"AGENTS.md"),r=O(e),i=m(t)?I(g(t)):new Map,s=y=>{let v=i.get(y)?.value;if(!v)return"not set";try{return String(JSON.parse(v))}catch{return v}};console.log(`Codex home: ${e}`),console.log(`Config: ${t}${m(t)?"":" (missing)"}`),console.log(`Orchestrator: ${s("model")}`),console.log(`Reasoning effort: ${s("model_reasoning_effort")}`),console.log(`Plan mode reasoning effort: ${s("plan_mode_reasoning_effort")}`),console.log(`Kit state: ${m(d(e,_))?r.version:"not installed"}`),console.log(`Global routing: ${m(n)&&g(n).includes(z)?"installed":"not installed"}`),console.log(`Routing file: ${m(d(e,"SUBAGENT_ROUTING.md"))?d(e,"SUBAGENT_ROUTING.md"):"missing"}`);let a=r.hooks;console.log(`Routing hook: ${a&&m(a.target)&&g(a.target).includes(a.command)?"installed":"not installed"}`);let c=[[d(e,"skills",h,"SKILL.md"),r.files[`skills/${h}/SKILL.md`]],[d(e,"skills",h,"agents","openai.yaml"),r.files[`skills/${h}/agents/openai.yaml`]]],f=c.every(([y,v])=>m(y)&&v&&S(b(y))===v.hash)?"installed":c.some(([y])=>m(y))?"modified or incomplete":"missing";console.log(`Reconciliation skill: ${f}`),console.log("Custom agents:");let l=d(e,"agents"),x=m(l)?j(l).filter(y=>y.endsWith(".toml")).sort():[];if(!x.length){console.log(" (none)");return}let L=new Set(Object.values(r.files).map(y=>y.target));for(let y of x){let v=d(l,y),we=g(v),P=Ae=>new RegExp(`^${Ae}\\s*=\\s*"([^"]*)"`,"m").exec(we)?.[1]??"not set";console.log(` ${P("name")} \u2014 ${P("model")}, ${P("model_reasoning_effort")} (${L.has(v)?"managed":"unmanaged"})`)}}function he(o){let e=o.codexHome,t=d(e,_);if(!m(t)){console.log(`No installer state at ${t}; nothing removed.`);return}let n=O(e);for(let f of Object.values(n.files))se(f);let r=n.globalAgents?.target??d(e,"AGENTS.md");if(m(r)){let f=g(r),l=Xe(f,z,pe);l!==f&&(E(r),l?u(r,l):T(r),console.log(`removed managed routing from: ${r}`))}n.config&&ae(n.config),n.hooks&&de(n.hooks);let i=d(e,"skills",h),s=d(i,"agents");m(s)&&ge(s).isDirectory()&&!j(s).length&&T(s,{recursive:!0}),m(i)&&ge(i).isDirectory()&&!j(i).length&&T(i,{recursive:!0});let a=d(e,"codex-kit","allowances");m(a)&&T(a,{recursive:!0,force:!0});let c=d(e,"codex-kit");m(c)&&!j(c).length&&T(c,{recursive:!0}),T(t),console.log(`Codex kit uninstalled from ${e}`)}import{existsSync as w,statSync as Ye}from"node:fs";import{join as A}from"node:path";var ke=".codex-kit-state.json",Ze="<!-- BEGIN codex-kit:shared-template -->",Qe="<!-- END codex-kit:shared-template -->";function q(o){let e=A(o,ke);if(!w(e))return{version:1,template:{}};try{let t=JSON.parse(g(e));return k(t)&&k(t.template)?t:{version:1,template:{}}}catch{throw new Error(`${e} is not valid JSON; move it aside before syncing.`)}}var Se=(o,e)=>u(A(o,ke),`${JSON.stringify(e,null,2)}
19
+ `),X=o=>{if(!w(o)||!Ye(o).isDirectory())throw new Error(`Not a directory: ${o}`)};function eo(){return`Template reference updated. Use the global $${h} skill to reconcile it semantically.
280
20
 
281
- Keep critical safety, authorization, database, deployment, and destructive-
282
- operation restrictions in AGENTS.md even when a skill contains the detailed
283
- workflow. Do not create speculative skills.
284
-
285
- Preserve project-specific instructions and existing adaptations. Do not replace
286
- AGENTS.md wholesale. If a template rule conflicts with a local rule, keep the
287
- local rule and report the conflict.
288
-
289
- Summarize what was added, updated, skipped, adapted, or moved into a skill, and
290
- why. Validate any created or modified skills. When finished, run codex-kit
291
- project mark-applied.`;
292
- }
293
- function managedBlock(content, begin, end) {
294
- return `${begin}\n${content.trimEnd()}\n${end}`;
295
- }
296
- function replaceOrAppendBlock(original, content, begin, end) {
297
- const start = original.indexOf(begin);
298
- const finish = original.indexOf(end);
299
- if ((start >= 0) !== (finish >= 0) || (start >= 0 && finish < start)) {
300
- throw new Error(`Malformed managed block: expected both ${begin} and ${end}.`);
301
- }
302
- const block = managedBlock(content, begin, end);
303
- if (start >= 0)
304
- return `${original.slice(0, start)}${block}${original.slice(finish + end.length)}`;
305
- return `${original.trimEnd()}${original.trim() ? "\n\n" : ""}${block}\n`;
306
- }
307
- function removeBlock(original, begin, end) {
308
- const start = original.indexOf(begin);
309
- const finish = original.indexOf(end);
310
- if (start < 0 && finish < 0)
311
- return original;
312
- if (start < 0 || finish < start)
313
- throw new Error(`Malformed managed block in AGENTS.md.`);
314
- const before = original.slice(0, start).trimEnd();
315
- const after = original.slice(finish + end.length).trimStart();
316
- return `${before}${before && after ? "\n\n" : ""}${after}${before || after ? "\n" : ""}`;
317
- }
318
- function installFile(source, target, key, prior, force) {
319
- const sourceData = read(source);
320
- const sourceHash = sha256(sourceData);
321
- const previous = prior.files[key];
322
- if (!existsSync(target)) {
323
- write(target, sourceData);
324
- console.log(`installed: ${target}`);
325
- return { target, hash: sourceHash, ownership: "created", backup: null };
326
- }
327
- const targetHash = sha256(read(target));
328
- if (targetHash === sourceHash) {
329
- console.log(`unchanged: ${target}`);
330
- return previous ?? { target, hash: sourceHash, ownership: "preexisting", backup: null };
331
- }
332
- const safelyOwned = previous &&
333
- previous.target === target &&
334
- previous.ownership !== "preexisting" &&
335
- previous.hash === targetHash;
336
- if (!safelyOwned && !force) {
337
- console.warn(`preserved modified or pre-existing file: ${target} (use --force to replace)`);
338
- return previous ?? null;
339
- }
340
- const newBackup = backup(target);
341
- write(target, sourceData);
342
- console.log(`updated: ${target}`);
343
- return {
344
- target,
345
- hash: sourceHash,
346
- ownership: safelyOwned ? previous.ownership : "replaced",
347
- backup: safelyOwned ? previous.backup : newBackup,
348
- };
349
- }
350
- function installGlobal(options) {
351
- const home = options.codexHome;
352
- mkdirSync(home, { recursive: true });
353
- const prior = loadState(home);
354
- const next = {
355
- version: PACKAGE.version,
356
- files: {},
357
- globalAgents: null,
358
- hooks: null,
359
- config: prior.config ?? null,
360
- };
361
- for (const name of readdirSync(AGENTS_DIR).filter((name) => name.endsWith(".toml")).sort()) {
362
- const key = `agents/${name}`;
363
- const record = installFile(join(AGENTS_DIR, name), join(home, "agents", name), key, prior, options.force);
364
- if (record)
365
- next.files[key] = record;
366
- }
367
- const routingRecord = installFile(ROUTING_FILE, join(home, "SUBAGENT_ROUTING.md"), "routing", prior, options.force);
368
- if (routingRecord)
369
- next.files.routing = routingRecord;
370
- const hookRecord = installFile(ROUTING_HOOK_FILE, join(home, "codex-kit", "routing-hook.js"), "routing-hook", prior, options.force);
371
- if (hookRecord)
372
- next.files["routing-hook"] = hookRecord;
373
- for (const [key, record] of Object.entries(prior.files)) {
374
- if (key in next.files)
375
- continue;
376
- const target = record.target;
377
- if (!existsSync(target) || sha256(read(target)) !== record.hash) {
378
- console.warn(`preserved stale modified or missing file: ${target}`);
379
- next.files[key] = record;
380
- continue;
381
- }
382
- if (record.ownership === "created") {
383
- rmSync(target);
384
- console.log(`removed stale: ${target}`);
385
- }
386
- else if (record.ownership === "replaced" && record.backup && existsSync(record.backup)) {
387
- copyFileSync(record.backup, target);
388
- console.log(`restored stale: ${target}`);
389
- }
390
- }
391
- const globalAgents = join(home, "AGENTS.md");
392
- const original = existsSync(globalAgents) ? readText(globalAgents) : "";
393
- const updated = replaceOrAppendBlock(original, readText(ROUTING_FILE), GLOBAL_BEGIN, GLOBAL_END);
394
- if (updated !== original) {
395
- backup(globalAgents);
396
- write(globalAgents, updated);
397
- console.log(`updated: ${globalAgents}`);
398
- }
399
- next.globalAgents = { target: globalAgents };
400
- next.hooks = installRoutingHooks(home, prior.hooks);
401
- saveState(home, next);
402
- const commitPusher = join(home, "agents", "commit-pusher.toml");
403
- if (existsSync(commitPusher)) {
404
- console.warn(`warning: existing unmanaged commit-pusher remains at ${commitPusher}`);
405
- }
406
- console.log(`Codex kit ${PACKAGE.version} installed under ${home}`);
407
- }
408
- function configureGlobal(options) {
409
- const home = options.codexHome;
410
- mkdirSync(home, { recursive: true });
411
- const configFile = join(home, "config.toml");
412
- const desired = {
413
- model: options.orchestrator,
414
- model_reasoning_effort: options.reasoningEffort,
415
- plan_mode_reasoning_effort: options.planReasoningEffort,
416
- };
417
- const priorState = loadState(home);
418
- const original = existsSync(configFile) ? readText(configFile) : "";
419
- const current = topLevelConfigEntries(original);
420
- const priorConfig = priorState.config;
421
- if (priorConfig?.target === configFile) {
422
- const changedByUser = Object.entries(priorConfig.desired).some(([key, value]) => {
423
- const entry = current.get(key);
424
- return !entry || entry.value !== tomlString(value);
425
- });
426
- if (changedByUser && !options.force) {
427
- console.warn(`preserved modified config: ${configFile} (use --force to replace)`);
428
- return;
429
- }
430
- }
431
- const updated = setTopLevelConfig(original, desired);
432
- if (updated !== original) {
433
- backup(configFile);
434
- write(configFile, updated);
435
- console.log(`configured orchestrator: ${configFile}`);
436
- }
437
- else
438
- console.log(`unchanged: ${configFile}`);
439
- const previous = priorConfig?.previous ?? {};
440
- for (const key of Object.keys(desired)) {
441
- if (key in previous)
442
- continue;
443
- const entry = current.get(key);
444
- previous[key] = entry
445
- ? { present: true, value: entry.value }
446
- : { present: false };
447
- }
448
- priorState.version = PACKAGE.version;
449
- priorState.config = {
450
- target: configFile,
451
- desired,
452
- previous,
453
- };
454
- saveState(home, priorState);
455
- console.log(`Orchestrator: ${desired.model}`);
456
- console.log(`Reasoning effort: ${desired.model_reasoning_effort}`);
457
- console.log(`Plan mode reasoning effort: ${desired.plan_mode_reasoning_effort}`);
458
- }
459
- function listGlobal(options) {
460
- const home = options.codexHome;
461
- const configFile = join(home, "config.toml");
462
- const globalAgents = join(home, "AGENTS.md");
463
- const routingFile = join(home, "SUBAGENT_ROUTING.md");
464
- const agentsDir = join(home, "agents");
465
- const stateFile = join(home, STATE_FILE);
466
- const state = loadState(home);
467
- const config = existsSync(configFile) ? topLevelConfigEntries(readText(configFile)) : new Map();
468
- const value = (key) => {
469
- const raw = config.get(key)?.value;
470
- if (!raw)
471
- return "not set";
472
- try {
473
- return String(JSON.parse(raw));
474
- }
475
- catch {
476
- return raw;
477
- }
478
- };
479
- const managedTargets = new Set(Object.values(state.files).map((record) => record.target));
480
- console.log(`Codex home: ${home}`);
481
- console.log(`Config: ${configFile}${existsSync(configFile) ? "" : " (missing)"}`);
482
- console.log(`Orchestrator: ${value("model")}`);
483
- console.log(`Reasoning effort: ${value("model_reasoning_effort")}`);
484
- console.log(`Plan mode reasoning effort: ${value("plan_mode_reasoning_effort")}`);
485
- console.log(`Kit state: ${existsSync(stateFile) ? state.version : "not installed"}`);
486
- const hasRoutingBlock = existsSync(globalAgents) && readText(globalAgents).includes(GLOBAL_BEGIN);
487
- console.log(`Global routing: ${hasRoutingBlock ? "installed" : "not installed"}`);
488
- console.log(`Routing file: ${existsSync(routingFile) ? routingFile : "missing"}`);
489
- const routingHook = state.hooks;
490
- const hooksInstalled = Boolean(routingHook &&
491
- existsSync(routingHook.target) &&
492
- readText(routingHook.target).includes(routingHook.command));
493
- console.log(`Routing hook: ${hooksInstalled ? "installed" : "not installed"}`);
494
- console.log("Custom agents:");
495
- const agents = existsSync(agentsDir)
496
- ? readdirSync(agentsDir).filter((name) => name.endsWith(".toml")).sort()
497
- : [];
498
- if (!agents.length)
499
- return console.log(" (none)");
500
- for (const filename of agents) {
501
- const file = join(agentsDir, filename);
502
- const contents = readText(file);
503
- const field = (key) => new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m").exec(contents)?.[1] ?? "not set";
504
- const ownership = managedTargets.has(file) ? "managed" : "unmanaged";
505
- console.log(` ${field("name")} — ${field("model")}, ${field("model_reasoning_effort")} (${ownership})`);
506
- }
507
- }
508
- function uninstallGlobal(options) {
509
- const home = options.codexHome;
510
- const statePath = join(home, STATE_FILE);
511
- if (!existsSync(statePath)) {
512
- console.log(`No installer state at ${statePath}; nothing removed.`);
513
- return;
514
- }
515
- const state = loadState(home);
516
- for (const record of Object.values(state.files)) {
517
- const target = record.target;
518
- if (!existsSync(target) || sha256(read(target)) !== record.hash) {
519
- console.warn(`preserved modified or missing file: ${target}`);
520
- continue;
521
- }
522
- if (record.ownership === "created") {
523
- rmSync(target);
524
- console.log(`removed: ${target}`);
525
- }
526
- else if (record.ownership === "replaced" && record.backup && existsSync(record.backup)) {
527
- copyFileSync(record.backup, target);
528
- console.log(`restored: ${target}`);
529
- }
530
- else {
531
- console.log(`preserved pre-existing file: ${target}`);
532
- }
533
- }
534
- const globalAgents = state.globalAgents?.target ?? join(home, "AGENTS.md");
535
- if (existsSync(globalAgents)) {
536
- const original = readText(globalAgents);
537
- const updated = removeBlock(original, GLOBAL_BEGIN, GLOBAL_END);
538
- if (updated !== original) {
539
- backup(globalAgents);
540
- if (updated)
541
- write(globalAgents, updated);
542
- else
543
- rmSync(globalAgents);
544
- console.log(`removed managed routing from: ${globalAgents}`);
545
- }
546
- }
547
- if (state.config?.target) {
548
- const configFile = state.config.target;
549
- if (!existsSync(configFile)) {
550
- console.warn(`preserved missing config: ${configFile}`);
551
- }
552
- else {
553
- const originalConfig = readText(configFile);
554
- const current = topLevelConfigEntries(originalConfig);
555
- const changed = Object.entries(state.config.desired).some(([key, value]) => {
556
- const entry = current.get(key);
557
- return !entry || entry.value !== tomlString(value);
558
- });
559
- if (changed) {
560
- console.warn(`preserved modified config: ${configFile}`);
561
- }
562
- else {
563
- const restored = restoreTopLevelConfig(originalConfig, state.config);
564
- if (restored !== originalConfig) {
565
- backup(configFile);
566
- if (restored.trim())
567
- write(configFile, restored);
568
- else
569
- rmSync(configFile);
570
- console.log(`restored config: ${configFile}`);
571
- }
572
- }
573
- }
574
- }
575
- if (state.hooks)
576
- uninstallRoutingHooks(state.hooks);
577
- const allowancesDir = join(home, "codex-kit", "allowances");
578
- if (existsSync(allowancesDir))
579
- rmSync(allowancesDir, { recursive: true, force: true });
580
- const kitDir = join(home, "codex-kit");
581
- if (existsSync(kitDir) && !readdirSync(kitDir).length)
582
- rmSync(kitDir, { recursive: true });
583
- rmSync(statePath);
584
- console.log(`Codex kit uninstalled from ${home}`);
585
- }
586
- function syncProject(options) {
587
- const cwd = options.cwd;
588
- if (!existsSync(cwd) || !statSync(cwd).isDirectory())
589
- throw new Error(`Not a directory: ${cwd}`);
590
- const agentsFile = join(cwd, "AGENTS.md");
591
- const stagedTemplate = join(cwd, "TEMPLATE_AGENTS.md");
592
- const template = readText(TEMPLATE_FILE);
593
- const desired = Buffer.from(template);
594
- const sourceHash = sha256(desired);
595
- const state = loadProjectState(cwd);
596
- const previousAvailable = state.template.availableHash;
597
- if (existsSync(stagedTemplate)) {
598
- const currentHash = sha256(read(stagedTemplate));
599
- const locallyModified = currentHash !== sourceHash && (!previousAvailable || currentHash !== previousAvailable);
600
- if (locallyModified && currentHash !== sourceHash && !options.force) {
601
- console.warn(`preserved locally modified template: ${stagedTemplate} (use --force to replace)`);
602
- console.log(`The installed kit has template ${PACKAGE.version}; review the local change before syncing.`);
603
- return;
604
- }
605
- if (currentHash === sourceHash)
606
- console.log(`unchanged: ${stagedTemplate}`);
607
- else {
608
- backup(stagedTemplate);
609
- write(stagedTemplate, desired);
610
- console.log(`refreshed template reference: ${stagedTemplate}`);
611
- }
612
- }
613
- else {
614
- write(stagedTemplate, desired);
615
- console.log(`created template reference: ${stagedTemplate}`);
616
- }
617
- state.version = 1;
618
- state.template = {
619
- ...state.template,
620
- availableHash: sourceHash,
621
- availableVersion: PACKAGE.version,
622
- };
623
- saveProjectState(cwd, state);
624
- if (!existsSync(agentsFile)) {
625
- const contents = `# Project-Specific Instructions
21
+ Inspect TEMPLATE_AGENTS.md, AGENTS.md, .codex-kit-state.json, existing
22
+ .agents/skills, and codex-kit project status. Preserve local adaptations and
23
+ AGENTS.md organization; merge only applicable reusable guidance. Keep critical
24
+ always-on safety and authorization rules in AGENTS.md, extract only concrete
25
+ conditional procedures into validated skills, and do not copy the complete
26
+ template or introduce managed markers. Mark applied only after reconciliation
27
+ and validation, then report any template-worthy generalized promotion.`}function Ee(o){let{cwd:e}=o;X(e);let t=A(e,"AGENTS.md"),n=A(e,"TEMPLATE_AGENTS.md"),r=Buffer.from(g(D)),i=S(r),s=q(e);if(w(n)){let a=S(b(n));if(a!==i&&(!s.template.availableHash||a!==s.template.availableHash)&&!o.force){console.warn(`preserved locally modified template: ${n} (use --force to replace)`),console.log(`The installed kit has template ${p.version}; review the local change before syncing.`);return}a===i?console.log(`unchanged: ${n}`):(E(n),u(n,r),console.log(`refreshed template reference: ${n}`))}else u(n,r),console.log(`created template reference: ${n}`);if(s.version=1,s.template={...s.template,availableHash:i,availableVersion:p.version},Se(e,s),!w(t))u(t,`# Project-Specific Instructions
626
28
 
627
29
  <!-- Add repository-specific commands, architecture, and exceptions here. -->
628
- `;
629
- write(agentsFile, contents);
630
- console.log(`created project instructions file: ${agentsFile}`);
631
- }
632
- else if (readText(agentsFile).includes(PROJECT_BEGIN) || readText(agentsFile).includes(PROJECT_END)) {
633
- console.warn(`preserved legacy managed template in: ${agentsFile}`);
634
- console.warn("Ask Codex to migrate it to semantic template reconciliation before applying updates.");
635
- }
636
- console.log(templatePrompt());
637
- }
638
- function projectStatus(options) {
639
- const cwd = options.cwd;
640
- if (!existsSync(cwd) || !statSync(cwd).isDirectory())
641
- throw new Error(`Not a directory: ${cwd}`);
642
- const stagedTemplate = join(cwd, "TEMPLATE_AGENTS.md");
643
- const agentsFile = join(cwd, "AGENTS.md");
644
- const state = loadProjectState(cwd);
645
- const availableHash = state.template.availableHash ?? null;
646
- const appliedHash = state.template.appliedHash ?? null;
647
- const sourceHash = sha256(read(TEMPLATE_FILE));
648
- const localHash = existsSync(stagedTemplate) ? sha256(read(stagedTemplate)) : null;
649
- console.log(`Project: ${cwd}`);
650
- if (!localHash)
651
- return console.log("Status: not initialized (run codex-kit project sync)");
652
- if (!existsSync(agentsFile))
653
- return console.log("Status: AGENTS.md missing (reconcile the template first)");
654
- console.log(`Available: ${state.template.availableVersion ?? "unknown"} (${availableHash ?? "untracked"})`);
655
- console.log(`Applied: ${appliedHash ?? "never"}`);
656
- if (sourceHash !== availableHash)
657
- return console.log("Status: kit template update available; run project sync");
658
- if (localHash !== availableHash)
659
- return console.log("Status: local template changed; review it before syncing");
660
- if (appliedHash !== localHash)
661
- return console.log("Status: reconciliation required");
662
- console.log("Status: up to date");
663
- }
664
- function markApplied(options) {
665
- const cwd = options.cwd;
666
- if (!existsSync(cwd) || !statSync(cwd).isDirectory())
667
- throw new Error(`Not a directory: ${cwd}`);
668
- const stagedTemplate = join(cwd, "TEMPLATE_AGENTS.md");
669
- const agentsFile = join(cwd, "AGENTS.md");
670
- if (!existsSync(stagedTemplate))
671
- throw new Error(`Missing ${stagedTemplate}; run project sync first.`);
672
- if (!existsSync(agentsFile))
673
- throw new Error(`Missing ${agentsFile}; reconcile the template into AGENTS.md first.`);
674
- const state = loadProjectState(cwd);
675
- const appliedHash = sha256(read(stagedTemplate));
676
- state.version = 1;
677
- state.template = {
678
- ...state.template,
679
- appliedHash,
680
- appliedAt: new Date().toISOString(),
681
- };
682
- saveProjectState(cwd, state);
683
- console.log(`recorded template reconciliation: ${stagedTemplate}`);
684
- }
685
- function compareVersions(left, right) {
686
- const parseVersion = (value) => {
687
- const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(value);
688
- if (!match)
689
- throw new Error(`Invalid package version: ${value}`);
690
- return {
691
- numbers: [Number(match[1]), Number(match[2]), Number(match[3])],
692
- prerelease: match[4] ?? null,
693
- };
694
- };
695
- const a = parseVersion(left);
696
- const b = parseVersion(right);
697
- for (let index = 0; index < 3; index++) {
698
- const leftNumber = a.numbers[index];
699
- const rightNumber = b.numbers[index];
700
- if (leftNumber !== rightNumber)
701
- return Math.sign(leftNumber - rightNumber);
702
- }
703
- if (a.prerelease === b.prerelease)
704
- return 0;
705
- if (!a.prerelease)
706
- return 1;
707
- if (!b.prerelease)
708
- return -1;
709
- return Math.sign(a.prerelease.localeCompare(b.prerelease, "en", { numeric: true }));
710
- }
711
- function checkVersion() {
712
- let latest = process.env.CODEX_KIT_LATEST_VERSION;
713
- if (!latest) {
714
- const executable = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
715
- const result = spawnSync(executable, ["view", PACKAGE.name, "version", "--json", `--registry=${REGISTRY}`], { encoding: "utf8", timeout: 15_000 });
716
- if (result.error)
717
- throw new Error(`Unable to run pnpm: ${result.error.message}`);
718
- if (result.status !== 0) {
719
- const detail = result.stderr.trim() || "pnpm view failed";
720
- throw new Error(`Unable to check ${REGISTRY}: ${detail}`);
721
- }
722
- try {
723
- const value = JSON.parse(result.stdout);
724
- latest = Array.isArray(value) && typeof value.at(-1) === "string"
725
- ? value.at(-1)
726
- : typeof value === "string"
727
- ? value
728
- : undefined;
729
- }
730
- catch {
731
- latest = result.stdout.trim();
732
- }
733
- }
734
- if (typeof latest !== "string" || !latest)
735
- throw new Error("Registry returned no package version.");
736
- console.log(`Installed: ${PACKAGE.version}`);
737
- console.log(`Latest: ${latest}`);
738
- const comparison = compareVersions(PACKAGE.version, latest);
739
- if (comparison === 0)
740
- return console.log("codex-kit is up to date.");
741
- if (comparison > 0)
742
- return console.log("This local build is newer than the published package.");
743
- console.log(`Update available. Run:
744
- pnpm add --global ${PACKAGE.name}@latest
745
- codex-kit global install`);
746
- }
747
- function parse(argv) {
748
- const options = {
749
- cwd: process.cwd(),
750
- codexHome: resolve(process.env.CODEX_HOME || join(homedir(), ".codex")),
751
- orchestrator: DEFAULT_ORCHESTRATOR,
752
- reasoningEffort: DEFAULT_REASONING_EFFORT,
753
- planReasoningEffort: DEFAULT_PLAN_REASONING_EFFORT,
754
- force: false,
755
- positionals: [],
756
- };
757
- for (let index = 0; index < argv.length; index++) {
758
- const arg = argv[index];
759
- if (arg === undefined)
760
- continue;
761
- if (arg === "--force")
762
- options.force = true;
763
- else if (arg === "--cwd" || arg === "--codex-home") {
764
- const value = argv[++index];
765
- if (!value)
766
- throw new Error(`${arg} requires a path.`);
767
- if (arg === "--cwd")
768
- options.cwd = resolve(value);
769
- else
770
- options.codexHome = resolve(value);
771
- }
772
- else if (arg === "--orchestrator" || arg === "--model") {
773
- const value = argv[++index];
774
- if (!value)
775
- throw new Error(`${arg} requires a model.`);
776
- options.orchestrator = value;
777
- }
778
- else if (arg === "--reasoning-effort") {
779
- const value = argv[++index];
780
- if (!value)
781
- throw new Error(`${arg} requires a value.`);
782
- options.reasoningEffort = value;
783
- }
784
- else if (arg === "--plan-reasoning-effort") {
785
- const value = argv[++index];
786
- if (!value)
787
- throw new Error(`${arg} requires a value.`);
788
- options.planReasoningEffort = value;
789
- }
790
- else
791
- options.positionals.push(arg);
792
- }
793
- return options;
794
- }
795
- function help() {
796
- console.log(`codex-kit ${PACKAGE.version}
30
+ `),console.log(`created project instructions file: ${t}`);else{let a=g(t);(a.includes(Ze)||a.includes(Qe))&&(console.warn(`preserved legacy managed template in: ${t}`),console.warn("Ask Codex to migrate it to semantic template reconciliation before applying updates."))}console.log(eo())}function ye(o){let{cwd:e}=o;X(e);let t=A(e,"TEMPLATE_AGENTS.md"),n=q(e),r=n.template.availableHash??null,i=w(t)?S(b(t)):null;if(console.log(`Project: ${e}`),!i){console.log("Status: not initialized (run codex-kit project sync)");return}if(!w(A(e,"AGENTS.md"))){console.log("Status: AGENTS.md missing (reconcile the template first)");return}if(console.log(`Available: ${n.template.availableVersion??"unknown"} (${r??"untracked"})`),console.log(`Applied: ${n.template.appliedHash??"never"}`),S(b(D))!==r){console.log("Status: kit template update available; run project sync");return}if(i!==r){console.log("Status: local template changed; review it before syncing");return}if(n.template.appliedHash!==i){console.log("Status: reconciliation required");return}console.log("Status: up to date")}function be(o){let{cwd:e}=o;X(e);let t=A(e,"TEMPLATE_AGENTS.md"),n=A(e,"AGENTS.md");if(!w(t))throw new Error(`Missing ${t}; run project sync first.`);if(!w(n))throw new Error(`Missing ${n}; reconcile the template into AGENTS.md first.`);let r=q(e);r.version=1,r.template={...r.template,appliedHash:S(b(t)),appliedAt:new Date().toISOString()},Se(e,r),console.log(`recorded template reconciliation: ${t}`)}import{spawnSync as oo}from"node:child_process";function to(o,e){let t=i=>{let s=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(i);if(!s)throw new Error(`Invalid package version: ${i}`);return{numbers:[Number(s[1]),Number(s[2]),Number(s[3])],prerelease:s[4]??null}},n=t(o),r=t(e);for(let[i,s]of[[n.numbers[0],r.numbers[0]],[n.numbers[1],r.numbers[1]],[n.numbers[2],r.numbers[2]]])if(i!==s)return Math.sign(i-s);return n.prerelease===r.prerelease?0:n.prerelease?r.prerelease?Math.sign(n.prerelease.localeCompare(r.prerelease,"en",{numeric:!0})):-1:1}function xe(){let o=process.env.CODEX_KIT_LATEST_VERSION;if(!o){let t=oo(process.platform==="win32"?"pnpm.cmd":"pnpm",["view",p.name,"version","--json",`--registry=${J}`],{encoding:"utf8",timeout:15e3});if(t.error)throw new Error(`Unable to run pnpm: ${t.error.message}`);if(t.status!==0)throw new Error(`Unable to check ${J}: ${t.stderr.trim()||"pnpm view failed"}`);try{let n=JSON.parse(t.stdout);o=Array.isArray(n)&&typeof n.at(-1)=="string"?n.at(-1):typeof n=="string"?n:void 0}catch{o=t.stdout.trim()}}if(!o)throw new Error("Registry returned no package version.");console.log(`Installed: ${p.version}`),console.log(`Latest: ${o}`);let e=to(p.version,o);if(e===0){console.log("codex-kit is up to date.");return}if(e>0){console.log("This local build is newer than the published package.");return}console.log(`Update available. Run:
31
+ pnpm add --global ${p.name}@latest
32
+ codex-kit global install`)}import{homedir as no}from"node:os";import{join as ro,resolve as Y}from"node:path";function ve(o){let e={cwd:process.cwd(),codexHome:Y(process.env.CODEX_HOME||ro(no(),".codex")),orchestrator:"gpt-5.6-sol",reasoningEffort:"low",planReasoningEffort:"high",force:!1,positionals:[]};for(let t=0;t<o.length;t++){let n=o[t];if(n!==void 0)if(n==="--force")e.force=!0;else if(n==="--cwd"||n==="--codex-home"){let r=o[++t];if(!r)throw new Error(`${n} requires a path.`);n==="--cwd"?e.cwd=Y(r):e.codexHome=Y(r)}else if(n==="--orchestrator"||n==="--model"){let r=o[++t];if(!r)throw new Error(`${n} requires a model.`);e.orchestrator=r}else if(n==="--reasoning-effort"||n==="--plan-reasoning-effort"){let r=o[++t];if(!r)throw new Error(`${n} requires a value.`);n==="--reasoning-effort"?e.reasoningEffort=r:e.planReasoningEffort=r}else e.positionals.push(n)}return e}function so(){console.log(`codex-kit ${p.version}
797
33
 
798
34
  Usage:
799
35
  codex-kit <command> [options]
@@ -838,40 +74,4 @@ Examples:
838
74
  codex-kit global install --force
839
75
  codex-kit global configure --reasoning-effort low --plan-reasoning-effort high
840
76
  codex-kit project sync --cwd /path/to/project --force
841
- codex-kit project status --cwd /path/to/project`);
842
- }
843
- export function main(argv = process.argv.slice(2)) {
844
- const options = parse(argv);
845
- if (options.positionals.includes("--version"))
846
- return console.log(PACKAGE.version);
847
- if (!options.positionals.length || options.positionals.includes("--help"))
848
- return help();
849
- const [scope, action] = options.positionals;
850
- if (scope === "global" && action === "install")
851
- return installGlobal(options);
852
- if (scope === "global" && action === "configure")
853
- return configureGlobal(options);
854
- if (scope === "global" && action === "list")
855
- return listGlobal(options);
856
- if (scope === "global" && action === "uninstall")
857
- return uninstallGlobal(options);
858
- if (scope === "project" && (action === "init" || action === "sync"))
859
- return syncProject(options);
860
- if (scope === "project" && action === "status")
861
- return projectStatus(options);
862
- if (scope === "project" && action === "mark-applied")
863
- return markApplied(options);
864
- if (scope === "version" && action === "check")
865
- return checkVersion();
866
- throw new Error(`Unknown command: ${options.positionals.join(" ")}`);
867
- }
868
- if (process.argv[1] &&
869
- realpathSync(resolve(process.argv[1])) === realpathSync(fileURLToPath(import.meta.url))) {
870
- try {
871
- main();
872
- }
873
- catch (error) {
874
- console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
875
- process.exitCode = 1;
876
- }
877
- }
77
+ codex-kit project status --cwd /path/to/project`)}function Z(o=process.argv.slice(2)){let e=ve(o);if(e.positionals.includes("--version")){console.log(p.version);return}if(!e.positionals.length||e.positionals.includes("--help")){so();return}let[t,n]=e.positionals;if(t==="global"&&n==="install")me(e);else if(t==="global"&&n==="configure")V(e);else if(t==="global"&&n==="list")ue(e);else if(t==="global"&&n==="uninstall")he(e);else if(t==="project"&&(n==="init"||n==="sync"))Ee(e);else if(t==="project"&&n==="status")ye(e);else if(t==="project"&&n==="mark-applied")be(e);else if(t==="version"&&n==="check")xe();else throw new Error(`Unknown command: ${e.positionals.join(" ")}`)}if(process.argv[1]&&$e(io(process.argv[1]))===$e(ao(import.meta.url)))try{Z()}catch(o){console.error(`error: ${o instanceof Error?o.message:String(o)}`),process.exitCode=1}export{Z as main};
@@ -1,26 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- const home = resolve(dirname(fileURLToPath(import.meta.url)), "..");
6
- const routingFile = join(home, "SUBAGENT_ROUTING.md");
7
- const input = JSON.parse(readFileSync(0, "utf8"));
8
- function hookContext(event, context) {
9
- return JSON.stringify({
10
- hookSpecificOutput: {
11
- hookEventName: event,
12
- additionalContext: context,
13
- },
14
- });
15
- }
16
- if (input.hook_event_name === "UserPromptSubmit" && existsSync(routingFile)) {
17
- const routing = readFileSync(routingFile, "utf8").trim();
18
- process.stdout.write(`Codex-kit routing policy for this turn:\n\n${routing}\n\n` +
19
- "Classify the task using this policy before acting. When it requires delegation, " +
20
- "spawn the exact named role before doing that role's work. Agent definitions, not " +
21
- "this policy, determine each role's model and reasoning effort.");
22
- }
23
- if (input.hook_event_name === "SubagentStart") {
24
- process.stdout.write(hookContext("SubagentStart", `You are the delegated ${input.agent_type ?? "worker"}. ` +
25
- "Follow the assigned scope, perform the role's work directly without further delegation, validate it, and return concise evidence."));
26
- }
2
+ import{existsSync as r,readFileSync as n}from"node:fs";import{dirname as s,join as a,resolve as u}from"node:path";import{fileURLToPath as d}from"node:url";var c=u(s(d(import.meta.url)),".."),o=a(c,"SUBAGENT_ROUTING.md"),e=JSON.parse(n(0,"utf8"));function g(t,i){return JSON.stringify({hookSpecificOutput:{hookEventName:t,additionalContext:i}})}if(e.hook_event_name==="UserPromptSubmit"&&r(o)){let t=n(o,"utf8").trim();process.stdout.write(`Codex-kit routing policy for this turn:
3
+
4
+ ${t}
5
+
6
+ Classify the task using this policy before acting. When it requires delegation, spawn the exact named role before doing that role's work. Agent definitions, not this policy, determine each role's model and reasoning effort.`)}e.hook_event_name==="SubagentStart"&&process.stdout.write(g("SubagentStart",`You are the delegated ${e.agent_type??"worker"}. Follow the assigned scope, perform the role's work directly without further delegation, validate it, and return concise evidence.`));
package/package.json CHANGED
@@ -1,39 +1,58 @@
1
1
  {
2
- "name": "@iamdevlinph/codex-kit",
3
- "version": "1.0.11",
4
- "description": "Portable Codex subagents and project AGENTS.md defaults.",
5
- "type": "module",
6
- "bin": {
7
- "codex-kit": "bin/codex-kit.js"
8
- },
9
- "files": [
10
- "assets/agents",
11
- "assets/SUBAGENT_ROUTING.md",
12
- "assets/TEMPLATE_AGENTS.md",
13
- "bin"
14
- ],
15
- "scripts": {
16
- "build": "pnpm exec tsc -p tsconfig.json",
17
- "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit && pnpm exec tsc -p tsconfig.test.json --noEmit",
18
- "test": "pnpm run build && pnpm exec tsc -p tsconfig.test.json && node --test .test-dist/test/*.test.js",
19
- "prepack": "pnpm run build",
20
- "pack:check": "pnpm pack --dry-run"
21
- },
22
- "engines": {
23
- "node": ">=20"
24
- },
25
- "repository": {
26
- "type": "git",
27
- "url": "git+https://github.com/iamdevlinph/codex-kit.git"
28
- },
29
- "publishConfig": {
30
- "registry": "https://registry.npmjs.org",
31
- "access": "public"
32
- },
33
- "devDependencies": {
34
- "@types/node": "20.19.43",
35
- "typescript": "7.0.1-rc"
36
- },
37
- "packageManager": "pnpm@11.5.2",
38
- "license": "UNLICENSED"
2
+ "name": "@iamdevlinph/codex-kit",
3
+ "version": "1.0.13",
4
+ "description": "Portable Codex subagents and project AGENTS.md defaults.",
5
+ "type": "module",
6
+ "bin": {
7
+ "codex-kit": "bin/codex-kit.js"
8
+ },
9
+ "files": [
10
+ "assets/agents",
11
+ "assets/SUBAGENT_ROUTING.md",
12
+ "assets/TEMPLATE_AGENTS.md",
13
+ "assets/skills",
14
+ "bin"
15
+ ],
16
+ "scripts": {
17
+ "build": "pnpm exec tsc -p tsconfig.json --noEmit && node scripts/build.mjs",
18
+ "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit && pnpm exec tsc -p tsconfig.test.json --noEmit",
19
+ "test": "pnpm run build && pnpm exec vitest run",
20
+ "prepack": "pnpm run build",
21
+ "pack:check": "pnpm pack --dry-run",
22
+ "format": "biome check --write .",
23
+ "format:check": "biome check .",
24
+ "lint": "biome lint .",
25
+ "lint:fix": "biome lint --write .",
26
+ "check": "biome check",
27
+ "fix": "biome check --fix",
28
+ "prepare": "husky",
29
+ "precommit": "lint-staged"
30
+ },
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/iamdevlinph/codex-kit.git"
37
+ },
38
+ "publishConfig": {
39
+ "registry": "https://registry.npmjs.org",
40
+ "access": "public"
41
+ },
42
+ "devDependencies": {
43
+ "@biomejs/biome": "2.5.3",
44
+ "@types/node": "20.19.43",
45
+ "esbuild": "0.28.1",
46
+ "husky": "9.1.7",
47
+ "lint-staged": "17.0.8",
48
+ "typescript": "7.0.1-rc",
49
+ "vitest": "4.1.10"
50
+ },
51
+ "packageManager": "pnpm@11.5.2",
52
+ "license": "ISC",
53
+ "lint-staged": {
54
+ "*": [
55
+ "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
56
+ ]
57
+ }
39
58
  }