@oh-my-pi/pi-coding-agent 4.2.0 → 4.2.2
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/CHANGELOG.md +46 -0
- package/docs/sdk.md +5 -5
- package/examples/sdk/10-settings.ts +2 -2
- package/package.json +5 -5
- package/src/capability/fs.ts +90 -0
- package/src/capability/index.ts +41 -227
- package/src/capability/types.ts +1 -11
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +7 -7
- package/src/core/agent-storage.ts +50 -0
- package/src/core/auth-storage.ts +102 -3
- package/src/core/bash-executor.ts +1 -1
- package/src/core/custom-tools/loader.ts +2 -2
- package/src/core/export-html/index.ts +1 -33
- package/src/core/extensions/loader.ts +2 -2
- package/src/core/extensions/types.ts +1 -1
- package/src/core/hooks/loader.ts +2 -2
- package/src/core/mcp/config.ts +2 -2
- package/src/core/model-registry.ts +46 -0
- package/src/core/sdk.ts +37 -29
- package/src/core/settings-manager.ts +152 -135
- package/src/core/skills.ts +72 -51
- package/src/core/slash-commands.ts +3 -3
- package/src/core/system-prompt.ts +52 -10
- package/src/core/tools/complete.ts +5 -2
- package/src/core/tools/edit.ts +7 -4
- package/src/core/tools/index.test.ts +16 -0
- package/src/core/tools/index.ts +21 -8
- package/src/core/tools/lsp/index.ts +4 -1
- package/src/core/tools/ssh.ts +6 -6
- package/src/core/tools/task/commands.ts +3 -9
- package/src/core/tools/task/executor.ts +88 -3
- package/src/core/tools/task/index.ts +4 -0
- package/src/core/tools/task/model-resolver.ts +10 -7
- package/src/core/tools/task/worker-protocol.ts +48 -2
- package/src/core/tools/task/worker.ts +152 -7
- package/src/core/tools/write.ts +7 -4
- package/src/discovery/agents-md.ts +13 -19
- package/src/discovery/builtin.ts +368 -293
- package/src/discovery/claude.ts +183 -345
- package/src/discovery/cline.ts +30 -10
- package/src/discovery/codex.ts +188 -272
- package/src/discovery/cursor.ts +106 -121
- package/src/discovery/gemini.ts +72 -97
- package/src/discovery/github.ts +7 -10
- package/src/discovery/helpers.ts +114 -57
- package/src/discovery/index.ts +1 -2
- package/src/discovery/mcp-json.ts +15 -18
- package/src/discovery/ssh.ts +9 -17
- package/src/discovery/vscode.ts +10 -5
- package/src/discovery/windsurf.ts +52 -86
- package/src/main.ts +5 -1
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +24 -11
- package/src/modes/interactive/components/extensions/state-manager.ts +19 -15
- package/src/modes/interactive/controllers/selector-controller.ts +9 -5
- package/src/modes/interactive/interactive-mode.ts +22 -15
- package/src/prompts/agents/plan.md +107 -30
- package/src/prompts/agents/task.md +5 -4
- package/src/prompts/system/system-prompt.md +5 -0
- package/src/prompts/tools/task.md +25 -19
- package/src/utils/shell.ts +2 -2
- package/src/prompts/agents/architect-plan.md +0 -10
- package/src/prompts/agents/implement-with-critic.md +0 -11
- package/src/prompts/agents/implement.md +0 -11
package/src/discovery/codex.ts
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* User directory: ~/.codex
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { join } from "path";
|
|
10
|
+
import { join } from "node:path";
|
|
11
11
|
import { parse as parseToml } from "smol-toml";
|
|
12
12
|
import type { ContextFile } from "../capability/context-file";
|
|
13
13
|
import { contextFileCapability } from "../capability/context-file";
|
|
14
14
|
import { type ExtensionModule, extensionModuleCapability } from "../capability/extension-module";
|
|
15
|
+
import { readFile } from "../capability/fs";
|
|
15
16
|
import type { Hook } from "../capability/hook";
|
|
16
17
|
import { hookCapability } from "../capability/hook";
|
|
17
18
|
import { registerProvider } from "../capability/index";
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
discoverExtensionModulePaths,
|
|
34
35
|
getExtensionNameFromPath,
|
|
35
36
|
loadFilesFromDir,
|
|
37
|
+
loadSkillsFromDir,
|
|
36
38
|
parseFrontmatter,
|
|
37
39
|
SOURCE_PATHS,
|
|
38
40
|
} from "./helpers";
|
|
@@ -41,27 +43,28 @@ const PROVIDER_ID = "codex";
|
|
|
41
43
|
const DISPLAY_NAME = "OpenAI Codex";
|
|
42
44
|
const PRIORITY = 70;
|
|
43
45
|
|
|
46
|
+
function getProjectCodexDir(ctx: LoadContext): string {
|
|
47
|
+
return join(ctx.cwd, ".codex");
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
// =============================================================================
|
|
45
51
|
// Context Files (AGENTS.md)
|
|
46
52
|
// =============================================================================
|
|
47
53
|
|
|
48
|
-
function loadContextFiles(ctx: LoadContext): LoadResult<ContextFile
|
|
54
|
+
async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFile>> {
|
|
49
55
|
const items: ContextFile[] = [];
|
|
50
56
|
const warnings: string[] = [];
|
|
51
57
|
|
|
52
58
|
// User level only: ~/.codex/AGENTS.md
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
_source: createSourceMeta(PROVIDER_ID, agentsMd, "user"),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
59
|
+
const agentsMd = join(ctx.home, SOURCE_PATHS.codex.userBase, "AGENTS.md");
|
|
60
|
+
const agentsContent = await readFile(agentsMd);
|
|
61
|
+
if (agentsContent) {
|
|
62
|
+
items.push({
|
|
63
|
+
path: agentsMd,
|
|
64
|
+
content: agentsContent,
|
|
65
|
+
level: "user",
|
|
66
|
+
_source: createSourceMeta(PROVIDER_ID, agentsMd, "user"),
|
|
67
|
+
});
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
return { items, warnings };
|
|
@@ -71,13 +74,19 @@ function loadContextFiles(ctx: LoadContext): LoadResult<ContextFile> {
|
|
|
71
74
|
// MCP Servers (config.toml)
|
|
72
75
|
// =============================================================================
|
|
73
76
|
|
|
74
|
-
function loadMCPServers(ctx: LoadContext): LoadResult<MCPServer
|
|
75
|
-
const items: MCPServer[] = [];
|
|
77
|
+
async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>> {
|
|
76
78
|
const warnings: string[] = [];
|
|
77
79
|
|
|
78
|
-
// User level: ~/.codex/config.toml
|
|
79
80
|
const userConfigPath = join(ctx.home, SOURCE_PATHS.codex.userBase, "config.toml");
|
|
80
|
-
const
|
|
81
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
82
|
+
const projectConfigPath = join(codexDir, "config.toml");
|
|
83
|
+
|
|
84
|
+
const [userConfig, projectConfig] = await Promise.all([
|
|
85
|
+
loadTomlConfig(ctx, userConfigPath),
|
|
86
|
+
loadTomlConfig(ctx, projectConfigPath),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const items: MCPServer[] = [];
|
|
81
90
|
if (userConfig) {
|
|
82
91
|
const servers = extractMCPServersFromToml(userConfig);
|
|
83
92
|
for (const [name, config] of Object.entries(servers)) {
|
|
@@ -88,29 +97,22 @@ function loadMCPServers(ctx: LoadContext): LoadResult<MCPServer> {
|
|
|
88
97
|
});
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (const [name, config] of Object.entries(servers)) {
|
|
100
|
-
items.push({
|
|
101
|
-
name,
|
|
102
|
-
...config,
|
|
103
|
-
_source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
100
|
+
if (projectConfig) {
|
|
101
|
+
const servers = extractMCPServersFromToml(projectConfig);
|
|
102
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
103
|
+
items.push({
|
|
104
|
+
name,
|
|
105
|
+
...config,
|
|
106
|
+
_source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
|
|
107
|
+
});
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
return { items, warnings };
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
function loadTomlConfig(
|
|
113
|
-
const content =
|
|
114
|
+
async function loadTomlConfig(_ctx: LoadContext, path: string): Promise<Record<string, unknown> | null> {
|
|
115
|
+
const content = await readFile(path);
|
|
114
116
|
if (!content) return null;
|
|
115
117
|
|
|
116
118
|
try {
|
|
@@ -205,56 +207,26 @@ function extractMCPServersFromToml(toml: Record<string, unknown>): Record<string
|
|
|
205
207
|
// Skills (skills/)
|
|
206
208
|
// =============================================================================
|
|
207
209
|
|
|
208
|
-
function loadSkills(ctx: LoadContext): LoadResult<Skill
|
|
209
|
-
const items: Skill[] = [];
|
|
210
|
-
const warnings: string[] = [];
|
|
211
|
-
|
|
212
|
-
// User level: ~/.codex/skills/
|
|
210
|
+
async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
213
211
|
const userSkillsDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "skills");
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
recursive: true,
|
|
217
|
-
transform: (name, content, path, source) => {
|
|
218
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
219
|
-
const skillName = frontmatter.name || name.replace(/\.md$/, "");
|
|
212
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
213
|
+
const projectSkillsDir = join(codexDir, "skills");
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
if (codexDir) {
|
|
237
|
-
const projectSkillsDir = join(codexDir, "skills");
|
|
238
|
-
const projectResult = loadFilesFromDir(ctx, projectSkillsDir, PROVIDER_ID, "project", {
|
|
239
|
-
extensions: ["md"],
|
|
240
|
-
recursive: true,
|
|
241
|
-
transform: (name, content, path, source) => {
|
|
242
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
243
|
-
const skillName = frontmatter.name || name.replace(/\.md$/, "");
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
name: String(skillName),
|
|
247
|
-
path,
|
|
248
|
-
content: body,
|
|
249
|
-
frontmatter,
|
|
250
|
-
level: "project" as const,
|
|
251
|
-
_source: source,
|
|
252
|
-
};
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
items.push(...projectResult.items);
|
|
256
|
-
warnings.push(...(projectResult.warnings || []));
|
|
257
|
-
}
|
|
215
|
+
const results = await Promise.all([
|
|
216
|
+
loadSkillsFromDir(ctx, {
|
|
217
|
+
dir: userSkillsDir,
|
|
218
|
+
providerId: PROVIDER_ID,
|
|
219
|
+
level: "user",
|
|
220
|
+
}),
|
|
221
|
+
loadSkillsFromDir(ctx, {
|
|
222
|
+
dir: projectSkillsDir,
|
|
223
|
+
providerId: PROVIDER_ID,
|
|
224
|
+
level: "project",
|
|
225
|
+
}),
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
const items = results.flatMap((r) => r.items);
|
|
229
|
+
const warnings = results.flatMap((r) => r.warnings || []);
|
|
258
230
|
|
|
259
231
|
return { items, warnings };
|
|
260
232
|
}
|
|
@@ -263,34 +235,32 @@ function loadSkills(ctx: LoadContext): LoadResult<Skill> {
|
|
|
263
235
|
// Extension Modules (extensions/)
|
|
264
236
|
// =============================================================================
|
|
265
237
|
|
|
266
|
-
function loadExtensionModules(ctx: LoadContext): LoadResult<ExtensionModule
|
|
267
|
-
const items: ExtensionModule[] = [];
|
|
238
|
+
async function loadExtensionModules(ctx: LoadContext): Promise<LoadResult<ExtensionModule>> {
|
|
268
239
|
const warnings: string[] = [];
|
|
269
240
|
|
|
270
|
-
// User level: ~/.codex/extensions/
|
|
271
241
|
const userExtensionsDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "extensions");
|
|
272
|
-
|
|
273
|
-
|
|
242
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
243
|
+
const projectExtensionsDir = join(codexDir, "extensions");
|
|
244
|
+
|
|
245
|
+
const [userPaths, projectPaths] = await Promise.all([
|
|
246
|
+
discoverExtensionModulePaths(ctx, userExtensionsDir),
|
|
247
|
+
discoverExtensionModulePaths(ctx, projectExtensionsDir),
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
const items: ExtensionModule[] = [
|
|
251
|
+
...userPaths.map((extPath) => ({
|
|
274
252
|
name: getExtensionNameFromPath(extPath),
|
|
275
253
|
path: extPath,
|
|
276
|
-
level: "user",
|
|
254
|
+
level: "user" as const,
|
|
277
255
|
_source: createSourceMeta(PROVIDER_ID, extPath, "user"),
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
items.push({
|
|
287
|
-
name: getExtensionNameFromPath(extPath),
|
|
288
|
-
path: extPath,
|
|
289
|
-
level: "project",
|
|
290
|
-
_source: createSourceMeta(PROVIDER_ID, extPath, "project"),
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
256
|
+
})),
|
|
257
|
+
...projectPaths.map((extPath) => ({
|
|
258
|
+
name: getExtensionNameFromPath(extPath),
|
|
259
|
+
path: extPath,
|
|
260
|
+
level: "project" as const,
|
|
261
|
+
_source: createSourceMeta(PROVIDER_ID, extPath, "project"),
|
|
262
|
+
})),
|
|
263
|
+
];
|
|
294
264
|
|
|
295
265
|
return { items, warnings };
|
|
296
266
|
}
|
|
@@ -299,52 +269,38 @@ function loadExtensionModules(ctx: LoadContext): LoadResult<ExtensionModule> {
|
|
|
299
269
|
// Slash Commands (commands/)
|
|
300
270
|
// =============================================================================
|
|
301
271
|
|
|
302
|
-
function loadSlashCommands(ctx: LoadContext): LoadResult<SlashCommand
|
|
303
|
-
const items: SlashCommand[] = [];
|
|
304
|
-
const warnings: string[] = [];
|
|
305
|
-
|
|
306
|
-
// User level: ~/.codex/commands/
|
|
272
|
+
async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashCommand>> {
|
|
307
273
|
const userCommandsDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "commands");
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
274
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
275
|
+
const projectCommandsDir = join(codexDir, "commands");
|
|
276
|
+
|
|
277
|
+
const transformCommand =
|
|
278
|
+
(level: "user" | "project") =>
|
|
279
|
+
(name: string, content: string, path: string, source: ReturnType<typeof createSourceMeta>) => {
|
|
311
280
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
312
281
|
const commandName = frontmatter.name || name.replace(/\.md$/, "");
|
|
313
|
-
|
|
314
282
|
return {
|
|
315
283
|
name: String(commandName),
|
|
316
284
|
path,
|
|
317
285
|
content: body,
|
|
318
|
-
level
|
|
286
|
+
level,
|
|
319
287
|
_source: source,
|
|
320
288
|
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// Project level: .codex/commands/
|
|
327
|
-
const codexDir = ctx.fs.walkUp(".codex", { dir: true });
|
|
328
|
-
if (codexDir) {
|
|
329
|
-
const projectCommandsDir = join(codexDir, "commands");
|
|
330
|
-
const projectResult = loadFilesFromDir(ctx, projectCommandsDir, PROVIDER_ID, "project", {
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const results = await Promise.all([
|
|
292
|
+
loadFilesFromDir(ctx, userCommandsDir, PROVIDER_ID, "user", {
|
|
331
293
|
extensions: ["md"],
|
|
332
|
-
transform: (
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
};
|
|
343
|
-
},
|
|
344
|
-
});
|
|
345
|
-
items.push(...projectResult.items);
|
|
346
|
-
warnings.push(...(projectResult.warnings || []));
|
|
347
|
-
}
|
|
294
|
+
transform: transformCommand("user"),
|
|
295
|
+
}),
|
|
296
|
+
loadFilesFromDir(ctx, projectCommandsDir, PROVIDER_ID, "project", {
|
|
297
|
+
extensions: ["md"],
|
|
298
|
+
transform: transformCommand("project"),
|
|
299
|
+
}),
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
const items = results.flatMap((r) => r.items);
|
|
303
|
+
const warnings = results.flatMap((r) => r.warnings || []);
|
|
348
304
|
|
|
349
305
|
return { items, warnings };
|
|
350
306
|
}
|
|
@@ -353,52 +309,41 @@ function loadSlashCommands(ctx: LoadContext): LoadResult<SlashCommand> {
|
|
|
353
309
|
// Prompts (prompts/*.md)
|
|
354
310
|
// =============================================================================
|
|
355
311
|
|
|
356
|
-
function loadPrompts(ctx: LoadContext): LoadResult<Prompt
|
|
357
|
-
const items: Prompt[] = [];
|
|
358
|
-
const warnings: string[] = [];
|
|
359
|
-
|
|
360
|
-
// User level: ~/.codex/prompts/
|
|
312
|
+
async function loadPrompts(ctx: LoadContext): Promise<LoadResult<Prompt>> {
|
|
361
313
|
const userPromptsDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "prompts");
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
314
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
315
|
+
const projectPromptsDir = join(codexDir, "prompts");
|
|
316
|
+
|
|
317
|
+
const transformPrompt = (
|
|
318
|
+
name: string,
|
|
319
|
+
content: string,
|
|
320
|
+
path: string,
|
|
321
|
+
source: ReturnType<typeof createSourceMeta>,
|
|
322
|
+
) => {
|
|
323
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
324
|
+
const promptName = frontmatter.name || name.replace(/\.md$/, "");
|
|
325
|
+
return {
|
|
326
|
+
name: String(promptName),
|
|
327
|
+
path,
|
|
328
|
+
content: body,
|
|
329
|
+
description: frontmatter.description ? String(frontmatter.description) : undefined,
|
|
330
|
+
_source: source,
|
|
331
|
+
};
|
|
332
|
+
};
|
|
367
333
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
path,
|
|
371
|
-
content: body,
|
|
372
|
-
description: frontmatter.description ? String(frontmatter.description) : undefined,
|
|
373
|
-
_source: source,
|
|
374
|
-
};
|
|
375
|
-
},
|
|
376
|
-
});
|
|
377
|
-
items.push(...userResult.items);
|
|
378
|
-
warnings.push(...(userResult.warnings || []));
|
|
379
|
-
|
|
380
|
-
// Project level: .codex/prompts/
|
|
381
|
-
const codexDir = ctx.fs.walkUp(".codex", { dir: true });
|
|
382
|
-
if (codexDir) {
|
|
383
|
-
const projectPromptsDir = join(codexDir, "prompts");
|
|
384
|
-
const projectResult = loadFilesFromDir(ctx, projectPromptsDir, PROVIDER_ID, "project", {
|
|
334
|
+
const results = await Promise.all([
|
|
335
|
+
loadFilesFromDir(ctx, userPromptsDir, PROVIDER_ID, "user", {
|
|
385
336
|
extensions: ["md"],
|
|
386
|
-
transform:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
};
|
|
397
|
-
},
|
|
398
|
-
});
|
|
399
|
-
items.push(...projectResult.items);
|
|
400
|
-
warnings.push(...(projectResult.warnings || []));
|
|
401
|
-
}
|
|
337
|
+
transform: transformPrompt,
|
|
338
|
+
}),
|
|
339
|
+
loadFilesFromDir(ctx, projectPromptsDir, PROVIDER_ID, "project", {
|
|
340
|
+
extensions: ["md"],
|
|
341
|
+
transform: transformPrompt,
|
|
342
|
+
}),
|
|
343
|
+
]);
|
|
344
|
+
|
|
345
|
+
const items = results.flatMap((r) => r.items);
|
|
346
|
+
const warnings = results.flatMap((r) => r.warnings || []);
|
|
402
347
|
|
|
403
348
|
return { items, warnings };
|
|
404
349
|
}
|
|
@@ -407,59 +352,41 @@ function loadPrompts(ctx: LoadContext): LoadResult<Prompt> {
|
|
|
407
352
|
// Hooks (hooks/)
|
|
408
353
|
// =============================================================================
|
|
409
354
|
|
|
410
|
-
function loadHooks(ctx: LoadContext): LoadResult<Hook
|
|
411
|
-
const items: Hook[] = [];
|
|
412
|
-
const warnings: string[] = [];
|
|
413
|
-
|
|
414
|
-
// User level: ~/.codex/hooks/
|
|
355
|
+
async function loadHooks(ctx: LoadContext): Promise<LoadResult<Hook>> {
|
|
415
356
|
const userHooksDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "hooks");
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
357
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
358
|
+
const projectHooksDir = join(codexDir, "hooks");
|
|
359
|
+
|
|
360
|
+
const transformHook =
|
|
361
|
+
(level: "user" | "project") =>
|
|
362
|
+
(name: string, _content: string, path: string, source: ReturnType<typeof createSourceMeta>) => {
|
|
420
363
|
const baseName = name.replace(/\.(ts|js)$/, "");
|
|
421
364
|
const match = baseName.match(/^(pre|post)-(.+)$/);
|
|
422
365
|
const hookType = (match?.[1] as "pre" | "post") || "pre";
|
|
423
366
|
const toolName = match?.[2] || baseName;
|
|
424
|
-
|
|
425
367
|
return {
|
|
426
368
|
name,
|
|
427
369
|
path,
|
|
428
370
|
type: hookType,
|
|
429
371
|
tool: toolName,
|
|
430
|
-
level
|
|
372
|
+
level,
|
|
431
373
|
_source: source,
|
|
432
374
|
};
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
// Project level: .codex/hooks/
|
|
439
|
-
const codexDir = ctx.fs.walkUp(".codex", { dir: true });
|
|
440
|
-
if (codexDir) {
|
|
441
|
-
const projectHooksDir = join(codexDir, "hooks");
|
|
442
|
-
const projectResult = loadFilesFromDir(ctx, projectHooksDir, PROVIDER_ID, "project", {
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const results = await Promise.all([
|
|
378
|
+
loadFilesFromDir(ctx, userHooksDir, PROVIDER_ID, "user", {
|
|
443
379
|
extensions: ["ts", "js"],
|
|
444
|
-
transform: (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
tool: toolName,
|
|
455
|
-
level: "project" as const,
|
|
456
|
-
_source: source,
|
|
457
|
-
};
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
items.push(...projectResult.items);
|
|
461
|
-
warnings.push(...(projectResult.warnings || []));
|
|
462
|
-
}
|
|
380
|
+
transform: transformHook("user"),
|
|
381
|
+
}),
|
|
382
|
+
loadFilesFromDir(ctx, projectHooksDir, PROVIDER_ID, "project", {
|
|
383
|
+
extensions: ["ts", "js"],
|
|
384
|
+
transform: transformHook("project"),
|
|
385
|
+
}),
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
const items = results.flatMap((r) => r.items);
|
|
389
|
+
const warnings = results.flatMap((r) => r.warnings || []);
|
|
463
390
|
|
|
464
391
|
return { items, warnings };
|
|
465
392
|
}
|
|
@@ -468,46 +395,36 @@ function loadHooks(ctx: LoadContext): LoadResult<Hook> {
|
|
|
468
395
|
// Tools (tools/)
|
|
469
396
|
// =============================================================================
|
|
470
397
|
|
|
471
|
-
function loadTools(ctx: LoadContext): LoadResult<CustomTool
|
|
472
|
-
const items: CustomTool[] = [];
|
|
473
|
-
const warnings: string[] = [];
|
|
474
|
-
|
|
475
|
-
// User level: ~/.codex/tools/
|
|
398
|
+
async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
476
399
|
const userToolsDir = join(ctx.home, SOURCE_PATHS.codex.userBase, "tools");
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
400
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
401
|
+
const projectToolsDir = join(codexDir, "tools");
|
|
402
|
+
|
|
403
|
+
const transformTool =
|
|
404
|
+
(level: "user" | "project") =>
|
|
405
|
+
(name: string, _content: string, path: string, source: ReturnType<typeof createSourceMeta>) => {
|
|
480
406
|
const toolName = name.replace(/\.(ts|js)$/, "");
|
|
481
407
|
return {
|
|
482
408
|
name: toolName,
|
|
483
409
|
path,
|
|
484
|
-
level
|
|
410
|
+
level,
|
|
485
411
|
_source: source,
|
|
486
412
|
} as CustomTool;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
// Project level: .codex/tools/
|
|
493
|
-
const codexDir = ctx.fs.walkUp(".codex", { dir: true });
|
|
494
|
-
if (codexDir) {
|
|
495
|
-
const projectToolsDir = join(codexDir, "tools");
|
|
496
|
-
const projectResult = loadFilesFromDir(ctx, projectToolsDir, PROVIDER_ID, "project", {
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const results = await Promise.all([
|
|
416
|
+
loadFilesFromDir(ctx, userToolsDir, PROVIDER_ID, "user", {
|
|
497
417
|
extensions: ["ts", "js"],
|
|
498
|
-
transform: (
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
items.push(...projectResult.items);
|
|
509
|
-
warnings.push(...(projectResult.warnings || []));
|
|
510
|
-
}
|
|
418
|
+
transform: transformTool("user"),
|
|
419
|
+
}),
|
|
420
|
+
loadFilesFromDir(ctx, projectToolsDir, PROVIDER_ID, "project", {
|
|
421
|
+
extensions: ["ts", "js"],
|
|
422
|
+
transform: transformTool("project"),
|
|
423
|
+
}),
|
|
424
|
+
]);
|
|
425
|
+
|
|
426
|
+
const items = results.flatMap((r) => r.items);
|
|
427
|
+
const warnings = results.flatMap((r) => r.warnings || []);
|
|
511
428
|
|
|
512
429
|
return { items, warnings };
|
|
513
430
|
}
|
|
@@ -516,31 +433,30 @@ function loadTools(ctx: LoadContext): LoadResult<CustomTool> {
|
|
|
516
433
|
// Settings (config.toml)
|
|
517
434
|
// =============================================================================
|
|
518
435
|
|
|
519
|
-
function loadSettings(ctx: LoadContext): LoadResult<Settings
|
|
520
|
-
const items: Settings[] = [];
|
|
436
|
+
async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
521
437
|
const warnings: string[] = [];
|
|
522
438
|
|
|
523
|
-
// User level: ~/.codex/config.toml
|
|
524
439
|
const userConfigPath = join(ctx.home, SOURCE_PATHS.codex.userBase, "config.toml");
|
|
525
|
-
const
|
|
440
|
+
const codexDir = getProjectCodexDir(ctx);
|
|
441
|
+
const projectConfigPath = join(codexDir, "config.toml");
|
|
442
|
+
|
|
443
|
+
const [userConfig, projectConfig] = await Promise.all([
|
|
444
|
+
loadTomlConfig(ctx, userConfigPath),
|
|
445
|
+
loadTomlConfig(ctx, projectConfigPath),
|
|
446
|
+
]);
|
|
447
|
+
|
|
448
|
+
const items: Settings[] = [];
|
|
526
449
|
if (userConfig) {
|
|
527
450
|
items.push({
|
|
528
451
|
...userConfig,
|
|
529
452
|
_source: createSourceMeta(PROVIDER_ID, userConfigPath, "user"),
|
|
530
453
|
} as Settings);
|
|
531
454
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const projectConfig = loadTomlConfig(ctx, projectConfigPath);
|
|
538
|
-
if (projectConfig) {
|
|
539
|
-
items.push({
|
|
540
|
-
...projectConfig,
|
|
541
|
-
_source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
|
|
542
|
-
} as Settings);
|
|
543
|
-
}
|
|
455
|
+
if (projectConfig) {
|
|
456
|
+
items.push({
|
|
457
|
+
...projectConfig,
|
|
458
|
+
_source: createSourceMeta(PROVIDER_ID, projectConfigPath, "project"),
|
|
459
|
+
} as Settings);
|
|
544
460
|
}
|
|
545
461
|
|
|
546
462
|
return { items, warnings };
|