@claude-flow/codex 3.0.0-alpha.1
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/README.md +301 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +649 -0
- package/dist/cli.js.map +1 -0
- package/dist/generators/agents-md.d.ts +12 -0
- package/dist/generators/agents-md.d.ts.map +1 -0
- package/dist/generators/agents-md.js +641 -0
- package/dist/generators/agents-md.js.map +1 -0
- package/dist/generators/config-toml.d.ts +74 -0
- package/dist/generators/config-toml.d.ts.map +1 -0
- package/dist/generators/config-toml.js +910 -0
- package/dist/generators/config-toml.js.map +1 -0
- package/dist/generators/index.d.ts +9 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +9 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/skill-md.d.ts +20 -0
- package/dist/generators/skill-md.d.ts.map +1 -0
- package/dist/generators/skill-md.js +946 -0
- package/dist/generators/skill-md.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/initializer.d.ts +87 -0
- package/dist/initializer.d.ts.map +1 -0
- package/dist/initializer.js +666 -0
- package/dist/initializer.js.map +1 -0
- package/dist/migrations/index.d.ts +114 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +856 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/templates/index.d.ts +92 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +284 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types.d.ts +218 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/validators/index.d.ts +42 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +929 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @claude-flow/codex - Migrations
|
|
3
|
+
*
|
|
4
|
+
* Migration utilities for converting Claude Code configurations to Codex format
|
|
5
|
+
* Supports full CLAUDE.md parsing with section extraction, skill conversion,
|
|
6
|
+
* and proper AGENTS.md/config.toml generation.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Feature mappings between Claude Code and Codex
|
|
10
|
+
*/
|
|
11
|
+
export const FEATURE_MAPPINGS = [
|
|
12
|
+
{
|
|
13
|
+
claudeCode: 'CLAUDE.md',
|
|
14
|
+
codex: 'AGENTS.md',
|
|
15
|
+
status: 'mapped',
|
|
16
|
+
notes: 'Main instruction file - content is portable',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
claudeCode: 'CLAUDE.local.md',
|
|
20
|
+
codex: '.codex/AGENTS.override.md',
|
|
21
|
+
status: 'mapped',
|
|
22
|
+
notes: 'Local overrides - gitignored in both',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
claudeCode: 'settings.json',
|
|
26
|
+
codex: 'config.toml',
|
|
27
|
+
status: 'mapped',
|
|
28
|
+
notes: 'Format conversion required (JSON to TOML)',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
claudeCode: '/skill-name',
|
|
32
|
+
codex: '$skill-name',
|
|
33
|
+
status: 'mapped',
|
|
34
|
+
notes: 'Skill invocation syntax - search and replace',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
claudeCode: 'TodoWrite',
|
|
38
|
+
codex: 'Task tracking',
|
|
39
|
+
status: 'mapped',
|
|
40
|
+
notes: 'Similar functionality with different API',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
claudeCode: 'Task tool agents',
|
|
44
|
+
codex: 'Sub-agent collaboration',
|
|
45
|
+
status: 'partial',
|
|
46
|
+
notes: 'Codex sub-agents via CODEX_HANDOFF_TARGET env var',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
claudeCode: 'MCP servers',
|
|
50
|
+
codex: '[mcp_servers]',
|
|
51
|
+
status: 'mapped',
|
|
52
|
+
notes: 'Configuration format differs but same functionality',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
claudeCode: 'hooks system',
|
|
56
|
+
codex: 'Automations',
|
|
57
|
+
status: 'partial',
|
|
58
|
+
notes: 'Codex automations are scheduled, not event-driven',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
claudeCode: 'EnterPlanMode',
|
|
62
|
+
codex: 'No direct equivalent',
|
|
63
|
+
status: 'unsupported',
|
|
64
|
+
notes: 'Codex uses different planning paradigm',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
claudeCode: 'Permission modes',
|
|
68
|
+
codex: 'approval_policy + sandbox_mode',
|
|
69
|
+
status: 'mapped',
|
|
70
|
+
notes: 'Codex provides more granular control',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Hook keywords recognized in CLAUDE.md
|
|
75
|
+
*/
|
|
76
|
+
const HOOK_KEYWORDS = [
|
|
77
|
+
'pre-task',
|
|
78
|
+
'post-task',
|
|
79
|
+
'pre-edit',
|
|
80
|
+
'post-edit',
|
|
81
|
+
'pre-command',
|
|
82
|
+
'post-command',
|
|
83
|
+
'session-start',
|
|
84
|
+
'session-end',
|
|
85
|
+
'session-restore',
|
|
86
|
+
'route',
|
|
87
|
+
'explain',
|
|
88
|
+
'pretrain',
|
|
89
|
+
'notify',
|
|
90
|
+
];
|
|
91
|
+
/**
|
|
92
|
+
* Patterns that need migration warnings
|
|
93
|
+
*/
|
|
94
|
+
const WARNING_PATTERNS = [
|
|
95
|
+
{ pattern: 'EnterPlanMode', message: 'EnterPlanMode has no direct Codex equivalent - review planning workflow' },
|
|
96
|
+
{ pattern: 'claude -p', message: 'claude -p headless mode - use Codex sub-agent patterns instead' },
|
|
97
|
+
{ pattern: 'TodoWrite', message: 'TodoWrite - Codex uses different task tracking approach' },
|
|
98
|
+
{ pattern: /--dangerously-skip-permissions/g, message: 'Dangerous permission skip detected - use Codex approval_policy instead' },
|
|
99
|
+
{ pattern: /mcp__claude-flow__/g, message: 'MCP tool calls need migration to Codex MCP configuration' },
|
|
100
|
+
{ pattern: /mcp__ruv-swarm__/g, message: 'Swarm MCP calls - ensure ruv-swarm MCP server is configured in config.toml' },
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Parse a CLAUDE.md file completely
|
|
104
|
+
*/
|
|
105
|
+
export async function parseClaudeMd(content) {
|
|
106
|
+
const lines = content.split('\n');
|
|
107
|
+
const result = {
|
|
108
|
+
title: '',
|
|
109
|
+
sections: [],
|
|
110
|
+
skills: [],
|
|
111
|
+
hooks: [],
|
|
112
|
+
customInstructions: [],
|
|
113
|
+
codeBlocks: [],
|
|
114
|
+
mcpServers: [],
|
|
115
|
+
settings: {},
|
|
116
|
+
warnings: [],
|
|
117
|
+
};
|
|
118
|
+
// Extract title (first H1)
|
|
119
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
120
|
+
if (titleMatch && titleMatch[1]) {
|
|
121
|
+
result.title = titleMatch[1].trim();
|
|
122
|
+
result.settings.projectName = result.title;
|
|
123
|
+
}
|
|
124
|
+
// Parse sections
|
|
125
|
+
result.sections = parseSections(content, lines);
|
|
126
|
+
// Extract skills (both /skill-name and $skill-name syntax)
|
|
127
|
+
result.skills = extractSkills(content, lines);
|
|
128
|
+
// Extract hooks
|
|
129
|
+
result.hooks = extractHooks(content);
|
|
130
|
+
// Extract code blocks
|
|
131
|
+
result.codeBlocks = extractCodeBlocks(content, lines);
|
|
132
|
+
// Extract MCP server configurations from code blocks
|
|
133
|
+
result.mcpServers = extractMcpServers(result.codeBlocks);
|
|
134
|
+
// Extract settings from content
|
|
135
|
+
result.settings = {
|
|
136
|
+
...result.settings,
|
|
137
|
+
...extractSettings(content, result.sections),
|
|
138
|
+
};
|
|
139
|
+
// Extract custom instructions (behavioral rules)
|
|
140
|
+
result.customInstructions = extractBehavioralRules(content);
|
|
141
|
+
// Check for patterns that need warnings
|
|
142
|
+
for (const { pattern, message } of WARNING_PATTERNS) {
|
|
143
|
+
if (typeof pattern === 'string') {
|
|
144
|
+
if (content.includes(pattern)) {
|
|
145
|
+
result.warnings.push(message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
if (pattern.test(content)) {
|
|
150
|
+
result.warnings.push(message);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Parse sections from markdown content
|
|
158
|
+
*/
|
|
159
|
+
function parseSections(content, lines) {
|
|
160
|
+
const sections = [];
|
|
161
|
+
const sectionRegex = /^(#{1,6})\s+(.+)$/;
|
|
162
|
+
let currentSection = null;
|
|
163
|
+
let contentLines = [];
|
|
164
|
+
for (let i = 0; i < lines.length; i++) {
|
|
165
|
+
const line = lines[i];
|
|
166
|
+
const match = sectionRegex.exec(line);
|
|
167
|
+
if (match && match[1] && match[2]) {
|
|
168
|
+
// Save previous section
|
|
169
|
+
if (currentSection) {
|
|
170
|
+
currentSection.content = contentLines.join('\n').trim();
|
|
171
|
+
currentSection.endLine = i;
|
|
172
|
+
sections.push(currentSection);
|
|
173
|
+
}
|
|
174
|
+
// Start new section
|
|
175
|
+
currentSection = {
|
|
176
|
+
level: match[1].length,
|
|
177
|
+
title: match[2].trim(),
|
|
178
|
+
content: '',
|
|
179
|
+
startLine: i + 1,
|
|
180
|
+
endLine: i + 1,
|
|
181
|
+
};
|
|
182
|
+
contentLines = [];
|
|
183
|
+
}
|
|
184
|
+
else if (currentSection) {
|
|
185
|
+
contentLines.push(line);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Save last section
|
|
189
|
+
if (currentSection) {
|
|
190
|
+
currentSection.content = contentLines.join('\n').trim();
|
|
191
|
+
currentSection.endLine = lines.length;
|
|
192
|
+
sections.push(currentSection);
|
|
193
|
+
}
|
|
194
|
+
return sections;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Extract skill references from content
|
|
198
|
+
*/
|
|
199
|
+
function extractSkills(content, lines) {
|
|
200
|
+
const skills = [];
|
|
201
|
+
const seenSkills = new Set();
|
|
202
|
+
// Slash syntax: /skill-name
|
|
203
|
+
const slashRegex = /\/([a-z][a-z0-9-]*)/g;
|
|
204
|
+
let match;
|
|
205
|
+
while ((match = slashRegex.exec(content)) !== null) {
|
|
206
|
+
const name = match[1];
|
|
207
|
+
// Skip common false positives
|
|
208
|
+
if (['src', 'dist', 'docs', 'tests', 'config', 'scripts', 'examples', 'node_modules', 'workspaces'].includes(name)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (!seenSkills.has(`slash:${name}`)) {
|
|
212
|
+
seenSkills.add(`slash:${name}`);
|
|
213
|
+
const lineNum = findLineNumber(content, match.index);
|
|
214
|
+
skills.push({
|
|
215
|
+
name,
|
|
216
|
+
syntax: 'slash',
|
|
217
|
+
context: getContextAroundMatch(lines, lineNum),
|
|
218
|
+
line: lineNum,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Dollar syntax: $skill-name
|
|
223
|
+
const dollarRegex = /\$([a-z][a-z0-9-]+)/g;
|
|
224
|
+
while ((match = dollarRegex.exec(content)) !== null) {
|
|
225
|
+
const name = match[1];
|
|
226
|
+
if (!seenSkills.has(`dollar:${name}`)) {
|
|
227
|
+
seenSkills.add(`dollar:${name}`);
|
|
228
|
+
const lineNum = findLineNumber(content, match.index);
|
|
229
|
+
skills.push({
|
|
230
|
+
name,
|
|
231
|
+
syntax: 'dollar',
|
|
232
|
+
context: getContextAroundMatch(lines, lineNum),
|
|
233
|
+
line: lineNum,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return skills;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extract hooks referenced in content
|
|
241
|
+
*/
|
|
242
|
+
function extractHooks(content) {
|
|
243
|
+
const hooks = [];
|
|
244
|
+
const lowerContent = content.toLowerCase();
|
|
245
|
+
for (const hook of HOOK_KEYWORDS) {
|
|
246
|
+
if (lowerContent.includes(hook)) {
|
|
247
|
+
hooks.push(hook);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return hooks;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Extract code blocks from markdown
|
|
254
|
+
*/
|
|
255
|
+
function extractCodeBlocks(content, lines) {
|
|
256
|
+
const blocks = [];
|
|
257
|
+
const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
|
|
258
|
+
let match;
|
|
259
|
+
while ((match = codeBlockRegex.exec(content)) !== null) {
|
|
260
|
+
blocks.push({
|
|
261
|
+
language: match[1] || 'text',
|
|
262
|
+
content: match[2].trim(),
|
|
263
|
+
line: findLineNumber(content, match.index),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return blocks;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Extract MCP server configurations from code blocks
|
|
270
|
+
*/
|
|
271
|
+
function extractMcpServers(codeBlocks) {
|
|
272
|
+
const servers = [];
|
|
273
|
+
for (const block of codeBlocks) {
|
|
274
|
+
// Look for MCP server configurations in bash/shell blocks
|
|
275
|
+
if (['bash', 'shell', 'sh', 'zsh'].includes(block.language)) {
|
|
276
|
+
// Pattern: claude mcp add <name> <command> [args...]
|
|
277
|
+
const mcpAddRegex = /claude\s+mcp\s+add\s+(\S+)\s+(.+)/g;
|
|
278
|
+
let match;
|
|
279
|
+
while ((match = mcpAddRegex.exec(block.content)) !== null) {
|
|
280
|
+
const name = match[1];
|
|
281
|
+
const parts = match[2].trim().split(/\s+/);
|
|
282
|
+
servers.push({
|
|
283
|
+
name,
|
|
284
|
+
command: parts[0] || 'npx',
|
|
285
|
+
args: parts.slice(1),
|
|
286
|
+
enabled: true,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Look for JSON MCP configurations
|
|
291
|
+
if (['json', 'jsonc'].includes(block.language)) {
|
|
292
|
+
try {
|
|
293
|
+
const parsed = JSON.parse(block.content);
|
|
294
|
+
if (parsed.mcpServers) {
|
|
295
|
+
for (const [name, config] of Object.entries(parsed.mcpServers)) {
|
|
296
|
+
const mcpConfig = config;
|
|
297
|
+
servers.push({
|
|
298
|
+
name,
|
|
299
|
+
command: mcpConfig.command || 'npx',
|
|
300
|
+
args: mcpConfig.args || [],
|
|
301
|
+
enabled: true,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// Not valid JSON, skip
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Look for JavaScript/TypeScript MCP tool calls
|
|
311
|
+
if (['javascript', 'typescript', 'js', 'ts'].includes(block.language)) {
|
|
312
|
+
// Pattern: mcp__<server>__<tool>
|
|
313
|
+
const mcpCallRegex = /mcp__([a-z-]+)__/g;
|
|
314
|
+
const seenServers = new Set();
|
|
315
|
+
let match;
|
|
316
|
+
while ((match = mcpCallRegex.exec(block.content)) !== null) {
|
|
317
|
+
const serverName = match[1].replace(/-/g, '_');
|
|
318
|
+
if (!seenServers.has(serverName)) {
|
|
319
|
+
seenServers.add(serverName);
|
|
320
|
+
// Don't add as full config, just note it exists
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Deduplicate
|
|
326
|
+
const seen = new Set();
|
|
327
|
+
return servers.filter((s) => {
|
|
328
|
+
if (seen.has(s.name))
|
|
329
|
+
return false;
|
|
330
|
+
seen.add(s.name);
|
|
331
|
+
return true;
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Extract settings from content and sections
|
|
336
|
+
*/
|
|
337
|
+
function extractSettings(content, sections) {
|
|
338
|
+
const settings = {};
|
|
339
|
+
// Look for tech stack
|
|
340
|
+
const techMatch = content.match(/(?:Tech\s*Stack|Technology|Stack)[:\s]+([^\n]+)/i);
|
|
341
|
+
if (techMatch && techMatch[1]) {
|
|
342
|
+
settings.techStack = techMatch[1].replace(/\*\*/g, '').trim();
|
|
343
|
+
}
|
|
344
|
+
// Look for build command
|
|
345
|
+
const buildMatch = content.match(/(?:Build|Compile)[:\s]*\n?```(?:bash|sh)?\n([^\n]+)/i);
|
|
346
|
+
if (buildMatch && buildMatch[1]) {
|
|
347
|
+
settings.buildCommand = buildMatch[1].trim();
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
// Check for npm run build pattern
|
|
351
|
+
if (content.includes('npm run build')) {
|
|
352
|
+
settings.buildCommand = 'npm run build';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Look for test command
|
|
356
|
+
const testMatch = content.match(/(?:Test)[:\s]*\n?```(?:bash|sh)?\n([^\n]+)/i);
|
|
357
|
+
if (testMatch && testMatch[1]) {
|
|
358
|
+
settings.testCommand = testMatch[1].trim();
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
if (content.includes('npm test')) {
|
|
362
|
+
settings.testCommand = 'npm test';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Look for dev command
|
|
366
|
+
if (content.includes('npm run dev')) {
|
|
367
|
+
settings.devCommand = 'npm run dev';
|
|
368
|
+
}
|
|
369
|
+
// Look for approval/permission settings
|
|
370
|
+
if (content.includes('auto-approve') || content.includes('autoApprove')) {
|
|
371
|
+
settings.approvalPolicy = 'never';
|
|
372
|
+
}
|
|
373
|
+
else if (content.includes('read-only')) {
|
|
374
|
+
settings.sandboxMode = 'read-only';
|
|
375
|
+
}
|
|
376
|
+
// Look for model specification
|
|
377
|
+
const modelMatch = content.match(/model[:\s]+["']?([^"'\n]+)["']?/i);
|
|
378
|
+
if (modelMatch && modelMatch[1]) {
|
|
379
|
+
settings.model = modelMatch[1].trim();
|
|
380
|
+
}
|
|
381
|
+
return settings;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Extract behavioral rules from content
|
|
385
|
+
*/
|
|
386
|
+
function extractBehavioralRules(content) {
|
|
387
|
+
const rules = [];
|
|
388
|
+
// Look for Behavioral Rules section
|
|
389
|
+
const behavioralMatch = content.match(/##\s*Behavioral\s*Rules[^\n]*\n([\s\S]*?)(?=\n##|\n#\s|$)/i);
|
|
390
|
+
if (behavioralMatch && behavioralMatch[1]) {
|
|
391
|
+
const ruleLines = behavioralMatch[1].split('\n');
|
|
392
|
+
for (const line of ruleLines) {
|
|
393
|
+
const trimmed = line.trim();
|
|
394
|
+
if (trimmed.startsWith('- ')) {
|
|
395
|
+
rules.push(trimmed.substring(2));
|
|
396
|
+
}
|
|
397
|
+
else if (trimmed.startsWith('* ')) {
|
|
398
|
+
rules.push(trimmed.substring(2));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Also look for Security Rules
|
|
403
|
+
const securityMatch = content.match(/##\s*Security\s*Rules?[^\n]*\n([\s\S]*?)(?=\n##|\n#\s|$)/i);
|
|
404
|
+
if (securityMatch && securityMatch[1]) {
|
|
405
|
+
const securityLines = securityMatch[1].split('\n');
|
|
406
|
+
for (const line of securityLines) {
|
|
407
|
+
const trimmed = line.trim();
|
|
408
|
+
if (trimmed.startsWith('- ')) {
|
|
409
|
+
rules.push(trimmed.substring(2));
|
|
410
|
+
}
|
|
411
|
+
else if (trimmed.startsWith('* ')) {
|
|
412
|
+
rules.push(trimmed.substring(2));
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return rules;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Find line number for a character index
|
|
420
|
+
*/
|
|
421
|
+
function findLineNumber(content, index) {
|
|
422
|
+
return content.substring(0, index).split('\n').length;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get context around a match
|
|
426
|
+
*/
|
|
427
|
+
function getContextAroundMatch(lines, lineNum) {
|
|
428
|
+
const start = Math.max(0, lineNum - 2);
|
|
429
|
+
const end = Math.min(lines.length, lineNum + 1);
|
|
430
|
+
return lines.slice(start, end).join('\n');
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Analyze a CLAUDE.md file for migration (simplified interface)
|
|
434
|
+
*/
|
|
435
|
+
export async function analyzeClaudeMd(content) {
|
|
436
|
+
const parsed = await parseClaudeMd(content);
|
|
437
|
+
return {
|
|
438
|
+
sections: parsed.sections.map((s) => s.title),
|
|
439
|
+
skills: [...new Set(parsed.skills.map((s) => s.name))],
|
|
440
|
+
hooks: parsed.hooks,
|
|
441
|
+
customInstructions: parsed.customInstructions,
|
|
442
|
+
warnings: parsed.warnings,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Convert skill invocation syntax from slash to dollar
|
|
447
|
+
*/
|
|
448
|
+
export function convertSkillSyntax(content) {
|
|
449
|
+
// Convert /skill-name to $skill-name, but avoid path-like patterns
|
|
450
|
+
return content.replace(/(?<![a-zA-Z0-9_./])\/([a-z][a-z0-9-]*)(?![a-zA-Z0-9_./])/g, (match, skillName) => {
|
|
451
|
+
// Skip common directory names
|
|
452
|
+
const skipPatterns = ['src', 'dist', 'docs', 'tests', 'config', 'scripts', 'examples', 'node_modules', 'workspaces', 'data', 'logs', 'tmp', 'var', 'etc', 'usr', 'bin', 'lib'];
|
|
453
|
+
if (skipPatterns.includes(skillName)) {
|
|
454
|
+
return match;
|
|
455
|
+
}
|
|
456
|
+
return `$${skillName}`;
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Generate AGENTS.md content from parsed CLAUDE.md
|
|
461
|
+
*/
|
|
462
|
+
export function generateAgentsMdFromParsed(parsed) {
|
|
463
|
+
const lines = [];
|
|
464
|
+
// Title
|
|
465
|
+
lines.push(`# ${parsed.title || 'Project Agent Guide'}`);
|
|
466
|
+
lines.push('');
|
|
467
|
+
lines.push('> Migrated from CLAUDE.md by @claude-flow/codex');
|
|
468
|
+
lines.push('');
|
|
469
|
+
// Project Overview
|
|
470
|
+
lines.push('## Project Overview');
|
|
471
|
+
lines.push('');
|
|
472
|
+
if (parsed.settings.techStack) {
|
|
473
|
+
lines.push(`**Tech Stack**: ${parsed.settings.techStack}`);
|
|
474
|
+
}
|
|
475
|
+
lines.push('');
|
|
476
|
+
// Quick Start
|
|
477
|
+
lines.push('## Setup');
|
|
478
|
+
lines.push('');
|
|
479
|
+
if (parsed.settings.buildCommand) {
|
|
480
|
+
lines.push('### Build');
|
|
481
|
+
lines.push('```bash');
|
|
482
|
+
lines.push(parsed.settings.buildCommand);
|
|
483
|
+
lines.push('```');
|
|
484
|
+
lines.push('');
|
|
485
|
+
}
|
|
486
|
+
if (parsed.settings.testCommand) {
|
|
487
|
+
lines.push('### Test');
|
|
488
|
+
lines.push('```bash');
|
|
489
|
+
lines.push(parsed.settings.testCommand);
|
|
490
|
+
lines.push('```');
|
|
491
|
+
lines.push('');
|
|
492
|
+
}
|
|
493
|
+
if (parsed.settings.devCommand) {
|
|
494
|
+
lines.push('### Development');
|
|
495
|
+
lines.push('```bash');
|
|
496
|
+
lines.push(parsed.settings.devCommand);
|
|
497
|
+
lines.push('```');
|
|
498
|
+
lines.push('');
|
|
499
|
+
}
|
|
500
|
+
// Code Standards
|
|
501
|
+
lines.push('## Code Standards');
|
|
502
|
+
lines.push('');
|
|
503
|
+
lines.push('- Files under 500 lines');
|
|
504
|
+
lines.push('- No hardcoded secrets');
|
|
505
|
+
lines.push('- Input validation at boundaries');
|
|
506
|
+
lines.push('- Typed interfaces for public APIs');
|
|
507
|
+
lines.push('');
|
|
508
|
+
// Skills
|
|
509
|
+
if (parsed.skills.length > 0) {
|
|
510
|
+
lines.push('## Skills');
|
|
511
|
+
lines.push('');
|
|
512
|
+
lines.push('| Skill | Original Syntax |');
|
|
513
|
+
lines.push('|-------|-----------------|');
|
|
514
|
+
for (const skill of parsed.skills) {
|
|
515
|
+
const codexSyntax = `$${skill.name}`;
|
|
516
|
+
const originalSyntax = skill.syntax === 'slash' ? `/${skill.name}` : `$${skill.name}`;
|
|
517
|
+
lines.push(`| \`${codexSyntax}\` | \`${originalSyntax}\` |`);
|
|
518
|
+
}
|
|
519
|
+
lines.push('');
|
|
520
|
+
}
|
|
521
|
+
// Security
|
|
522
|
+
lines.push('## Security');
|
|
523
|
+
lines.push('');
|
|
524
|
+
if (parsed.customInstructions.length > 0) {
|
|
525
|
+
for (const rule of parsed.customInstructions.slice(0, 10)) {
|
|
526
|
+
lines.push(`- ${rule}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
lines.push('- NEVER commit secrets or credentials');
|
|
531
|
+
lines.push('- Validate all user inputs');
|
|
532
|
+
lines.push('- Prevent directory traversal attacks');
|
|
533
|
+
}
|
|
534
|
+
lines.push('');
|
|
535
|
+
// Hooks (as reference)
|
|
536
|
+
if (parsed.hooks.length > 0) {
|
|
537
|
+
lines.push('## Automation Hooks');
|
|
538
|
+
lines.push('');
|
|
539
|
+
lines.push('The following hooks were detected in the original configuration:');
|
|
540
|
+
lines.push('');
|
|
541
|
+
for (const hook of parsed.hooks) {
|
|
542
|
+
lines.push(`- \`${hook}\``);
|
|
543
|
+
}
|
|
544
|
+
lines.push('');
|
|
545
|
+
lines.push('> Note: Codex uses scheduled Automations instead of event-driven hooks.');
|
|
546
|
+
lines.push('');
|
|
547
|
+
}
|
|
548
|
+
// MCP Servers
|
|
549
|
+
if (parsed.mcpServers.length > 0) {
|
|
550
|
+
lines.push('## MCP Servers');
|
|
551
|
+
lines.push('');
|
|
552
|
+
lines.push('Configure in config.toml:');
|
|
553
|
+
lines.push('');
|
|
554
|
+
for (const server of parsed.mcpServers) {
|
|
555
|
+
lines.push(`- **${server.name}**: \`${server.command} ${(server.args || []).join(' ')}\``);
|
|
556
|
+
}
|
|
557
|
+
lines.push('');
|
|
558
|
+
}
|
|
559
|
+
// Migration notes
|
|
560
|
+
if (parsed.warnings.length > 0) {
|
|
561
|
+
lines.push('## Migration Notes');
|
|
562
|
+
lines.push('');
|
|
563
|
+
for (const warning of parsed.warnings) {
|
|
564
|
+
lines.push(`- ${warning}`);
|
|
565
|
+
}
|
|
566
|
+
lines.push('');
|
|
567
|
+
}
|
|
568
|
+
return lines.join('\n');
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Convert settings.json to config.toml format
|
|
572
|
+
*/
|
|
573
|
+
export function convertSettingsToToml(settings) {
|
|
574
|
+
const lines = [];
|
|
575
|
+
lines.push('# Migrated from settings.json');
|
|
576
|
+
lines.push('# Generated by @claude-flow/codex');
|
|
577
|
+
lines.push('');
|
|
578
|
+
// Model
|
|
579
|
+
if (settings.model) {
|
|
580
|
+
lines.push(`model = "${settings.model}"`);
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
lines.push('model = "gpt-5.3-codex"');
|
|
584
|
+
}
|
|
585
|
+
lines.push('');
|
|
586
|
+
// Permissions mapping
|
|
587
|
+
if (settings.permissions) {
|
|
588
|
+
const perms = settings.permissions;
|
|
589
|
+
if (perms.autoApprove === true) {
|
|
590
|
+
lines.push('approval_policy = "never"');
|
|
591
|
+
lines.push('sandbox_mode = "danger-full-access"');
|
|
592
|
+
}
|
|
593
|
+
else if (perms.autoApprove === 'read-only') {
|
|
594
|
+
lines.push('approval_policy = "on-request"');
|
|
595
|
+
lines.push('sandbox_mode = "read-only"');
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
lines.push('approval_policy = "on-request"');
|
|
599
|
+
lines.push('sandbox_mode = "workspace-write"');
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
lines.push('approval_policy = "on-request"');
|
|
604
|
+
lines.push('sandbox_mode = "workspace-write"');
|
|
605
|
+
}
|
|
606
|
+
lines.push('');
|
|
607
|
+
// Web search
|
|
608
|
+
if (settings.webSearch !== undefined) {
|
|
609
|
+
const mode = settings.webSearch === true ? 'live' : settings.webSearch === false ? 'disabled' : 'cached';
|
|
610
|
+
lines.push(`web_search = "${mode}"`);
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
lines.push('web_search = "cached"');
|
|
614
|
+
}
|
|
615
|
+
lines.push('');
|
|
616
|
+
// Features
|
|
617
|
+
lines.push('[features]');
|
|
618
|
+
lines.push('child_agents_md = true');
|
|
619
|
+
lines.push('shell_snapshot = true');
|
|
620
|
+
lines.push('request_rule = true');
|
|
621
|
+
lines.push('');
|
|
622
|
+
// MCP servers
|
|
623
|
+
if (settings.mcpServers && typeof settings.mcpServers === 'object') {
|
|
624
|
+
for (const [name, config] of Object.entries(settings.mcpServers)) {
|
|
625
|
+
const mcpConfig = config;
|
|
626
|
+
lines.push(`[mcp_servers.${name}]`);
|
|
627
|
+
if (mcpConfig.command) {
|
|
628
|
+
lines.push(`command = "${mcpConfig.command}"`);
|
|
629
|
+
}
|
|
630
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
631
|
+
const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(', ');
|
|
632
|
+
lines.push(`args = [${argsStr}]`);
|
|
633
|
+
}
|
|
634
|
+
lines.push('enabled = true');
|
|
635
|
+
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
636
|
+
lines.push('');
|
|
637
|
+
lines.push(`[mcp_servers.${name}.env]`);
|
|
638
|
+
for (const [key, value] of Object.entries(mcpConfig.env)) {
|
|
639
|
+
lines.push(`${key} = "${value}"`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
lines.push('');
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
// Add default claude-flow server
|
|
647
|
+
lines.push('[mcp_servers.claude-flow]');
|
|
648
|
+
lines.push('command = "npx"');
|
|
649
|
+
lines.push('args = ["-y", "@claude-flow/cli@latest"]');
|
|
650
|
+
lines.push('enabled = true');
|
|
651
|
+
lines.push('');
|
|
652
|
+
}
|
|
653
|
+
// History
|
|
654
|
+
lines.push('[history]');
|
|
655
|
+
lines.push('persistence = "save-all"');
|
|
656
|
+
lines.push('');
|
|
657
|
+
// Profiles
|
|
658
|
+
lines.push('# Development profile');
|
|
659
|
+
lines.push('[profiles.dev]');
|
|
660
|
+
lines.push('approval_policy = "never"');
|
|
661
|
+
lines.push('sandbox_mode = "danger-full-access"');
|
|
662
|
+
lines.push('');
|
|
663
|
+
lines.push('# Safe profile');
|
|
664
|
+
lines.push('[profiles.safe]');
|
|
665
|
+
lines.push('approval_policy = "untrusted"');
|
|
666
|
+
lines.push('sandbox_mode = "read-only"');
|
|
667
|
+
lines.push('');
|
|
668
|
+
return lines.join('\n');
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Generate config.toml from parsed CLAUDE.md
|
|
672
|
+
*/
|
|
673
|
+
export function generateConfigTomlFromParsed(parsed) {
|
|
674
|
+
const lines = [];
|
|
675
|
+
lines.push('# Migrated from CLAUDE.md');
|
|
676
|
+
lines.push('# Generated by @claude-flow/codex');
|
|
677
|
+
lines.push('');
|
|
678
|
+
// Model
|
|
679
|
+
lines.push(`model = "${parsed.settings.model || 'gpt-5.3-codex'}"`);
|
|
680
|
+
lines.push('');
|
|
681
|
+
// Approval policy
|
|
682
|
+
const approvalPolicy = parsed.settings.approvalPolicy || 'on-request';
|
|
683
|
+
lines.push(`approval_policy = "${approvalPolicy}"`);
|
|
684
|
+
lines.push('');
|
|
685
|
+
// Sandbox mode
|
|
686
|
+
const sandboxMode = parsed.settings.sandboxMode || 'workspace-write';
|
|
687
|
+
lines.push(`sandbox_mode = "${sandboxMode}"`);
|
|
688
|
+
lines.push('');
|
|
689
|
+
// Web search
|
|
690
|
+
lines.push('web_search = "cached"');
|
|
691
|
+
lines.push('');
|
|
692
|
+
// Features
|
|
693
|
+
lines.push('[features]');
|
|
694
|
+
lines.push('child_agents_md = true');
|
|
695
|
+
lines.push('shell_snapshot = true');
|
|
696
|
+
lines.push('request_rule = true');
|
|
697
|
+
lines.push('');
|
|
698
|
+
// MCP servers
|
|
699
|
+
if (parsed.mcpServers.length > 0) {
|
|
700
|
+
for (const server of parsed.mcpServers) {
|
|
701
|
+
lines.push(`[mcp_servers.${server.name.replace(/-/g, '_')}]`);
|
|
702
|
+
lines.push(`command = "${server.command}"`);
|
|
703
|
+
if (server.args && server.args.length > 0) {
|
|
704
|
+
const argsStr = server.args.map((a) => `"${a}"`).join(', ');
|
|
705
|
+
lines.push(`args = [${argsStr}]`);
|
|
706
|
+
}
|
|
707
|
+
lines.push(`enabled = ${server.enabled ?? true}`);
|
|
708
|
+
lines.push('');
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
// Default claude-flow server
|
|
713
|
+
lines.push('[mcp_servers.claude_flow]');
|
|
714
|
+
lines.push('command = "npx"');
|
|
715
|
+
lines.push('args = ["-y", "@claude-flow/cli@latest"]');
|
|
716
|
+
lines.push('enabled = true');
|
|
717
|
+
lines.push('');
|
|
718
|
+
}
|
|
719
|
+
// Skills
|
|
720
|
+
if (parsed.skills.length > 0) {
|
|
721
|
+
lines.push('# Skills detected in original configuration');
|
|
722
|
+
for (const skill of parsed.skills) {
|
|
723
|
+
lines.push(`# - ${skill.name} (${skill.syntax} syntax)`);
|
|
724
|
+
}
|
|
725
|
+
lines.push('');
|
|
726
|
+
}
|
|
727
|
+
// History
|
|
728
|
+
lines.push('[history]');
|
|
729
|
+
lines.push('persistence = "save-all"');
|
|
730
|
+
lines.push('');
|
|
731
|
+
// Profiles
|
|
732
|
+
lines.push('[profiles.dev]');
|
|
733
|
+
lines.push('approval_policy = "never"');
|
|
734
|
+
lines.push('sandbox_mode = "danger-full-access"');
|
|
735
|
+
lines.push('');
|
|
736
|
+
lines.push('[profiles.safe]');
|
|
737
|
+
lines.push('approval_policy = "untrusted"');
|
|
738
|
+
lines.push('sandbox_mode = "read-only"');
|
|
739
|
+
lines.push('');
|
|
740
|
+
return lines.join('\n');
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Migrate from Claude Code (CLAUDE.md) to Codex (AGENTS.md)
|
|
744
|
+
*/
|
|
745
|
+
export async function migrateFromClaudeCode(options) {
|
|
746
|
+
const { sourcePath, targetPath, preserveComments = true, generateSkills = true } = options;
|
|
747
|
+
try {
|
|
748
|
+
// In actual implementation, this would read the file
|
|
749
|
+
// For now, we provide the structure for the migration
|
|
750
|
+
const result = {
|
|
751
|
+
success: true,
|
|
752
|
+
agentsMdPath: `${targetPath}/AGENTS.md`,
|
|
753
|
+
skillsCreated: generateSkills
|
|
754
|
+
? ['swarm-orchestration', 'memory-management', 'security-audit']
|
|
755
|
+
: [],
|
|
756
|
+
configTomlPath: `${targetPath}/.agents/config.toml`,
|
|
757
|
+
mappings: FEATURE_MAPPINGS,
|
|
758
|
+
warnings: [
|
|
759
|
+
'Review skill invocation syntax (changed from / to $)',
|
|
760
|
+
'Check hook configurations for Automation compatibility',
|
|
761
|
+
'Verify MCP server configurations in config.toml',
|
|
762
|
+
],
|
|
763
|
+
};
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
return {
|
|
768
|
+
success: false,
|
|
769
|
+
warnings: [`Migration failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Perform full migration with content
|
|
775
|
+
*/
|
|
776
|
+
export async function performFullMigration(claudeMdContent, settingsJson) {
|
|
777
|
+
// Parse CLAUDE.md
|
|
778
|
+
const parsed = await parseClaudeMd(claudeMdContent);
|
|
779
|
+
// Generate AGENTS.md
|
|
780
|
+
let agentsMd = generateAgentsMdFromParsed(parsed);
|
|
781
|
+
// Convert skill syntax in the generated content
|
|
782
|
+
agentsMd = convertSkillSyntax(agentsMd);
|
|
783
|
+
// Generate config.toml
|
|
784
|
+
let configToml;
|
|
785
|
+
if (settingsJson) {
|
|
786
|
+
configToml = convertSettingsToToml(settingsJson);
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
configToml = generateConfigTomlFromParsed(parsed);
|
|
790
|
+
}
|
|
791
|
+
// Collect skills to create
|
|
792
|
+
const skillsToCreate = [...new Set(parsed.skills.map((s) => s.name))];
|
|
793
|
+
return {
|
|
794
|
+
agentsMd,
|
|
795
|
+
configToml,
|
|
796
|
+
warnings: parsed.warnings,
|
|
797
|
+
skillsToCreate,
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Generate migration report
|
|
802
|
+
*/
|
|
803
|
+
export function generateMigrationReport(result) {
|
|
804
|
+
const lines = [];
|
|
805
|
+
lines.push('# Migration Report');
|
|
806
|
+
lines.push('');
|
|
807
|
+
lines.push(`**Status**: ${result.success ? 'Success' : 'Failed'}`);
|
|
808
|
+
lines.push(`**Generated**: ${new Date().toISOString()}`);
|
|
809
|
+
lines.push('');
|
|
810
|
+
if (result.agentsMdPath) {
|
|
811
|
+
lines.push('## Generated Files');
|
|
812
|
+
lines.push('');
|
|
813
|
+
lines.push(`- AGENTS.md: \`${result.agentsMdPath}\``);
|
|
814
|
+
if (result.configTomlPath) {
|
|
815
|
+
lines.push(`- config.toml: \`${result.configTomlPath}\``);
|
|
816
|
+
}
|
|
817
|
+
lines.push('');
|
|
818
|
+
}
|
|
819
|
+
if (result.skillsCreated && result.skillsCreated.length > 0) {
|
|
820
|
+
lines.push('## Skills Created');
|
|
821
|
+
lines.push('');
|
|
822
|
+
for (const skill of result.skillsCreated) {
|
|
823
|
+
lines.push(`- \`$${skill}\``);
|
|
824
|
+
}
|
|
825
|
+
lines.push('');
|
|
826
|
+
}
|
|
827
|
+
if (result.mappings) {
|
|
828
|
+
lines.push('## Feature Mappings');
|
|
829
|
+
lines.push('');
|
|
830
|
+
lines.push('| Claude Code | Codex | Status | Notes |');
|
|
831
|
+
lines.push('|-------------|-------|--------|-------|');
|
|
832
|
+
for (const mapping of result.mappings) {
|
|
833
|
+
const notes = mapping.notes || '';
|
|
834
|
+
lines.push(`| \`${mapping.claudeCode}\` | \`${mapping.codex}\` | ${mapping.status} | ${notes} |`);
|
|
835
|
+
}
|
|
836
|
+
lines.push('');
|
|
837
|
+
}
|
|
838
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
839
|
+
lines.push('## Warnings');
|
|
840
|
+
lines.push('');
|
|
841
|
+
for (const warning of result.warnings) {
|
|
842
|
+
lines.push(`- ${warning}`);
|
|
843
|
+
}
|
|
844
|
+
lines.push('');
|
|
845
|
+
}
|
|
846
|
+
lines.push('## Next Steps');
|
|
847
|
+
lines.push('');
|
|
848
|
+
lines.push('1. Review generated AGENTS.md for accuracy');
|
|
849
|
+
lines.push('2. Update skill references from `/skill-name` to `$skill-name`');
|
|
850
|
+
lines.push('3. Configure MCP servers in config.toml');
|
|
851
|
+
lines.push('4. Create skill definitions in `.agents/skills/`');
|
|
852
|
+
lines.push('5. Test with `codex` CLI');
|
|
853
|
+
lines.push('');
|
|
854
|
+
return lines.join('\n');
|
|
855
|
+
}
|
|
856
|
+
//# sourceMappingURL=index.js.map
|