mthds 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,7 +22,7 @@ const VERSION_RE = /^[\w-]+\s+(\d+\.\d+\.\d+)/;
22
22
  const PIPELEX_PKG = {
23
23
  package: "pipelex",
24
24
  uv_package: "pipelex",
25
- version_constraint: ">=0.27.0",
25
+ version_constraint: ">=0.28.0",
26
26
  version_extract: VERSION_RE,
27
27
  install_url: "https://pypi.org/project/pipelex/",
28
28
  auto_installable: true,
@@ -31,7 +31,7 @@ const PIPELEX_PKG = {
31
31
  const PIPELEX_TOOLS_PKG = {
32
32
  package: "pipelex-tools",
33
33
  uv_package: "pipelex-tools",
34
- version_constraint: ">=0.3.3",
34
+ version_constraint: ">=0.4.0",
35
35
  version_extract: VERSION_RE,
36
36
  install_url: "https://pypi.org/project/pipelex-tools/",
37
37
  auto_installable: true,
@@ -12,13 +12,11 @@
12
12
  * - BOOTSTRAP_PARTIAL <json> -- some targets installed, some failed
13
13
  * - BOOTSTRAP_FAILED <json> -- all targets failed
14
14
  */
15
- import { writeFileSync } from "node:fs";
16
- import { join } from "node:path";
17
15
  import { isUvInstalled, installUv, uvToolInstallSync } from "../../installer/runtime/installer.js";
18
16
  import { checkBinaryVersion } from "../../installer/runtime/version-check.js";
19
17
  import { BINARY_RECOVERY } from "../binaries.js";
20
18
  import { agentError, AGENT_ERROR_DOMAINS } from "../output.js";
21
- import { clearCache, ensureStateDir, STATE_DIR } from "../update-cache.js";
19
+ import { clearCache, writeUpgradeMarker } from "../update-cache.js";
22
20
  import { clearSnooze } from "../snooze.js";
23
21
  import { loadConfig } from "../../config/config.js";
24
22
  import { Runners } from "../../runners/types.js";
@@ -136,13 +134,7 @@ export async function agentBootstrap(options = {}) {
136
134
  const allSucceeded = failed.size === 0;
137
135
  const allFailed = succeeded.size === 0;
138
136
  if (allSucceeded) {
139
- try {
140
- ensureStateDir();
141
- writeFileSync(join(STATE_DIR, "just-upgraded-from"), JSON.stringify(markerData), "utf-8");
142
- }
143
- catch (err) {
144
- process.stderr.write(`Warning: could not write upgrade marker: ${errorMsg(err)}.\n`);
145
- }
137
+ writeUpgradeMarker(markerData);
146
138
  clearCache();
147
139
  clearSnooze();
148
140
  process.stdout.write("BOOTSTRAP_COMPLETE " + JSON.stringify({ installed: installedEntries }) + "\n");
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/agent/commands/bootstrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/agent/commands/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -1,31 +1,44 @@
1
1
  /**
2
- * mthds-agent codex apply-config — additively merge required Codex sandbox
3
- * settings into ~/.codex/config.toml so the mthds PostToolUse hook can run
4
- * without being blocked by Codex's default workspace-write sandbox.
2
+ * mthds-agent codex apply-config — make ~/.codex/ correct for the mthds plugin.
5
3
  *
6
- * Currently the only required key is `[sandbox_workspace_write] network_access = true`.
7
- * Without it, any hook that fetches remote pipelex config (or any other
8
- * outbound request) hangs/fails inside the sandbox.
4
+ * Two jobs:
5
+ * 1. Additively merge required keys into ~/.codex/config.toml:
6
+ * [sandbox_workspace_write] network_access = true
7
+ * — Codex's default workspace-write sandbox blocks outbound network for
8
+ * hook commands; without it any remote fetch hangs/fails.
9
+ * [features] plugin_hooks = true
10
+ * — plugin-bundled hooks are opt-in; without it Codex never loads the
11
+ * mthds validation hook shipped inside the plugin.
12
+ * 2. Remove any obsolete mthds entry left in ~/.codex/hooks.json by the retired
13
+ * `install-hook` command (see codex.ts) — it would double-fire alongside the
14
+ * plugin-bundled hook.
9
15
  *
10
- * Warning-only checks (we never modify these — too high-risk):
11
- * - `[features] codex_hooks = false` explicitly disables hooks
12
- * (default is on as of Codex 0.124.0; codex-rs/features/src/lib.rs:768).
13
- * - `sandbox_mode = "read-only"` — hook can't run apply_patch validation.
16
+ * Warning-only checks (never modified — too high-risk):
17
+ * - `[features] hooks = false` (or its alias `codex_hooks = false`) disables
18
+ * hooks entirely; we check both keys defensively.
19
+ * - `sandbox_mode = "read-only"` — apply_patch can't run, so the hook can't
20
+ * either.
14
21
  *
15
22
  * Output statuses (via agentSuccess):
16
- * - { status: "ALREADY_OK", config_file, warnings? } no changes needed
17
- * - { status: "APPLIED", config_file, applied, warnings? } diff merged + written
18
- * - { status: "WOULD_APPLY", config_file, applied, warnings? } — --dry-run only
23
+ * - { status: "ALREADY_OK", ... } nothing needed changing
24
+ * - { status: "APPLIED", ... } config.toml merged and/or stale hook removed
25
+ * - { status: "WOULD_APPLY", ... } — --dry-run only
19
26
  *
20
27
  * Flags:
21
28
  * --check exits non-zero if anything would change (no writes, no warnings demoted)
22
- * --dry-run prints proposed diff and exits 0 without touching the file
29
+ * --dry-run prints proposed diff and exits 0 without touching any file
23
30
  */
24
31
  export interface AppliedChange {
25
32
  table: string;
26
33
  key: string;
27
34
  value: string;
28
35
  }
36
+ export interface SettingConflict {
37
+ table: string;
38
+ key: string;
39
+ current: string;
40
+ required: string;
41
+ }
29
42
  export interface CodexConfigWarning {
30
43
  code: string;
31
44
  message: string;
@@ -33,7 +46,8 @@ export interface CodexConfigWarning {
33
46
  export interface CodexConfigInspection {
34
47
  config_file: string;
35
48
  exists: boolean;
36
- needs_change: AppliedChange | null;
49
+ needs_changes: AppliedChange[];
50
+ conflicts: SettingConflict[];
37
51
  warnings: CodexConfigWarning[];
38
52
  parse_error?: string;
39
53
  }
@@ -1,41 +1,43 @@
1
1
  /**
2
- * mthds-agent codex apply-config — additively merge required Codex sandbox
3
- * settings into ~/.codex/config.toml so the mthds PostToolUse hook can run
4
- * without being blocked by Codex's default workspace-write sandbox.
2
+ * mthds-agent codex apply-config — make ~/.codex/ correct for the mthds plugin.
5
3
  *
6
- * Currently the only required key is `[sandbox_workspace_write] network_access = true`.
7
- * Without it, any hook that fetches remote pipelex config (or any other
8
- * outbound request) hangs/fails inside the sandbox.
4
+ * Two jobs:
5
+ * 1. Additively merge required keys into ~/.codex/config.toml:
6
+ * [sandbox_workspace_write] network_access = true
7
+ * — Codex's default workspace-write sandbox blocks outbound network for
8
+ * hook commands; without it any remote fetch hangs/fails.
9
+ * [features] plugin_hooks = true
10
+ * — plugin-bundled hooks are opt-in; without it Codex never loads the
11
+ * mthds validation hook shipped inside the plugin.
12
+ * 2. Remove any obsolete mthds entry left in ~/.codex/hooks.json by the retired
13
+ * `install-hook` command (see codex.ts) — it would double-fire alongside the
14
+ * plugin-bundled hook.
9
15
  *
10
- * Warning-only checks (we never modify these — too high-risk):
11
- * - `[features] codex_hooks = false` explicitly disables hooks
12
- * (default is on as of Codex 0.124.0; codex-rs/features/src/lib.rs:768).
13
- * - `sandbox_mode = "read-only"` — hook can't run apply_patch validation.
16
+ * Warning-only checks (never modified — too high-risk):
17
+ * - `[features] hooks = false` (or its alias `codex_hooks = false`) disables
18
+ * hooks entirely; we check both keys defensively.
19
+ * - `sandbox_mode = "read-only"` — apply_patch can't run, so the hook can't
20
+ * either.
14
21
  *
15
22
  * Output statuses (via agentSuccess):
16
- * - { status: "ALREADY_OK", config_file, warnings? } no changes needed
17
- * - { status: "APPLIED", config_file, applied, warnings? } diff merged + written
18
- * - { status: "WOULD_APPLY", config_file, applied, warnings? } — --dry-run only
23
+ * - { status: "ALREADY_OK", ... } nothing needed changing
24
+ * - { status: "APPLIED", ... } config.toml merged and/or stale hook removed
25
+ * - { status: "WOULD_APPLY", ... } — --dry-run only
19
26
  *
20
27
  * Flags:
21
28
  * --check exits non-zero if anything would change (no writes, no warnings demoted)
22
- * --dry-run prints proposed diff and exits 0 without touching the file
29
+ * --dry-run prints proposed diff and exits 0 without touching any file
23
30
  */
24
31
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
25
32
  import { dirname, join } from "node:path";
26
33
  import { homedir } from "node:os";
27
34
  import { parse as parseToml } from "smol-toml";
28
35
  import { agentError, agentSuccess, AGENT_ERROR_DOMAINS } from "../output.js";
29
- // ── Constants ──────────────────────────────────────────────────────
30
- //
31
- // Single-key invariant: this command currently enforces exactly one
32
- // (table, key, value) tuple. `diffRequired` and `collectWarnings` are
33
- // structured around that assumption — if a future requirement adds a
34
- // second required key (e.g. a sandbox_mode default), both will need to
35
- // fan out to a list rather than the singleton structure used here.
36
- const REQUIRED_TABLE = "sandbox_workspace_write";
37
- const REQUIRED_KEY = "network_access";
38
- const REQUIRED_VALUE = true;
36
+ import { inspectLegacyCodexHook, removeLegacyCodexHook } from "./codex.js";
37
+ const REQUIRED_SETTINGS = [
38
+ { table: "sandbox_workspace_write", key: "network_access", value: true },
39
+ { table: "features", key: "plugin_hooks", value: true },
40
+ ];
39
41
  // ── Helpers ────────────────────────────────────────────────────────
40
42
  function configFilePath() {
41
43
  return join(homedir(), ".codex", "config.toml");
@@ -46,9 +48,13 @@ function writeAtomic(path, contents) {
46
48
  writeFileSync(tmp, contents, { encoding: "utf8", mode: 0o644 });
47
49
  renameSync(tmp, path);
48
50
  }
51
+ /** Parse TOML text, treating empty/whitespace-only input as an empty table. */
52
+ function parseTomlOrEmpty(text) {
53
+ return text.trim().length === 0 ? {} : parseToml(text);
54
+ }
49
55
  /**
50
- * Append a [table] section with key=value to existing TOML text. Used when
51
- * the table is missing entirely. We append rather than re-serialize so user
56
+ * Append a [table] section with key=value to existing TOML text. Used when the
57
+ * table is missing entirely. We append rather than re-serialize so user
52
58
  * formatting/comments elsewhere in the file are preserved verbatim.
53
59
  */
54
60
  function appendTomlTable(existing, table, key, value) {
@@ -57,22 +63,22 @@ function appendTomlTable(existing, table, key, value) {
57
63
  return `${trimmed}${sep}[${table}]\n${key} = ${value}\n`;
58
64
  }
59
65
  /**
60
- * Insert key=value into an existing [table] section without re-serializing
61
- * the document. Locates the table header line, finds the end of its body
62
- * (next [section] header or EOF), and inserts the line just before that
63
- * boundary. This preserves comments and ordering of every other table.
66
+ * Insert key=value into an existing [table] section without re-serializing the
67
+ * document. Locates the table header line, finds the end of its body (next
68
+ * [section] header or EOF), and inserts the line just before that boundary.
69
+ * This preserves comments and ordering of every other table.
64
70
  */
65
71
  function insertIntoExistingTable(existing, table, key, value) {
66
72
  const lines = existing.split("\n");
67
- const headerRegex = new RegExp(`^\\s*\\[\\s*${table.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\\s*\\]\\s*(#.*)?$`);
68
- // Treat both `[table]` and `[[array_of_tables]]` as section boundaries —
69
- // the inner `\[\[?...\]\]?` lets the boundary scan stop at array-of-tables
73
+ const headerRegex = new RegExp(`^\\s*\\[\\s*${table.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*\\]\\s*(#.*)?$`);
74
+ // Treat both `[table]` and `[[array_of_tables]]` as section boundaries — the
75
+ // inner `\[\[?...\]\]?` lets the boundary scan stop at array-of-tables
70
76
  // headers too, so we don't accidentally insert into one.
71
77
  const anyHeaderRegex = /^\s*\[\[?[^\]]+\]\]?\s*(#.*)?$/;
72
78
  let tableStart = -1;
73
- for (let i = 0; i < lines.length; i++) {
74
- if (headerRegex.test(lines[i])) {
75
- tableStart = i;
79
+ for (let index = 0; index < lines.length; index++) {
80
+ if (headerRegex.test(lines[index])) {
81
+ tableStart = index;
76
82
  break;
77
83
  }
78
84
  }
@@ -81,9 +87,9 @@ function insertIntoExistingTable(existing, table, key, value) {
81
87
  return appendTomlTable(existing, table, key, value);
82
88
  }
83
89
  let insertAt = lines.length;
84
- for (let i = tableStart + 1; i < lines.length; i++) {
85
- if (anyHeaderRegex.test(lines[i])) {
86
- insertAt = i;
90
+ for (let index = tableStart + 1; index < lines.length; index++) {
91
+ if (anyHeaderRegex.test(lines[index])) {
92
+ insertAt = index;
87
93
  break;
88
94
  }
89
95
  }
@@ -93,40 +99,61 @@ function insertIntoExistingTable(existing, table, key, value) {
93
99
  }
94
100
  const before = lines.slice(0, insertAt);
95
101
  const after = lines.slice(insertAt);
96
- const newLine = `${key} = ${value}`;
97
- return [...before, newLine, ...after].join("\n");
102
+ return [...before, `${key} = ${value}`, ...after].join("\n");
103
+ }
104
+ function readSettingValue(parsed, setting) {
105
+ const table = parsed[setting.table];
106
+ if (table && typeof table === "object" && !Array.isArray(table)) {
107
+ return table[setting.key];
108
+ }
109
+ return undefined;
98
110
  }
99
111
  /**
100
- * Decide whether the parsed config already satisfies the required key.
101
- * Returns null if satisfied, otherwise an AppliedChange describing what
102
- * needs to be added.
112
+ * Compare the parsed config against REQUIRED_SETTINGS:
113
+ * - missing key → an AppliedChange to add it
114
+ * - wrong value → a SettingConflict (the user must hand-fix; we never flip
115
+ * a key that is explicitly set)
116
+ * - right value → satisfied, ignored
103
117
  */
104
- function diffRequired(parsed) {
105
- const table = parsed[REQUIRED_TABLE];
106
- if (table &&
107
- typeof table === "object" &&
108
- !Array.isArray(table) &&
109
- table[REQUIRED_KEY] === REQUIRED_VALUE) {
110
- return null;
118
+ function evaluateSettings(parsed) {
119
+ const changes = [];
120
+ const conflicts = [];
121
+ for (const setting of REQUIRED_SETTINGS) {
122
+ const current = readSettingValue(parsed, setting);
123
+ if (current === setting.value)
124
+ continue;
125
+ if (current === undefined) {
126
+ changes.push({ table: setting.table, key: setting.key, value: String(setting.value) });
127
+ }
128
+ else {
129
+ conflicts.push({
130
+ table: setting.table,
131
+ key: setting.key,
132
+ current: JSON.stringify(current),
133
+ required: String(setting.value),
134
+ });
135
+ }
111
136
  }
112
- return {
113
- table: REQUIRED_TABLE,
114
- key: REQUIRED_KEY,
115
- value: String(REQUIRED_VALUE),
116
- };
137
+ return { changes, conflicts };
117
138
  }
118
139
  /** Collect non-fatal warnings about the user's config without modifying it. */
119
140
  function collectWarnings(parsed) {
120
141
  const warnings = [];
121
142
  const features = parsed.features;
122
- if (features &&
123
- typeof features === "object" &&
124
- !Array.isArray(features) &&
125
- features.codex_hooks === false) {
126
- warnings.push({
127
- code: "CODEX_HOOKS_DISABLED",
128
- message: "[features] codex_hooks is explicitly set to false; the mthds hook will not load. Remove this key (Codex 0.124+ enables hooks by default).",
129
- });
143
+ if (features && typeof features === "object" && !Array.isArray(features)) {
144
+ const featuresTable = features;
145
+ // Check both `hooks` and its alias `codex_hooks` defensively. Either being
146
+ // explicitly false disables hooks entirely and breaks the mthds hook. When
147
+ // both are false, name both so neither is silently omitted.
148
+ const disabledKeys = ["hooks", "codex_hooks"].filter((key) => featuresTable[key] === false);
149
+ if (disabledKeys.length > 0) {
150
+ const keyList = disabledKeys.map((key) => `[features] ${key}`).join(" and ");
151
+ const plural = disabledKeys.length > 1;
152
+ warnings.push({
153
+ code: "CODEX_HOOKS_DISABLED",
154
+ message: `${keyList} ${plural ? "are" : "is"} explicitly set to false; the mthds hook will not load. Remove ${plural ? "these keys" : "this key"} — hooks are enabled by default.`,
155
+ });
156
+ }
130
157
  }
131
158
  if (parsed.sandbox_mode === "read-only") {
132
159
  warnings.push({
@@ -136,6 +163,27 @@ function collectWarnings(parsed) {
136
163
  }
137
164
  return warnings;
138
165
  }
166
+ function legacyHookWarning(parseError) {
167
+ return {
168
+ code: "LEGACY_HOOK_UNREADABLE",
169
+ message: `Could not read ~/.codex/hooks.json to remove any obsolete mthds hook entry (${parseError}). If you previously ran \`mthds-agent codex install-hook\`, delete that entry by hand so the validation hook does not run twice.`,
170
+ };
171
+ }
172
+ /** Apply each change to the TOML text in sequence, re-parsing between changes
173
+ * so the table-exists decision reflects edits already made. */
174
+ function applyChanges(raw, changes) {
175
+ let next = raw;
176
+ for (const change of changes) {
177
+ const parsed = parseTomlOrEmpty(next);
178
+ const table = parsed[change.table];
179
+ const tableExists = table !== undefined && typeof table === "object" && !Array.isArray(table);
180
+ next = tableExists
181
+ ? insertIntoExistingTable(next, change.table, change.key, change.value)
182
+ : appendTomlTable(next, change.table, change.key, change.value);
183
+ }
184
+ return next;
185
+ }
186
+ // ── Read-only inspection ───────────────────────────────────────────
139
187
  /**
140
188
  * Read-only inspection of ~/.codex/config.toml. Used by doctor to surface
141
189
  * issues without writing anything. Never throws — parse errors are reported
@@ -145,15 +193,16 @@ export function inspectCodexConfig() {
145
193
  const file = configFilePath();
146
194
  const exists = existsSync(file);
147
195
  if (!exists) {
148
- // No config file ⇒ definitely needs a change (the required key is missing).
196
+ // No config file ⇒ every required key is missing.
149
197
  return {
150
198
  config_file: file,
151
199
  exists: false,
152
- needs_change: {
153
- table: REQUIRED_TABLE,
154
- key: REQUIRED_KEY,
155
- value: String(REQUIRED_VALUE),
156
- },
200
+ needs_changes: REQUIRED_SETTINGS.map((setting) => ({
201
+ table: setting.table,
202
+ key: setting.key,
203
+ value: String(setting.value),
204
+ })),
205
+ conflicts: [],
157
206
  warnings: [],
158
207
  };
159
208
  }
@@ -165,20 +214,22 @@ export function inspectCodexConfig() {
165
214
  return {
166
215
  config_file: file,
167
216
  exists: true,
168
- needs_change: null,
217
+ needs_changes: [],
218
+ conflicts: [],
169
219
  warnings: [],
170
220
  parse_error: `Failed to read ${file}: ${err.message}`,
171
221
  };
172
222
  }
173
223
  let parsed;
174
224
  try {
175
- parsed = raw.trim().length === 0 ? {} : parseToml(raw);
225
+ parsed = parseTomlOrEmpty(raw);
176
226
  }
177
227
  catch (err) {
178
228
  return {
179
229
  config_file: file,
180
230
  exists: true,
181
- needs_change: null,
231
+ needs_changes: [],
232
+ conflicts: [],
182
233
  warnings: [],
183
234
  parse_error: err.message,
184
235
  };
@@ -187,15 +238,18 @@ export function inspectCodexConfig() {
187
238
  return {
188
239
  config_file: file,
189
240
  exists: true,
190
- needs_change: null,
241
+ needs_changes: [],
242
+ conflicts: [],
191
243
  warnings: [],
192
244
  parse_error: "Top-level value is not a TOML table",
193
245
  };
194
246
  }
247
+ const { changes, conflicts } = evaluateSettings(parsed);
195
248
  return {
196
249
  config_file: file,
197
250
  exists: true,
198
- needs_change: diffRequired(parsed),
251
+ needs_changes: changes,
252
+ conflicts,
199
253
  warnings: collectWarnings(parsed),
200
254
  };
201
255
  }
@@ -210,92 +264,117 @@ export async function agentCodexApplyConfig(options = {}) {
210
264
  raw = readFileSync(file, "utf8");
211
265
  }
212
266
  catch (err) {
213
- agentError(`Failed to read ${file}: ${err.message}`, "IOError", { error_domain: AGENT_ERROR_DOMAINS.IO });
267
+ agentError(`Failed to read ${file}: ${err.message}`, "IOError", {
268
+ error_domain: AGENT_ERROR_DOMAINS.IO,
269
+ });
214
270
  return;
215
271
  }
216
272
  }
217
273
  let parsed;
218
274
  try {
219
- parsed = raw.trim().length === 0 ? {} : parseToml(raw);
275
+ parsed = parseTomlOrEmpty(raw);
220
276
  }
221
277
  catch (err) {
222
278
  agentError(`Invalid TOML in ${file}: ${err.message}. Fix the file by hand or delete it and re-run.`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
223
279
  return;
224
280
  }
225
281
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
226
- agentError(`${file} does not contain a TOML table at the top level.`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
282
+ agentError(`${file} does not contain a TOML table at the top level.`, "ConfigError", {
283
+ error_domain: AGENT_ERROR_DOMAINS.CONFIG,
284
+ });
227
285
  return;
228
286
  }
229
- const change = diffRequired(parsed);
287
+ const { changes, conflicts } = evaluateSettings(parsed);
230
288
  const warnings = collectWarnings(parsed);
231
- // --check mode: exit non-zero if anything would change. Warnings count
232
- // because they signal explicit user state that breaks the hook.
289
+ // A key explicitly set to a conflicting value is a hard error in every mode:
290
+ // apply-config never overrides an explicit user choice.
291
+ if (conflicts.length > 0) {
292
+ const lines = conflicts.map((conflict) => ` [${conflict.table}] ${conflict.key} = ${conflict.current} (the mthds plugin needs ${conflict.required})`);
293
+ agentError(`~/.codex/config.toml has settings that conflict with the mthds plugin:\n${lines.join("\n")}\nChange them by hand, then re-run \`mthds-agent codex apply-config\`.`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
294
+ return;
295
+ }
296
+ // ── --check mode: report only, exit non-zero if anything needs attention ──
233
297
  if (checkMode) {
234
- if (change || warnings.length > 0) {
298
+ const legacy = inspectLegacyCodexHook();
299
+ const needsAttention = changes.length > 0 ||
300
+ warnings.length > 0 ||
301
+ legacy.has_legacy_entry ||
302
+ legacy.parse_error !== undefined;
303
+ if (needsAttention) {
235
304
  agentError(`Codex config needs attention. Run 'mthds-agent codex apply-config' (and review warnings).`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
236
305
  return;
237
306
  }
238
- agentSuccess({ status: "ALREADY_OK", config_file: file });
239
- return;
240
- }
241
- if (!change) {
242
307
  agentSuccess({
243
308
  status: "ALREADY_OK",
244
309
  config_file: file,
245
- warnings,
310
+ applied: [],
311
+ legacy_hook: { hooks_file: legacy.hooks_file, status: "absent" },
312
+ warnings: [],
246
313
  });
247
314
  return;
248
315
  }
249
- // Build the new file contents additively.
250
- //
251
- // Edge case: `parsed[REQUIRED_TABLE]` may be a TOML-implicit table
252
- // created by a dotted-key header like `[sandbox_workspace_write.sub]`
253
- // even when no literal `[sandbox_workspace_write]` header exists in the
254
- // source text. In that case `insertIntoExistingTable` falls back to
255
- // appending a fresh header, which would cause smol-toml to reject the
256
- // result on the sanity-parse below (table redefinition). The sanity
257
- // parse catches it and we surface a ConfigError not pretty, but safe.
258
- // If this becomes a real problem, switch to writing the dotted form
259
- // `sandbox_workspace_write.network_access = true` at top-of-file.
260
- let nextRaw;
261
- const tableExists = parsed[REQUIRED_TABLE] !== undefined &&
262
- typeof parsed[REQUIRED_TABLE] === "object" &&
263
- !Array.isArray(parsed[REQUIRED_TABLE]);
264
- if (tableExists) {
265
- nextRaw = insertIntoExistingTable(raw, change.table, change.key, change.value);
266
- }
267
- else {
268
- nextRaw = appendTomlTable(raw, change.table, change.key, change.value);
269
- }
270
- // Sanity-parse the new contents before committing.
271
- try {
272
- parseToml(nextRaw);
273
- }
274
- catch (err) {
275
- agentError(`Generated config would not re-parse: ${err.message}`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
276
- return;
316
+ // Validate the prospective config.toml contents before any write.
317
+ let nextRaw = raw;
318
+ if (changes.length > 0) {
319
+ try {
320
+ nextRaw = applyChanges(raw, changes);
321
+ // Sanity-parse: an implicit table created by a dotted-key header can make
322
+ // appendTomlTable emit a duplicate header that smol-toml rejects.
323
+ // applyChanges itself re-parses between changes, so a malformed
324
+ // intermediate state throws here toocaught the same way.
325
+ parseToml(nextRaw);
326
+ }
327
+ catch (err) {
328
+ agentError(`Generated config would not re-parse: ${err.message}`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
329
+ return;
330
+ }
277
331
  }
332
+ // ── --dry-run mode: report the proposed diff, write nothing ──
278
333
  if (dryRunMode) {
334
+ const legacy = inspectLegacyCodexHook();
335
+ const allWarnings = legacy.parse_error !== undefined
336
+ ? [...warnings, legacyHookWarning(legacy.parse_error)]
337
+ : warnings;
338
+ const wouldChange = changes.length > 0 || legacy.has_legacy_entry;
279
339
  agentSuccess({
280
- status: "WOULD_APPLY",
340
+ status: wouldChange ? "WOULD_APPLY" : "ALREADY_OK",
281
341
  config_file: file,
282
- applied: [change],
283
- warnings,
342
+ applied: changes,
343
+ legacy_hook: {
344
+ hooks_file: legacy.hooks_file,
345
+ status: legacy.has_legacy_entry
346
+ ? "would-remove"
347
+ : legacy.parse_error !== undefined
348
+ ? "error"
349
+ : "absent",
350
+ },
351
+ warnings: allWarnings,
284
352
  });
285
353
  return;
286
354
  }
287
- try {
288
- writeAtomic(file, nextRaw);
289
- }
290
- catch (err) {
291
- agentError(`Failed to write ${file}: ${err.message}`, "IOError", { error_domain: AGENT_ERROR_DOMAINS.IO });
292
- return;
355
+ // ── Apply ──
356
+ if (changes.length > 0) {
357
+ try {
358
+ writeAtomic(file, nextRaw);
359
+ }
360
+ catch (err) {
361
+ agentError(`Failed to write ${file}: ${err.message}`, "IOError", {
362
+ error_domain: AGENT_ERROR_DOMAINS.IO,
363
+ });
364
+ return;
365
+ }
293
366
  }
367
+ const removal = removeLegacyCodexHook();
368
+ const allWarnings = removal.status === "error" && removal.error !== undefined
369
+ ? [...warnings, legacyHookWarning(removal.error)]
370
+ : warnings;
371
+ const didChange = changes.length > 0 || removal.status === "removed";
294
372
  agentSuccess({
295
- status: "APPLIED",
373
+ status: didChange ? "APPLIED" : "ALREADY_OK",
296
374
  config_file: file,
297
- applied: [change],
298
- warnings,
375
+ applied: changes,
376
+ legacy_hook: { hooks_file: removal.hooks_file, status: removal.status },
377
+ warnings: allWarnings,
299
378
  });
300
379
  }
301
380
  //# sourceMappingURL=codex-config.js.map