@jmylchreest/aide-plugin 0.0.56 → 0.0.58

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Install command — registers aide plugin and MCP server in OpenCode config.
2
+ * Install command — registers aide plugin and MCP server for OpenCode or Codex CLI.
3
3
  *
4
4
  * On reinstall, detects and upgrades stale MCP command configurations
5
5
  * (e.g. old `aide-wrapper` commands) to the current format.
@@ -15,51 +15,37 @@ import {
15
15
  PLUGIN_NAME,
16
16
  MCP_SERVER_NAME,
17
17
  } from "./config.js";
18
+ import { installCodex, isCodexConfigured } from "./codex-config.js";
18
19
 
19
20
  export interface InstallFlags {
20
21
  project?: boolean;
21
22
  noMcp?: boolean;
23
+ platform?: "opencode" | "codex";
22
24
  }
23
25
 
24
- /**
25
- * Check if an existing MCP config has the current expected command format.
26
- * Returns false if the command is missing, empty, or uses an outdated format.
27
- */
28
26
  function isMcpCommandCurrent(config: ReturnType<typeof readConfig>): boolean {
29
27
  const mcpConfig = config.mcp?.[MCP_SERVER_NAME];
30
- if (!mcpConfig?.command || mcpConfig.command.length === 0) {
31
- return false;
32
- }
33
-
28
+ if (!mcpConfig?.command || mcpConfig.command.length === 0) return false;
34
29
  const cmd = mcpConfig.command;
35
-
36
- // Current format: ["bunx", "-y", "@jmylchreest/aide-plugin", "mcp"]
37
- if (
30
+ return (
38
31
  cmd.length === 4 &&
39
32
  cmd[0] === "bunx" &&
40
33
  cmd[1] === "-y" &&
41
34
  cmd[2] === PLUGIN_NAME &&
42
35
  cmd[3] === "mcp"
43
- ) {
44
- return true;
45
- }
46
-
47
- return false;
36
+ );
48
37
  }
49
38
 
50
- export async function install(flags: InstallFlags): Promise<void> {
39
+ async function installOpenCode(flags: InstallFlags): Promise<void> {
51
40
  const configPath = flags.project
52
41
  ? getProjectConfigPath()
53
42
  : getGlobalConfigPath();
54
43
 
55
44
  const scope = flags.project ? "project" : "global";
56
- console.log(`Installing aide plugin (${scope})...\n`);
45
+ console.log(`Installing aide plugin for OpenCode (${scope})...\n`);
57
46
 
58
- // Read existing config
59
47
  const existing = readConfig(configPath);
60
48
  const before = isAideConfigured(existing);
61
-
62
- // Check if MCP command needs updating (stale format from older versions)
63
49
  const mcpNeedsUpdate =
64
50
  !flags.noMcp && before.mcp && !isMcpCommandCurrent(existing);
65
51
 
@@ -71,16 +57,13 @@ export async function install(flags: InstallFlags): Promise<void> {
71
57
  return;
72
58
  }
73
59
 
74
- // If MCP config is stale, remove it so addAideToConfig will write the current version
75
60
  if (mcpNeedsUpdate && existing.mcp) {
76
61
  delete existing.mcp[MCP_SERVER_NAME];
77
62
  }
78
63
 
79
- // Apply changes
80
64
  const updated = addAideToConfig(existing, { noMcp: flags.noMcp });
81
65
  writeConfig(configPath, updated);
82
66
 
83
- // Report what was done
84
67
  const after = isAideConfigured(updated);
85
68
  console.log(`Updated: ${configPath}\n`);
86
69
 
@@ -110,3 +93,47 @@ export async function install(flags: InstallFlags): Promise<void> {
110
93
  );
111
94
  }
112
95
  }
96
+
97
+ async function installForCodex(flags: InstallFlags): Promise<void> {
98
+ const scope = flags.project ? "project" : "user";
99
+ console.log(`Installing aide for Codex CLI (${scope})...\n`);
100
+
101
+ const before = isCodexConfigured(scope);
102
+ if (before.mcp && before.hooks) {
103
+ console.log("aide is already configured for Codex CLI");
104
+ console.log(" mcp: registered in config.toml");
105
+ console.log(" hooks: registered in hooks.json");
106
+ console.log("\nNothing to do.");
107
+ return;
108
+ }
109
+
110
+ const result = installCodex(scope);
111
+
112
+ if (result.configWritten) {
113
+ console.log(" + Added aide MCP server to config.toml");
114
+ } else if (before.mcp) {
115
+ console.log(" = MCP server already registered in config.toml");
116
+ }
117
+
118
+ if (result.hooksWritten) {
119
+ console.log(" + Generated hooks.json with aide hooks");
120
+ } else if (before.hooks) {
121
+ console.log(" = Hooks already registered in hooks.json");
122
+ }
123
+
124
+ console.log("\nInstallation complete. Start Codex CLI to use aide.");
125
+
126
+ if (!flags.project) {
127
+ console.log(
128
+ "\nThe plugin is installed globally and will apply to all Codex CLI sessions.",
129
+ );
130
+ }
131
+ }
132
+
133
+ export async function install(flags: InstallFlags): Promise<void> {
134
+ if (flags.platform === "codex") {
135
+ await installForCodex(flags);
136
+ } else {
137
+ await installOpenCode(flags);
138
+ }
139
+ }
package/src/cli/status.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Status command — shows current aide installation status for OpenCode.
2
+ * Status command — shows current aide installation status for OpenCode or Codex CLI.
3
3
  */
4
4
 
5
5
  import { existsSync } from "fs";
@@ -9,38 +9,71 @@ import {
9
9
  isAideConfigured,
10
10
  readConfig,
11
11
  } from "./config.js";
12
+ import {
13
+ isCodexConfigured,
14
+ getCodexConfigTomlPath,
15
+ getCodexHooksJsonPath,
16
+ } from "./codex-config.js";
17
+
18
+ export interface StatusFlags {
19
+ platform?: "opencode" | "codex";
20
+ }
12
21
 
13
- export async function status(): Promise<void> {
14
- console.log("aide plugin status\n");
22
+ function showOpenCodeStatus(): void {
23
+ console.log("aide plugin status (OpenCode)\n");
15
24
 
16
25
  const globalPath = getGlobalConfigPath();
17
26
  const projectPath = getProjectConfigPath();
18
27
 
19
- // Global config
20
28
  console.log(`Global config: ${globalPath}`);
21
29
  if (existsSync(globalPath)) {
22
- const globalConfig = readConfig(globalPath);
23
- const globalStatus = isAideConfigured(globalConfig);
24
- console.log(
25
- ` plugin: ${globalStatus.plugin ? "registered" : "not found"}`,
26
- );
27
- console.log(` mcp: ${globalStatus.mcp ? "registered" : "not found"}`);
30
+ const s = isAideConfigured(readConfig(globalPath));
31
+ console.log(` plugin: ${s.plugin ? "registered" : "not found"}`);
32
+ console.log(` mcp: ${s.mcp ? "registered" : "not found"}`);
28
33
  } else {
29
34
  console.log(" (file does not exist)");
30
35
  }
31
36
 
32
37
  console.log();
33
38
 
34
- // Project config
35
39
  console.log(`Project config: ${projectPath}`);
36
40
  if (existsSync(projectPath)) {
37
- const projectConfig = readConfig(projectPath);
38
- const projectStatus = isAideConfigured(projectConfig);
39
- console.log(
40
- ` plugin: ${projectStatus.plugin ? "registered" : "not found"}`,
41
- );
42
- console.log(` mcp: ${projectStatus.mcp ? "registered" : "not found"}`);
41
+ const s = isAideConfigured(readConfig(projectPath));
42
+ console.log(` plugin: ${s.plugin ? "registered" : "not found"}`);
43
+ console.log(` mcp: ${s.mcp ? "registered" : "not found"}`);
43
44
  } else {
44
45
  console.log(" (file does not exist)");
45
46
  }
46
47
  }
48
+
49
+ function showCodexStatus(): void {
50
+ console.log("aide plugin status (Codex CLI)\n");
51
+
52
+ const userConfig = getCodexConfigTomlPath("user");
53
+ const userHooks = getCodexHooksJsonPath("user");
54
+ const userStatus = isCodexConfigured("user");
55
+
56
+ console.log(`User config: ${userConfig}`);
57
+ console.log(`User hooks: ${userHooks}`);
58
+ console.log(` mcp: ${userStatus.mcp ? "registered" : "not found"}`);
59
+ console.log(` hooks: ${userStatus.hooks ? "registered" : "not found"}`);
60
+
61
+ console.log();
62
+
63
+ const projectConfig = getCodexConfigTomlPath("project");
64
+ const projectHooks = getCodexHooksJsonPath("project");
65
+ const projectStatus = isCodexConfigured("project");
66
+
67
+ console.log(`Project config: ${projectConfig}`);
68
+ console.log(`Project hooks: ${projectHooks}`);
69
+ console.log(` mcp: ${projectStatus.mcp ? "registered" : "not found"}`);
70
+ console.log(` hooks: ${projectStatus.hooks ? "registered" : "not found"}`);
71
+ }
72
+
73
+ export async function status(flags?: StatusFlags): Promise<void> {
74
+ if (flags?.platform === "codex") {
75
+ showCodexStatus();
76
+ } else {
77
+ showOpenCodeStatus();
78
+ }
79
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Uninstall command — removes aide plugin and MCP server from OpenCode config.
2
+ * Uninstall command — removes aide plugin and MCP server from OpenCode or Codex CLI config.
3
3
  */
4
4
 
5
5
  import {
@@ -10,18 +10,20 @@ import {
10
10
  removeAideFromConfig,
11
11
  writeConfig,
12
12
  } from "./config.js";
13
+ import { uninstallCodex, isCodexConfigured } from "./codex-config.js";
13
14
 
14
15
  export interface UninstallFlags {
15
16
  project?: boolean;
17
+ platform?: "opencode" | "codex";
16
18
  }
17
19
 
18
- export async function uninstall(flags: UninstallFlags): Promise<void> {
20
+ async function uninstallOpenCode(flags: UninstallFlags): Promise<void> {
19
21
  const configPath = flags.project
20
22
  ? getProjectConfigPath()
21
23
  : getGlobalConfigPath();
22
24
 
23
25
  const scope = flags.project ? "project" : "global";
24
- console.log(`Uninstalling aide plugin (${scope})...\n`);
26
+ console.log(`Uninstalling aide plugin from OpenCode (${scope})...\n`);
25
27
 
26
28
  const existing = readConfig(configPath);
27
29
  const before = isAideConfigured(existing);
@@ -36,13 +38,32 @@ export async function uninstall(flags: UninstallFlags): Promise<void> {
36
38
  writeConfig(configPath, updated);
37
39
 
38
40
  console.log(`Updated: ${configPath}\n`);
41
+ if (before.plugin) console.log(` - Removed aide plugin from plugin array`);
42
+ if (before.mcp) console.log(` - Removed aide MCP server`);
43
+ console.log("\nUninstallation complete.");
44
+ }
39
45
 
40
- if (before.plugin) {
41
- console.log(` - Removed aide plugin from plugin array`);
42
- }
43
- if (before.mcp) {
44
- console.log(` - Removed aide MCP server`);
46
+ async function uninstallFromCodex(flags: UninstallFlags): Promise<void> {
47
+ const scope = flags.project ? "project" : "user";
48
+ console.log(`Uninstalling aide from Codex CLI (${scope})...\n`);
49
+
50
+ const before = isCodexConfigured(scope);
51
+ if (!before.mcp && !before.hooks) {
52
+ console.log("aide is not configured for Codex CLI.");
53
+ console.log("\nNothing to do.");
54
+ return;
45
55
  }
46
56
 
57
+ const result = uninstallCodex(scope);
58
+ if (result.configRemoved) console.log(" - Removed aide MCP server from config.toml");
59
+ if (result.hooksRemoved) console.log(" - Removed aide hooks from hooks.json");
47
60
  console.log("\nUninstallation complete.");
48
61
  }
62
+
63
+ export async function uninstall(flags: UninstallFlags): Promise<void> {
64
+ if (flags.platform === "codex") {
65
+ await uninstallFromCodex(flags);
66
+ } else {
67
+ await uninstallOpenCode(flags);
68
+ }
69
+ }
@@ -8,6 +8,7 @@
8
8
  * Supports:
9
9
  * - Claude Code: ~/.claude.json (user), .mcp.json (project) (reads legacy ~/.mcp.json)
10
10
  * - OpenCode: ~/.config/opencode/opencode.json (user), ./opencode.json (project)
11
+ * - Codex CLI: ~/.codex/config.toml (user), .codex/config.toml (project)
11
12
  * - Aide canonical: ~/.aide/config/mcp.json (user), .aide/config/mcp.json (project)
12
13
  *
13
14
  * Uses a journal to detect intentional deletions from the aide canonical file,
@@ -17,19 +18,84 @@
17
18
  import {
18
19
  existsSync,
19
20
  readFileSync,
21
+ readdirSync,
20
22
  writeFileSync,
21
23
  mkdirSync,
22
24
  statSync,
23
25
  } from "fs";
24
26
  import { join, dirname } from "path";
25
27
  import { homedir } from "os";
28
+ import * as TOML from "smol-toml";
26
29
 
27
30
  // =============================================================================
28
31
  // Types
29
32
  // =============================================================================
30
33
 
31
34
  /** Platform identifier for the current assistant. */
32
- export type McpPlatform = "claude-code" | "opencode";
35
+ export type McpPlatform = "claude-code" | "opencode" | "codex";
36
+
37
+ /**
38
+ * Discover MCP server names managed by installed Claude Code plugins.
39
+ *
40
+ * Scans plugin cache and marketplace directories for plugin.json files
41
+ * and returns the set of MCP server names they define. These servers
42
+ * must not be synced from assistant configs — doing so would override
43
+ * the plugin's own definition and bypass CLAUDE_PLUGIN_ROOT.
44
+ */
45
+ function getPluginManagedServers(): Set<string> {
46
+ const names = new Set<string>();
47
+ const pluginsDir = join(homedir(), ".claude", "plugins");
48
+
49
+ // Scan both cache (installed plugins) and marketplaces (git-cloned plugins)
50
+ const searchDirs = [
51
+ join(pluginsDir, "cache"),
52
+ join(pluginsDir, "marketplaces"),
53
+ ];
54
+
55
+ for (const baseDir of searchDirs) {
56
+ if (!existsSync(baseDir)) continue;
57
+
58
+ try {
59
+ // Walk up to 3 levels deep to find .claude-plugin/plugin.json
60
+ // cache: <marketplace>/<plugin>/<version>/.claude-plugin/plugin.json
61
+ // marketplaces: <name>/.claude-plugin/plugin.json
62
+ const findPluginJsons = (dir: string, depth: number): string[] => {
63
+ if (depth > 3) return [];
64
+ const results: string[] = [];
65
+ const pluginJson = join(dir, ".claude-plugin", "plugin.json");
66
+ if (existsSync(pluginJson)) results.push(pluginJson);
67
+
68
+ try {
69
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
70
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
71
+ results.push(...findPluginJsons(join(dir, entry.name), depth + 1));
72
+ }
73
+ }
74
+ } catch {
75
+ // Permission or read error — skip
76
+ }
77
+ return results;
78
+ };
79
+
80
+ for (const pluginJsonPath of findPluginJsons(baseDir, 0)) {
81
+ try {
82
+ const content = JSON.parse(readFileSync(pluginJsonPath, "utf-8"));
83
+ if (content.mcpServers && typeof content.mcpServers === "object") {
84
+ for (const serverName of Object.keys(content.mcpServers)) {
85
+ names.add(serverName);
86
+ }
87
+ }
88
+ } catch {
89
+ // Skip unparseable plugin.json files
90
+ }
91
+ }
92
+ } catch {
93
+ // Directory read error — skip
94
+ }
95
+ }
96
+
97
+ return names;
98
+ }
33
99
 
34
100
  /** Scope level for config files. */
35
101
  export type McpScope = "user" | "project";
@@ -139,6 +205,11 @@ function getAssistantReadPaths(
139
205
  ? [join(homedir(), ".config", "opencode", "opencode.json")]
140
206
  : [join(cwd, "opencode.json")];
141
207
  }
208
+ if (platform === "codex") {
209
+ return scope === "user"
210
+ ? [join(homedir(), ".codex", "config.toml")]
211
+ : [join(cwd, ".codex", "config.toml")];
212
+ }
142
213
  return [];
143
214
  }
144
215
 
@@ -157,6 +228,11 @@ function getAssistantWritePaths(
157
228
  ? [join(homedir(), ".config", "opencode", "opencode.json")]
158
229
  : [join(cwd, "opencode.json")];
159
230
  }
231
+ if (platform === "codex") {
232
+ return scope === "user"
233
+ ? [join(homedir(), ".codex", "config.toml")]
234
+ : [join(cwd, ".codex", "config.toml")];
235
+ }
160
236
  return [];
161
237
  }
162
238
 
@@ -304,6 +380,59 @@ function readOpenCodeConfig(path: string): Record<string, CanonicalMcpServer> {
304
380
  }
305
381
  }
306
382
 
383
+ /**
384
+ * Read Codex CLI MCP config from config.toml.
385
+ *
386
+ * Format:
387
+ * ```toml
388
+ * [mcp_servers.name]
389
+ * command = "npx"
390
+ * args = ["package-name"]
391
+ *
392
+ * [mcp_servers.name.env]
393
+ * KEY = "value"
394
+ * ```
395
+ */
396
+ function readCodexConfig(path: string): Record<string, CanonicalMcpServer> {
397
+ if (!existsSync(path)) return {};
398
+
399
+ try {
400
+ const raw = TOML.parse(readFileSync(path, "utf-8"));
401
+ const servers: Record<string, CanonicalMcpServer> = {};
402
+
403
+ const mcpServers = raw.mcp_servers as
404
+ | Record<string, Record<string, unknown>>
405
+ | undefined;
406
+ if (
407
+ typeof mcpServers !== "object" ||
408
+ mcpServers === null ||
409
+ Array.isArray(mcpServers)
410
+ )
411
+ return {};
412
+
413
+ for (const [name, def] of Object.entries(mcpServers)) {
414
+ const hasUrl = typeof def.url === "string";
415
+ const isRemote = hasUrl;
416
+
417
+ servers[name] = {
418
+ name,
419
+ type: isRemote ? "remote" : "local",
420
+ transport: isRemote ? "http" : undefined,
421
+ command: def.command as string | undefined,
422
+ args: def.args as string[] | undefined,
423
+ url: def.url as string | undefined,
424
+ env: (def.env as Record<string, string>) || undefined,
425
+ headers: def.headers as Record<string, string> | undefined,
426
+ enabled: true,
427
+ };
428
+ }
429
+
430
+ return servers;
431
+ } catch {
432
+ return {};
433
+ }
434
+ }
435
+
307
436
  /**
308
437
  * Read MCP servers from a specific assistant's config file.
309
438
  */
@@ -313,6 +442,7 @@ function readAssistantConfig(
313
442
  ): Record<string, CanonicalMcpServer> {
314
443
  if (platform === "claude-code") return readClaudeConfig(path);
315
444
  if (platform === "opencode") return readOpenCodeConfig(path);
445
+ if (platform === "codex") return readCodexConfig(path);
316
446
  return {};
317
447
  }
318
448
 
@@ -446,6 +576,59 @@ function writeOpenCodeConfig(
446
576
  writeFileSync(path, JSON.stringify(existing, null, 2) + "\n");
447
577
  }
448
578
 
579
+ /**
580
+ * Write canonical servers to a Codex CLI config.toml file.
581
+ *
582
+ * Preserves all non-mcp_servers content in the file.
583
+ */
584
+ function writeCodexConfig(
585
+ path: string,
586
+ servers: Record<string, CanonicalMcpServer>,
587
+ ): void {
588
+ const dir = dirname(path);
589
+ if (!existsSync(dir)) {
590
+ mkdirSync(dir, { recursive: true });
591
+ }
592
+
593
+ // Read existing file to preserve non-mcp_servers keys
594
+ let existing: Record<string, unknown> = {};
595
+ if (existsSync(path)) {
596
+ try {
597
+ existing = TOML.parse(readFileSync(path, "utf-8")) as Record<
598
+ string,
599
+ unknown
600
+ >;
601
+ } catch {
602
+ // Start fresh
603
+ }
604
+ }
605
+
606
+ const mcpServers: Record<string, Record<string, unknown>> = {};
607
+ for (const [name, server] of Object.entries(servers)) {
608
+ const entry: Record<string, unknown> = {};
609
+
610
+ if (server.type === "remote") {
611
+ if (server.url) entry.url = server.url;
612
+ if (server.headers) entry.headers = server.headers;
613
+ } else {
614
+ if (server.command) entry.command = server.command;
615
+ if (server.args) entry.args = server.args;
616
+ }
617
+
618
+ if (server.env && Object.keys(server.env).length > 0) {
619
+ entry.env = server.env;
620
+ }
621
+
622
+ mcpServers[name] = entry;
623
+ }
624
+
625
+ existing.mcp_servers = mcpServers;
626
+ writeFileSync(
627
+ path,
628
+ TOML.stringify(existing as Record<string, unknown>) + "\n",
629
+ );
630
+ }
631
+
449
632
  /**
450
633
  * Write canonical servers to the current assistant's config file.
451
634
  */
@@ -456,6 +639,7 @@ function writeAssistantConfig(
456
639
  ): void {
457
640
  if (platform === "claude-code") writeClaudeConfig(path, servers);
458
641
  else if (platform === "opencode") writeOpenCodeConfig(path, servers);
642
+ else if (platform === "codex") writeCodexConfig(path, servers);
459
643
  }
460
644
 
461
645
  // =============================================================================
@@ -579,7 +763,7 @@ interface McpSource {
579
763
  * Gather all MCP config source files for a given scope.
580
764
  */
581
765
  function gatherSources(scope: McpScope, cwd: string): McpSource[] {
582
- const platforms: McpPlatform[] = ["claude-code", "opencode"];
766
+ const platforms: McpPlatform[] = ["claude-code", "opencode", "codex"];
583
767
  const sources: McpSource[] = [];
584
768
 
585
769
  const aidePath =
@@ -782,7 +966,7 @@ function syncScope(
782
966
  if (!bestPresent) result.skipped++;
783
967
  }
784
968
 
785
- // Write to aide canonical config (if changed)
969
+ // Write to aide canonical config (if changed) — includes ALL servers
786
970
  const sortedStringify = (obj: object) =>
787
971
  JSON.stringify(obj, Object.keys(obj).sort());
788
972
  const aideChanged =
@@ -793,21 +977,37 @@ function syncScope(
793
977
  result.modified = true;
794
978
  }
795
979
 
980
+ // For Claude Code, exclude servers managed by plugins — these are
981
+ // defined in plugin.json and must not be written to assistant configs
982
+ // (doing so overrides the plugin's definition and bypasses
983
+ // CLAUDE_PLUGIN_ROOT). The aide canonical config keeps them so they
984
+ // can still sync to non-plugin assistants like OpenCode.
985
+ let assistantServers = finalServers;
986
+ if (platform === "claude-code") {
987
+ const pluginManaged = getPluginManagedServers();
988
+ if (pluginManaged.size > 0) {
989
+ assistantServers = { ...finalServers };
990
+ for (const name of pluginManaged) {
991
+ delete assistantServers[name];
992
+ }
993
+ }
994
+ }
995
+
796
996
  // Write to current assistant's config
797
997
  const assistantPaths = getAssistantWritePaths(platform, scope, cwd);
798
998
  for (const p of assistantPaths) {
799
999
  const existingAssistant = readAssistantConfig(platform, p);
800
1000
  const assistantChanged =
801
- sortedStringify(existingAssistant) !== sortedStringify(finalServers);
1001
+ sortedStringify(existingAssistant) !== sortedStringify(assistantServers);
802
1002
 
803
1003
  if (assistantChanged) {
804
- writeAssistantConfig(platform, p, finalServers);
1004
+ writeAssistantConfig(platform, p, assistantServers);
805
1005
  result.modified = true;
806
1006
  }
807
1007
  }
808
1008
 
809
- result.serversWritten = Object.keys(finalServers).length;
810
- result.serverNames = Object.keys(finalServers);
1009
+ result.serversWritten = Object.keys(assistantServers).length;
1010
+ result.serverNames = Object.keys(assistantServers);
811
1011
 
812
1012
  return result;
813
1013
  }
@@ -824,7 +1024,7 @@ function syncScope(
824
1024
  * handles intentional deletions via a journal, and writes the result to
825
1025
  * both the aide canonical config and the current assistant's config files.
826
1026
  *
827
- * @param platform - The current assistant platform ("claude-code" or "opencode")
1027
+ * @param platform - The current assistant platform ("claude-code", "opencode", or "codex")
828
1028
  * @param cwd - The project working directory
829
1029
  * @returns Combined sync results for both user and project scopes
830
1030
  */
package/src/core/types.ts CHANGED
@@ -111,7 +111,7 @@ export interface Skill {
111
111
  path: string;
112
112
  triggers: string[];
113
113
  description?: string;
114
- /** Optional platform restriction. If set, only matched on listed platforms ("opencode", "claude-code"). */
114
+ /** Optional platform restriction. If set, only matched on listed platforms ("opencode", "claude-code", "codex"). */
115
115
  platforms?: string[];
116
116
  /** Optional binary requirement. If set, skill is only matched when all listed binaries exist on PATH. */
117
117
  requires_binary?: string[];
@@ -156,7 +156,7 @@ export const MAX_PERSISTENCE_ITERATIONS = 20;
156
156
  * Identifies which host platform aide is running in.
157
157
  * Used for platform-specific behavior like binary discovery or context injection.
158
158
  */
159
- export type AidePlatform = "claude-code" | "opencode" | "unknown";
159
+ export type AidePlatform = "claude-code" | "opencode" | "codex" | "unknown";
160
160
 
161
161
  /**
162
162
  * Options for finding the aide binary.