mthds 0.4.1 → 0.6.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.
Files changed (47) hide show
  1. package/README.md +5 -6
  2. package/dist/agent/binaries.d.ts +5 -0
  3. package/dist/agent/binaries.js +26 -27
  4. package/dist/agent/binaries.js.map +1 -1
  5. package/dist/agent/commands/codex-config.d.ts +51 -0
  6. package/dist/agent/commands/codex-config.js +301 -0
  7. package/dist/agent/commands/codex-config.js.map +1 -0
  8. package/dist/agent/commands/codex-hook.d.ts +70 -0
  9. package/dist/agent/commands/codex-hook.js +205 -0
  10. package/dist/agent/commands/codex-hook.js.map +1 -0
  11. package/dist/agent/commands/codex.d.ts +17 -10
  12. package/dist/agent/commands/codex.js +89 -27
  13. package/dist/agent/commands/codex.js.map +1 -1
  14. package/dist/agent/commands/doctor.js +42 -2
  15. package/dist/agent/commands/doctor.js.map +1 -1
  16. package/dist/agent/commands/install.d.ts +0 -2
  17. package/dist/agent/commands/install.js +10 -81
  18. package/dist/agent/commands/install.js.map +1 -1
  19. package/dist/agent/plugin-version.d.ts +2 -2
  20. package/dist/agent/plugin-version.js +2 -2
  21. package/dist/agent/plugin-version.js.map +1 -1
  22. package/dist/agent-cli.js +20 -4
  23. package/dist/agent-cli.js.map +1 -1
  24. package/dist/cli/commands/install.js +34 -119
  25. package/dist/cli/commands/install.js.map +1 -1
  26. package/dist/client/client.js +72 -9
  27. package/dist/client/client.js.map +1 -1
  28. package/dist/client/exceptions.d.ts +41 -1
  29. package/dist/client/exceptions.js +50 -2
  30. package/dist/client/exceptions.js.map +1 -1
  31. package/dist/client/index.d.ts +1 -1
  32. package/dist/client/index.js +1 -1
  33. package/dist/client/index.js.map +1 -1
  34. package/dist/installer/methods/install-flow.d.ts +25 -0
  35. package/dist/installer/methods/install-flow.js +51 -0
  36. package/dist/installer/methods/install-flow.js.map +1 -0
  37. package/dist/installer/methods/types.d.ts +4 -0
  38. package/dist/installer/methods/types.js.map +1 -0
  39. package/dist/installer/methods/writer.d.ts +19 -0
  40. package/dist/installer/{agents/registry.js → methods/writer.js} +11 -26
  41. package/dist/installer/methods/writer.js.map +1 -0
  42. package/package.json +1 -1
  43. package/dist/installer/agents/registry.d.ts +0 -12
  44. package/dist/installer/agents/registry.js.map +0 -1
  45. package/dist/installer/agents/types.d.ts +0 -16
  46. package/dist/installer/agents/types.js.map +0 -1
  47. /package/dist/installer/{agents → methods}/types.js +0 -0
package/README.md CHANGED
@@ -46,10 +46,9 @@ The CLI will:
46
46
  1. Fetch the `methods/` folder from the GitHub repository
47
47
  2. Validate each method's `METHODS.toml` manifest
48
48
  3. If `--method <name>` is provided, install only that method (errors if name not found)
49
- 4. Ask which AI agent to install it for (Claude Code, with more coming soon)
50
- 5. Ask where to install **local** (current project) or **global** (your machine)
51
- 6. Optionally install a [runner](#runners)
52
- 7. Copy all `.mthds` files to `.claude/methods/<name>/`
49
+ 4. Ask where to install **local** (current project) or **global** (your machine)
50
+ 5. Optionally install a [runner](#runners)
51
+ 6. Copy all `.mthds` files to `.mthds/methods/<name>/`
53
52
 
54
53
  You can also install from a local directory:
55
54
 
@@ -61,8 +60,8 @@ npx mthds install --local /path/to/repo
61
60
 
62
61
  | Location | Path |
63
62
  |----------|------|
64
- | Local | `<cwd>/.claude/methods/<name>/` |
65
- | Global | `~/.claude/methods/<name>/` |
63
+ | Local | `<cwd>/.mthds/methods/<name>/` |
64
+ | Global | `~/.mthds/methods/<name>/` |
66
65
 
67
66
  ## Publishing a method
68
67
 
@@ -4,6 +4,11 @@
4
4
  * version_constraint is a semver range (e.g. ">=0.22.0") checked against the
5
5
  * output of `<binary> --version`. Use buildInstallCommand() to get the
6
6
  * canonical install command — single source of truth.
7
+ *
8
+ * Each BINARY_RECOVERY entry spreads a shared `*_PKG` constant that holds
9
+ * the per-PyPI-package metadata (constraint, install URL, etc.). To bump a
10
+ * minimum version, edit the constant — never the per-binary entry — so that
11
+ * binaries shipping from the same package can never drift.
7
12
  */
8
13
  export interface BinaryRecoveryInfo {
9
14
  binary: string;
@@ -4,6 +4,11 @@
4
4
  * version_constraint is a semver range (e.g. ">=0.22.0") checked against the
5
5
  * output of `<binary> --version`. Use buildInstallCommand() to get the
6
6
  * canonical install command — single source of truth.
7
+ *
8
+ * Each BINARY_RECOVERY entry spreads a shared `*_PKG` constant that holds
9
+ * the per-PyPI-package metadata (constraint, install URL, etc.). To bump a
10
+ * minimum version, edit the constant — never the per-binary entry — so that
11
+ * binaries shipping from the same package can never drift.
7
12
  */
8
13
  /**
9
14
  * Build the canonical install/upgrade command string for a binary.
@@ -13,33 +18,27 @@ export function buildInstallCommand(recovery) {
13
18
  }
14
19
  /** Shared version-extract regex: `<name> <semver>` */
15
20
  const VERSION_RE = /^[\w-]+\s+(\d+\.\d+\.\d+)/;
21
+ /** Shared metadata for the `pipelex` PyPI package — provides both `pipelex` and `pipelex-agent` binaries. */
22
+ const PIPELEX_PKG = {
23
+ package: "pipelex",
24
+ uv_package: "pipelex",
25
+ version_constraint: ">=0.25.0",
26
+ version_extract: VERSION_RE,
27
+ install_url: "https://pypi.org/project/pipelex/",
28
+ auto_installable: true,
29
+ };
30
+ /** Shared metadata for the `pipelex-tools` PyPI package — provides the `plxt` binary. */
31
+ const PIPELEX_TOOLS_PKG = {
32
+ package: "pipelex-tools",
33
+ uv_package: "pipelex-tools",
34
+ version_constraint: ">=0.3.3",
35
+ version_extract: VERSION_RE,
36
+ install_url: "https://pypi.org/project/pipelex-tools/",
37
+ auto_installable: true,
38
+ };
16
39
  export const BINARY_RECOVERY = {
17
- pipelex: {
18
- binary: "pipelex",
19
- package: "pipelex",
20
- uv_package: "pipelex",
21
- version_constraint: ">=0.23.5",
22
- version_extract: VERSION_RE,
23
- install_url: "https://pypi.org/project/pipelex/",
24
- auto_installable: true,
25
- },
26
- "pipelex-agent": {
27
- binary: "pipelex-agent",
28
- package: "pipelex",
29
- uv_package: "pipelex",
30
- version_constraint: ">=0.23.5",
31
- version_extract: VERSION_RE,
32
- install_url: "https://pypi.org/project/pipelex/",
33
- auto_installable: true,
34
- },
35
- plxt: {
36
- binary: "plxt",
37
- package: "pipelex-tools",
38
- uv_package: "pipelex-tools",
39
- version_constraint: ">=0.3.2",
40
- version_extract: VERSION_RE,
41
- install_url: "https://pypi.org/project/pipelex-tools/",
42
- auto_installable: true,
43
- },
40
+ pipelex: { binary: "pipelex", ...PIPELEX_PKG },
41
+ "pipelex-agent": { binary: "pipelex-agent", ...PIPELEX_PKG },
42
+ plxt: { binary: "plxt", ...PIPELEX_TOOLS_PKG },
44
43
  };
45
44
  //# sourceMappingURL=binaries.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"binaries.js","sourceRoot":"","sources":["../../src/agent/binaries.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA4B;IAC9D,OAAO,8BAA8B,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,kBAAkB,GAAG,CAAC;AAC5F,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C,MAAM,CAAC,MAAM,eAAe,GAAuC;IACjE,OAAO,EAAE;QACP,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,SAAS;QACrB,kBAAkB,EAAE,UAAU;QAC9B,eAAe,EAAE,UAAU;QAC3B,WAAW,EAAE,mCAAmC;QAChD,gBAAgB,EAAE,IAAI;KACvB;IACD,eAAe,EAAE;QACf,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,SAAS;QACrB,kBAAkB,EAAE,UAAU;QAC9B,eAAe,EAAE,UAAU;QAC3B,WAAW,EAAE,mCAAmC;QAChD,gBAAgB,EAAE,IAAI;KACvB;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE,eAAe;QAC3B,kBAAkB,EAAE,SAAS;QAC7B,eAAe,EAAE,UAAU;QAC3B,WAAW,EAAE,yCAAyC;QACtD,gBAAgB,EAAE,IAAI;KACvB;CACF,CAAC"}
1
+ {"version":3,"file":"binaries.js","sourceRoot":"","sources":["../../src/agent/binaries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA4B;IAC9D,OAAO,8BAA8B,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,kBAAkB,GAAG,CAAC;AAC5F,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C,6GAA6G;AAC7G,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,SAAS;IACrB,kBAAkB,EAAE,UAAU;IAC9B,eAAe,EAAE,UAAU;IAC3B,WAAW,EAAE,mCAAmC;IAChD,gBAAgB,EAAE,IAAI;CACd,CAAC;AAEX,yFAAyF;AACzF,MAAM,iBAAiB,GAAG;IACxB,OAAO,EAAE,eAAe;IACxB,UAAU,EAAE,eAAe;IAC3B,kBAAkB,EAAE,SAAS;IAC7B,eAAe,EAAE,UAAU;IAC3B,WAAW,EAAE,yCAAyC;IACtD,gBAAgB,EAAE,IAAI;CACd,CAAC;AAEX,MAAM,CAAC,MAAM,eAAe,GAAuC;IACjE,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE;IAC9C,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE;IAC5D,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE;CAC/C,CAAC"}
@@ -0,0 +1,51 @@
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.
5
+ *
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.
9
+ *
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.
14
+ *
15
+ * 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
19
+ *
20
+ * Flags:
21
+ * --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
23
+ */
24
+ export interface AppliedChange {
25
+ table: string;
26
+ key: string;
27
+ value: string;
28
+ }
29
+ export interface CodexConfigWarning {
30
+ code: string;
31
+ message: string;
32
+ }
33
+ export interface CodexConfigInspection {
34
+ config_file: string;
35
+ exists: boolean;
36
+ needs_change: AppliedChange | null;
37
+ warnings: CodexConfigWarning[];
38
+ parse_error?: string;
39
+ }
40
+ interface ApplyConfigOptions {
41
+ check?: boolean;
42
+ dryRun?: boolean;
43
+ }
44
+ /**
45
+ * Read-only inspection of ~/.codex/config.toml. Used by doctor to surface
46
+ * issues without writing anything. Never throws — parse errors are reported
47
+ * via the `parse_error` field so the caller can decide how to render them.
48
+ */
49
+ export declare function inspectCodexConfig(): CodexConfigInspection;
50
+ export declare function agentCodexApplyConfig(options?: ApplyConfigOptions): Promise<void>;
51
+ export {};
@@ -0,0 +1,301 @@
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.
5
+ *
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.
9
+ *
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.
14
+ *
15
+ * 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
19
+ *
20
+ * Flags:
21
+ * --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
23
+ */
24
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
25
+ import { dirname, join } from "node:path";
26
+ import { homedir } from "node:os";
27
+ import { parse as parseToml } from "smol-toml";
28
+ 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;
39
+ // ── Helpers ────────────────────────────────────────────────────────
40
+ function configFilePath() {
41
+ return join(homedir(), ".codex", "config.toml");
42
+ }
43
+ function writeAtomic(path, contents) {
44
+ mkdirSync(dirname(path), { recursive: true });
45
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
46
+ writeFileSync(tmp, contents, { encoding: "utf8", mode: 0o644 });
47
+ renameSync(tmp, path);
48
+ }
49
+ /**
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
52
+ * formatting/comments elsewhere in the file are preserved verbatim.
53
+ */
54
+ function appendTomlTable(existing, table, key, value) {
55
+ const trimmed = existing.replace(/\s+$/, "");
56
+ const sep = trimmed.length === 0 ? "" : "\n\n";
57
+ return `${trimmed}${sep}[${table}]\n${key} = ${value}\n`;
58
+ }
59
+ /**
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.
64
+ */
65
+ function insertIntoExistingTable(existing, table, key, value) {
66
+ 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
70
+ // headers too, so we don't accidentally insert into one.
71
+ const anyHeaderRegex = /^\s*\[\[?[^\]]+\]\]?\s*(#.*)?$/;
72
+ let tableStart = -1;
73
+ for (let i = 0; i < lines.length; i++) {
74
+ if (headerRegex.test(lines[i])) {
75
+ tableStart = i;
76
+ break;
77
+ }
78
+ }
79
+ if (tableStart === -1) {
80
+ // Caller should have used appendTomlTable; defensive fallback.
81
+ return appendTomlTable(existing, table, key, value);
82
+ }
83
+ let insertAt = lines.length;
84
+ for (let i = tableStart + 1; i < lines.length; i++) {
85
+ if (anyHeaderRegex.test(lines[i])) {
86
+ insertAt = i;
87
+ break;
88
+ }
89
+ }
90
+ // Walk back over trailing blank lines so the new key sits with its table.
91
+ while (insertAt > tableStart + 1 && lines[insertAt - 1].trim() === "") {
92
+ insertAt--;
93
+ }
94
+ const before = lines.slice(0, insertAt);
95
+ const after = lines.slice(insertAt);
96
+ const newLine = `${key} = ${value}`;
97
+ return [...before, newLine, ...after].join("\n");
98
+ }
99
+ /**
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.
103
+ */
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;
111
+ }
112
+ return {
113
+ table: REQUIRED_TABLE,
114
+ key: REQUIRED_KEY,
115
+ value: String(REQUIRED_VALUE),
116
+ };
117
+ }
118
+ /** Collect non-fatal warnings about the user's config without modifying it. */
119
+ function collectWarnings(parsed) {
120
+ const warnings = [];
121
+ 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
+ });
130
+ }
131
+ if (parsed.sandbox_mode === "read-only") {
132
+ warnings.push({
133
+ code: "SANDBOX_READ_ONLY",
134
+ message: 'sandbox_mode = "read-only" prevents the apply_patch hook from running. Set to "workspace-write" or remove the key.',
135
+ });
136
+ }
137
+ return warnings;
138
+ }
139
+ /**
140
+ * Read-only inspection of ~/.codex/config.toml. Used by doctor to surface
141
+ * issues without writing anything. Never throws — parse errors are reported
142
+ * via the `parse_error` field so the caller can decide how to render them.
143
+ */
144
+ export function inspectCodexConfig() {
145
+ const file = configFilePath();
146
+ const exists = existsSync(file);
147
+ if (!exists) {
148
+ // No config file ⇒ definitely needs a change (the required key is missing).
149
+ return {
150
+ config_file: file,
151
+ exists: false,
152
+ needs_change: {
153
+ table: REQUIRED_TABLE,
154
+ key: REQUIRED_KEY,
155
+ value: String(REQUIRED_VALUE),
156
+ },
157
+ warnings: [],
158
+ };
159
+ }
160
+ let raw = "";
161
+ try {
162
+ raw = readFileSync(file, "utf8");
163
+ }
164
+ catch (err) {
165
+ return {
166
+ config_file: file,
167
+ exists: true,
168
+ needs_change: null,
169
+ warnings: [],
170
+ parse_error: `Failed to read ${file}: ${err.message}`,
171
+ };
172
+ }
173
+ let parsed;
174
+ try {
175
+ parsed = raw.trim().length === 0 ? {} : parseToml(raw);
176
+ }
177
+ catch (err) {
178
+ return {
179
+ config_file: file,
180
+ exists: true,
181
+ needs_change: null,
182
+ warnings: [],
183
+ parse_error: err.message,
184
+ };
185
+ }
186
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
187
+ return {
188
+ config_file: file,
189
+ exists: true,
190
+ needs_change: null,
191
+ warnings: [],
192
+ parse_error: "Top-level value is not a TOML table",
193
+ };
194
+ }
195
+ return {
196
+ config_file: file,
197
+ exists: true,
198
+ needs_change: diffRequired(parsed),
199
+ warnings: collectWarnings(parsed),
200
+ };
201
+ }
202
+ // ── Main ───────────────────────────────────────────────────────────
203
+ export async function agentCodexApplyConfig(options = {}) {
204
+ const file = configFilePath();
205
+ const checkMode = options.check === true;
206
+ const dryRunMode = options.dryRun === true;
207
+ let raw = "";
208
+ if (existsSync(file)) {
209
+ try {
210
+ raw = readFileSync(file, "utf8");
211
+ }
212
+ catch (err) {
213
+ agentError(`Failed to read ${file}: ${err.message}`, "IOError", { error_domain: AGENT_ERROR_DOMAINS.IO });
214
+ return;
215
+ }
216
+ }
217
+ let parsed;
218
+ try {
219
+ parsed = raw.trim().length === 0 ? {} : parseToml(raw);
220
+ }
221
+ catch (err) {
222
+ 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
+ return;
224
+ }
225
+ 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 });
227
+ return;
228
+ }
229
+ const change = diffRequired(parsed);
230
+ 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.
233
+ if (checkMode) {
234
+ if (change || warnings.length > 0) {
235
+ agentError(`Codex config needs attention. Run 'mthds-agent codex apply-config' (and review warnings).`, "ConfigError", { error_domain: AGENT_ERROR_DOMAINS.CONFIG });
236
+ return;
237
+ }
238
+ agentSuccess({ status: "ALREADY_OK", config_file: file });
239
+ return;
240
+ }
241
+ if (!change) {
242
+ agentSuccess({
243
+ status: "ALREADY_OK",
244
+ config_file: file,
245
+ warnings,
246
+ });
247
+ return;
248
+ }
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;
277
+ }
278
+ if (dryRunMode) {
279
+ agentSuccess({
280
+ status: "WOULD_APPLY",
281
+ config_file: file,
282
+ applied: [change],
283
+ warnings,
284
+ });
285
+ return;
286
+ }
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;
293
+ }
294
+ agentSuccess({
295
+ status: "APPLIED",
296
+ config_file: file,
297
+ applied: [change],
298
+ warnings,
299
+ });
300
+ }
301
+ //# sourceMappingURL=codex-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-config.js","sourceRoot":"","sources":["../../../src/agent/commands/codex-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * mthds-agent codex hook — Codex PostToolUse(apply_patch) hook runtime.
3
+ *
4
+ * Wired into ~/.codex/hooks.json by `mthds-agent codex install-hook`. Runs
5
+ * after every apply_patch tool call in a Codex session: parses the patch
6
+ * envelope from tool_input.command, finds touched .mthds files, and validates
7
+ * each one with plxt (lint + fmt). On lint or fmt failure it emits the Codex
8
+ * hook block protocol so the session sees the error.
9
+ *
10
+ * Replaces the bash script previously at ~/.codex/hooks/codex-validate-mthds.sh.
11
+ *
12
+ * Stdout protocol — Codex's hook contract, not the mthds agent JSON:
13
+ * - empty / no output → silent pass (no .mthds touched, or all clean)
14
+ * - {"decision":"block",...} → block the turn with the given reason
15
+ *
16
+ * Stage 3 (`mthds-agent validate bundle`) stays disabled until offline-mode
17
+ * validation lands in mthds-agent (Codex sandbox blocks the eager S3 fetch).
18
+ * Tracked as Phase 2D in mthds-plugins/TODOS.md.
19
+ */
20
+ interface PlxtRunResult {
21
+ exitCode: number;
22
+ stdout: string;
23
+ stderr: string;
24
+ }
25
+ /**
26
+ * Extract every distinct .mthds path mentioned in an apply_patch envelope.
27
+ *
28
+ * The envelope's relevant headers are:
29
+ * *** Update File: <path>
30
+ * *** Add File: <path>
31
+ * *** Move to: <path> (destination of a rename — we validate the dest)
32
+ *
33
+ * `Delete File:` and `Move from:` (the source of a rename) are deliberately
34
+ * skipped because the file no longer exists post-patch.
35
+ */
36
+ export declare function parseMthdsFiles(command: string): string[];
37
+ export declare function formatLintError(file: string, result: PlxtRunResult): string;
38
+ export declare function formatFmtError(file: string, result: PlxtRunResult): string;
39
+ export declare function buildBlockPayload(reason: string): string;
40
+ /**
41
+ * Build the ordered list of candidate paths for `name` on PATH.
42
+ *
43
+ * Pure — no fs access. Exported for testability.
44
+ *
45
+ * On Windows we also try each PATHEXT extension (`plxt.EXE`, `plxt.CMD`, ...)
46
+ * and skip the bare name, because Windows requires an extension to consider
47
+ * a file executable. PATHEXT defaults to `.COM;.EXE;.BAT;.CMD` when unset.
48
+ */
49
+ export declare function buildPathCandidates(name: string, pathEnv: string, platform: NodeJS.Platform, pathExt: string | undefined): string[];
50
+ /**
51
+ * Cross-platform `command -v <name>`. We can't rely on PATH lookup
52
+ * inside spawnSync because we need to detect absence vs. spawn failure
53
+ * before we report "missing tool" to the user.
54
+ *
55
+ * `accessSync(X_OK)` enforces the executable bit on POSIX. On Windows
56
+ * X_OK is satisfied by any readable file, but the PATHEXT loop in
57
+ * buildPathCandidates already restricts us to extensions Windows treats
58
+ * as runnable.
59
+ */
60
+ export declare function commandOnPath(name: string): boolean;
61
+ export interface CodexHookDeps {
62
+ readStdin: () => string;
63
+ fileExists: (path: string) => boolean;
64
+ hasPlxt: () => boolean;
65
+ runPlxt: (args: string[]) => PlxtRunResult;
66
+ emit: (output: string) => void;
67
+ }
68
+ export declare function runCodexHook(deps: CodexHookDeps): Promise<void>;
69
+ export declare function agentCodexHook(): Promise<void>;
70
+ export {};