wogiflow 1.0.47 → 1.0.49

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 (67) hide show
  1. package/.claude/commands/wogi-research.md +223 -0
  2. package/.claude/commands/wogi-review-fix.md +241 -0
  3. package/.claude/docs/commands.md +17 -0
  4. package/.workflow/bridges/base-bridge.js +124 -15
  5. package/.workflow/bridges/claude-bridge.js +50 -22
  6. package/.workflow/bridges/codex-bridge.js +466 -0
  7. package/.workflow/bridges/cursor-bridge.js +573 -0
  8. package/.workflow/bridges/gemini-bridge.js +614 -0
  9. package/.workflow/bridges/index.js +41 -4
  10. package/.workflow/bridges/kimi-bridge.js +454 -0
  11. package/.workflow/bridges/opencode-bridge.js +825 -0
  12. package/.workflow/templates/agents-md.hbs +127 -0
  13. package/.workflow/templates/claude-md.hbs +66 -0
  14. package/.workflow/templates/codex-config.hbs +69 -0
  15. package/.workflow/templates/cursor-rules.mdc.hbs +142 -0
  16. package/.workflow/templates/gemini-md.hbs +334 -26
  17. package/.workflow/templates/opencode-agents-md.hbs +158 -0
  18. package/.workflow/templates/opencode-config.hbs +27 -0
  19. package/.workflow/templates/partials/auto-features.hbs +125 -0
  20. package/.workflow/templates/partials/enforcement-rules.hbs +164 -0
  21. package/.workflow/templates/partials/user-commands.hbs +154 -0
  22. package/.workflow/templates/research-report.md +153 -0
  23. package/README.md +170 -1589
  24. package/package.json +1 -1
  25. package/scripts/flow-import-profile +17 -8
  26. package/scripts/flow-operational-scanner.js +13 -8
  27. package/scripts/flow-parity-check.js +281 -0
  28. package/scripts/flow-prompt-composer.js +10 -0
  29. package/scripts/flow-research-protocol.js +1022 -0
  30. package/scripts/flow-strict-adherence.js +14 -9
  31. package/scripts/flow-utils.js +4 -0
  32. package/scripts/hooks/adapters/base-adapter.js +22 -2
  33. package/scripts/hooks/adapters/claude-code.js +14 -1
  34. package/scripts/hooks/adapters/cursor.js +421 -0
  35. package/scripts/hooks/adapters/gemini.js +368 -0
  36. package/scripts/hooks/adapters/index.js +99 -0
  37. package/scripts/hooks/adapters/opencode.js +317 -0
  38. package/scripts/hooks/core/constants.js +75 -0
  39. package/scripts/hooks/core/implementation-gate.js +15 -1
  40. package/scripts/hooks/core/index.js +21 -1
  41. package/scripts/hooks/core/research-gate.js +306 -0
  42. package/scripts/hooks/entry/claude-code/pre-tool-use.js +1 -1
  43. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +47 -2
  44. package/scripts/hooks/entry/cursor/after-file-edit.js +127 -0
  45. package/scripts/hooks/entry/cursor/before-shell.js +131 -0
  46. package/scripts/hooks/entry/cursor/before-submit-prompt.js +102 -0
  47. package/scripts/hooks/entry/cursor/session-start.js +125 -0
  48. package/scripts/hooks/entry/cursor/stop.js +135 -0
  49. package/scripts/hooks/entry/gemini-cli/after-tool.js +119 -0
  50. package/scripts/hooks/entry/gemini-cli/before-agent.js +140 -0
  51. package/scripts/hooks/entry/gemini-cli/before-tool.js +189 -0
  52. package/scripts/hooks/entry/gemini-cli/session-end.js +120 -0
  53. package/scripts/hooks/entry/gemini-cli/session-start.js +80 -0
  54. package/scripts/hooks/entry/opencode/prompt-append.js +76 -0
  55. package/scripts/hooks/entry/opencode/session-start.js +86 -0
  56. package/scripts/hooks/entry/opencode/tool-after.js +77 -0
  57. package/scripts/hooks/entry/opencode/tool-before.js +143 -0
  58. package/.claude/rules/README.md +0 -60
  59. package/.claude/rules/architecture/component-reuse.md +0 -38
  60. package/.claude/rules/architecture/document-structure.md +0 -76
  61. package/.claude/rules/architecture/feature-refactoring-cleanup.md +0 -87
  62. package/.claude/rules/architecture/model-management.md +0 -35
  63. package/.claude/rules/code-style/naming-conventions.md +0 -55
  64. package/.claude/rules/security/security-patterns.md +0 -143
  65. package/.workflow/specs/architecture.md.template +0 -24
  66. package/.workflow/specs/stack.md.template +0 -33
  67. package/.workflow/specs/testing.md.template +0 -36
@@ -59,12 +59,21 @@ class ClaudeBridge extends BaseBridge {
59
59
 
60
60
  /**
61
61
  * Generate CLAUDE.md from Handlebars-like template
62
- * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}
62
+ * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}, {{> partial}}
63
63
  */
64
64
  generateFromTemplate(templatePath, config) {
65
- const template = fs.readFileSync(templatePath, 'utf-8');
65
+ let template;
66
+ try {
67
+ template = fs.readFileSync(templatePath, 'utf-8');
68
+ } catch (err) {
69
+ this.log(`Warning: Could not read template ${templatePath}: ${err.message}`);
70
+ return this.generateDefaultClaudeMd(config);
71
+ }
66
72
  let content = template;
67
73
 
74
+ // Process {{> partial}} includes first (before other processing)
75
+ content = this.processPartials(content);
76
+
68
77
  // Process {{#if config.path.to.value}}...{{/if}} blocks
69
78
  // Non-greedy match to handle nested conditions
70
79
  content = this.processConditionals(content, config);
@@ -340,24 +349,47 @@ Last synced: ${new Date().toISOString()}
340
349
  }
341
350
 
342
351
  /**
343
- * Generate settings.local.json with wildcard permissions
344
- * Claude Code 2.1.0+ supports wildcards like Bash(npm *)
352
+ * Generate settings.local.json with permissions
353
+ * NOTE: Requires Claude Code 2.1.7+ which fixed wildcard matching of shell operators.
354
+ * See security-patterns.md rule #6 for details.
345
355
  */
346
356
  generateSettings(config) {
347
357
  const projectDir = this.projectDir;
348
358
 
349
- // Base wildcard permissions - covers most common use cases
359
+ // Permission rules - balancing security with workflow convenience
360
+ // Wildcards are safe in 2.1.7+ (shell operators rejected)
350
361
  const wildcardPermissions = [
351
- // Package managers
352
- 'Bash(npm *)',
362
+ // Package managers - specific safe operations
363
+ 'Bash(npm install *)',
364
+ 'Bash(npm run *)',
365
+ 'Bash(npm test *)',
366
+ 'Bash(npm exec *)',
367
+ 'Bash(npm ci)',
368
+ 'Bash(npm audit *)',
369
+ 'Bash(npm outdated *)',
370
+ 'Bash(npm ls *)',
371
+ 'Bash(npm version *)',
353
372
  'Bash(npx *)',
354
- 'Bash(yarn *)',
355
- 'Bash(pnpm *)',
356
- 'Bash(pip *)',
357
- 'Bash(python *)',
358
- 'Bash(python3 *)',
359
-
360
- // Git operations
373
+ 'Bash(yarn install *)',
374
+ 'Bash(yarn add *)',
375
+ 'Bash(yarn remove *)',
376
+ 'Bash(yarn run *)',
377
+ 'Bash(yarn test *)',
378
+ 'Bash(yarn build *)',
379
+ 'Bash(yarn dev *)',
380
+ 'Bash(pnpm install *)',
381
+ 'Bash(pnpm add *)',
382
+ 'Bash(pnpm remove *)',
383
+ 'Bash(pnpm run *)',
384
+ 'Bash(pnpm test *)',
385
+ 'Bash(pnpm build *)',
386
+ 'Bash(pnpm dev *)',
387
+ 'Bash(pip install *)',
388
+ 'Bash(pip list *)',
389
+ 'Bash(python -m *)',
390
+ 'Bash(python3 -m *)',
391
+
392
+ // Git operations - all safe read/write operations
361
393
  'Bash(git status)',
362
394
  'Bash(git status *)',
363
395
  'Bash(git diff *)',
@@ -386,18 +418,14 @@ Last synced: ${new Date().toISOString()}
386
418
  'Bash(./scripts/flow *)',
387
419
  'Bash(./scripts/flow)',
388
420
 
389
- // Common utilities
421
+ // Safe read-only utilities
390
422
  'Bash(ls *)',
391
423
  'Bash(tree *)',
392
- 'Bash(cat *)',
393
- 'Bash(head *)',
394
- 'Bash(tail *)',
395
424
  'Bash(wc *)',
396
- 'Bash(grep *)',
397
- 'Bash(find *)',
398
425
  'Bash(chmod +x *)', // Only make executable, not arbitrary permissions
399
- 'Bash(node *)',
400
- 'Bash(bash *)',
426
+ 'Bash(node --check *)',
427
+ 'Bash(node --version)',
428
+ 'Bash(bash -n *)', // Syntax check only
401
429
  'Bash(open *)',
402
430
  'Bash(test *)',
403
431
 
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Codex CLI Bridge
5
+ *
6
+ * Generates AGENTS.md and .codex/config.toml from WogiFlow configuration.
7
+ * Provides soft parity with Claude Code/Gemini CLI - same rules, same memory,
8
+ * but enforcement is advisory (Codex lacks pre-operation hooks).
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const BaseBridge = require('./base-bridge');
14
+
15
+ // Try to load Handlebars, fall back to inline templates if not available
16
+ let Handlebars;
17
+ try {
18
+ Handlebars = require('handlebars');
19
+ } catch {
20
+ Handlebars = null;
21
+ }
22
+
23
+ // ============================================================
24
+ // Codex Bridge Class
25
+ // ============================================================
26
+
27
+ class CodexBridge extends BaseBridge {
28
+ constructor(options = {}) {
29
+ super('codex', options);
30
+ }
31
+
32
+ // ==================== Abstract Method Implementations ====================
33
+
34
+ getCliFolder() {
35
+ return '.codex';
36
+ }
37
+
38
+ getRulesFileName() {
39
+ return 'AGENTS.md';
40
+ }
41
+
42
+ getSkillsPath() {
43
+ return path.join('.codex', 'skills');
44
+ }
45
+
46
+ getRulesPath() {
47
+ return path.join('.codex', 'rules');
48
+ }
49
+
50
+ /**
51
+ * Register template partials with Handlebars
52
+ */
53
+ registerPartials() {
54
+ if (!Handlebars) return;
55
+
56
+ const partialsDir = path.join(this.projectDir, this.workflowDir, 'templates', 'partials');
57
+ if (!fs.existsSync(partialsDir)) return;
58
+
59
+ try {
60
+ const partialFiles = fs.readdirSync(partialsDir).filter(f => f.endsWith('.hbs'));
61
+ for (const file of partialFiles) {
62
+ const partialName = path.basename(file, '.hbs');
63
+ const partialContent = fs.readFileSync(path.join(partialsDir, file), 'utf-8');
64
+ Handlebars.registerPartial(partialName, partialContent);
65
+ this.log(`Registered partial: ${partialName}`);
66
+ }
67
+ } catch (err) {
68
+ this.log(`Warning: Could not register partials: ${err.message}`);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Generate AGENTS.md content
74
+ */
75
+ generateRulesContent(config) {
76
+ const context = this.buildContext(config);
77
+
78
+ // Try to use Handlebars template with proper error handling
79
+ if (Handlebars) {
80
+ // Register partials before compiling
81
+ this.registerPartials();
82
+
83
+ try {
84
+ const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'agents-md.hbs');
85
+ if (fs.existsSync(templatePath)) {
86
+ const templateSource = fs.readFileSync(templatePath, 'utf-8');
87
+ const template = Handlebars.compile(templateSource);
88
+ return template(context);
89
+ }
90
+ } catch (err) {
91
+ // Template failed - fall through to inline generation
92
+ this.log(`Template generation failed, using fallback: ${err.message}`);
93
+ }
94
+ }
95
+
96
+ // Fallback to inline generation (includes partial content directly)
97
+ return this.generateAgentsMdFallback(context);
98
+ }
99
+
100
+ /**
101
+ * Codex-specific setup: generate config.toml
102
+ */
103
+ async setupCliSpecific(config) {
104
+ // Generate config.toml
105
+ const configTomlPath = path.join(this.projectDir, this.getCliFolder(), 'config.toml');
106
+ const configTomlContent = this.generateConfigToml(config);
107
+ fs.writeFileSync(configTomlPath, configTomlContent);
108
+ this.log('Generated .codex/config.toml');
109
+
110
+ // Convert Claude skills to Codex format
111
+ this.convertAndSyncSkills();
112
+ }
113
+
114
+ // ==================== Codex-Specific Methods ====================
115
+
116
+ /**
117
+ * Build template context
118
+ */
119
+ buildContext(config) {
120
+ // Load decisions.md for coding rules
121
+ const decisionsPath = path.join(this.projectDir, this.workflowDir, 'state', 'decisions.md');
122
+ let decisions = '';
123
+ try {
124
+ decisions = fs.readFileSync(decisionsPath, 'utf-8');
125
+ } catch (err) {
126
+ // File may not exist or be unreadable - continue with empty decisions
127
+ if (err.code !== 'ENOENT') {
128
+ this.log(`Failed to read decisions.md: ${err.message}`);
129
+ }
130
+ }
131
+
132
+ // Get installed skills
133
+ const skills = config.skills?.installed || [];
134
+
135
+ return {
136
+ projectName: path.basename(this.projectDir),
137
+ projectRoot: this.projectDir,
138
+ timestamp: new Date().toISOString(),
139
+ config,
140
+ decisions,
141
+ skills,
142
+ enforcement: config.enforcement || {},
143
+ research: config.research || {},
144
+ qualityGates: config.qualityGates || {},
145
+ commits: config.commits || {}
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Fallback AGENTS.md generation (no Handlebars)
151
+ */
152
+ generateAgentsMdFallback(context) {
153
+ const lines = [];
154
+
155
+ lines.push('# WogiFlow Project Instructions');
156
+ lines.push('');
157
+ lines.push(`> Generated by WogiFlow Codex Bridge - ${context.timestamp}`);
158
+ lines.push('');
159
+ lines.push('## Core Principles');
160
+ lines.push('');
161
+ lines.push('1. **State files are memory** - Read `.workflow/state/` first');
162
+ lines.push('2. **Config drives behavior** - Follow `.workflow/config.json` rules');
163
+ lines.push('3. **Log every change** - Append to `request-log.md`');
164
+ lines.push('4. **Reuse components** - Check `app-map.md` before creating');
165
+ lines.push('5. **Learn from feedback** - Update instructions when corrected');
166
+ lines.push('');
167
+
168
+ // Task Gating
169
+ if (context.enforcement?.strictMode) {
170
+ lines.push('## Task Gating (MANDATORY)');
171
+ lines.push('');
172
+ lines.push('**STOP. Before ANY implementation:**');
173
+ lines.push('1. Check `.workflow/state/ready.json` for existing tasks');
174
+ lines.push('2. If no task exists, create one with `/wogi-story`');
175
+ lines.push('3. Start with `/wogi-start TASK-XXX`');
176
+ lines.push('');
177
+ }
178
+
179
+ // Research Protocol
180
+ if (context.research?.enabled) {
181
+ lines.push('## Research Protocol');
182
+ lines.push('');
183
+ lines.push('For capability/feasibility/existence questions:');
184
+ lines.push('1. Search local files thoroughly (Glob, Grep)');
185
+ lines.push('2. Web search for current documentation');
186
+ lines.push('3. List assumptions and verify each');
187
+ lines.push('4. Cite sources for all claims');
188
+ lines.push('5. State confidence level (HIGH/MEDIUM/LOW)');
189
+ lines.push('');
190
+ lines.push('**FORBIDDEN:** Claiming "X doesn\'t exist" without exhaustive search.');
191
+ lines.push('');
192
+ }
193
+
194
+ // Essential Commands
195
+ lines.push('## Essential Commands');
196
+ lines.push('');
197
+ lines.push('| Command | Purpose |');
198
+ lines.push('|---------|---------|');
199
+ lines.push('| `/wogi-ready` | Show available tasks |');
200
+ lines.push('| `/wogi-start TASK-X` | Start task with context |');
201
+ lines.push('| `/wogi-story "title"` | Create story with AC |');
202
+ lines.push('| `/wogi-status` | Project overview |');
203
+ lines.push('| `/wogi-research "q"` | Research before answering |');
204
+ lines.push('');
205
+
206
+ // Request Logging
207
+ lines.push('## Request Logging');
208
+ lines.push('');
209
+ lines.push('After EVERY request that changes files:');
210
+ lines.push('```markdown');
211
+ lines.push('### R-[XXX] | [YYYY-MM-DD HH:MM]');
212
+ lines.push('**Type**: new | fix | change | refactor');
213
+ lines.push('**Tags**: #screen:[name] #component:[name]');
214
+ lines.push('**Request**: "[what user asked]"');
215
+ lines.push('**Result**: [what was done]');
216
+ lines.push('**Files**: [files changed]');
217
+ lines.push('```');
218
+ lines.push('');
219
+
220
+ // Component Reuse
221
+ lines.push('## Component Reuse');
222
+ lines.push('');
223
+ lines.push('**Before creating ANY component:**');
224
+ lines.push('1. Check `app-map.md`');
225
+ lines.push('2. Search codebase for existing');
226
+ lines.push('3. Priority: Use existing → Add variant → Extend → Create new');
227
+ lines.push('');
228
+
229
+ // File Locations
230
+ lines.push('## File Locations');
231
+ lines.push('');
232
+ lines.push('| What | Where |');
233
+ lines.push('|------|-------|');
234
+ lines.push('| Config | `.workflow/config.json` |');
235
+ lines.push('| Tasks | `.workflow/state/ready.json` |');
236
+ lines.push('| Logs | `.workflow/state/request-log.md` |');
237
+ lines.push('| Components | `.workflow/state/app-map.md` |');
238
+ lines.push('| Rules | `.workflow/state/decisions.md` |');
239
+ lines.push('');
240
+
241
+ lines.push('---');
242
+ lines.push('');
243
+ lines.push('*Note: Enforcement is advisory. Codex lacks pre-operation hooks.*');
244
+
245
+ return lines.join('\n');
246
+ }
247
+
248
+ /**
249
+ * Generate .codex/config.toml content
250
+ */
251
+ generateConfigToml(config) {
252
+ const context = this.buildContext(config);
253
+
254
+ // Try to use Handlebars template with proper error handling
255
+ if (Handlebars) {
256
+ try {
257
+ const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'codex-config.hbs');
258
+ if (fs.existsSync(templatePath)) {
259
+ const templateSource = fs.readFileSync(templatePath, 'utf-8');
260
+ const template = Handlebars.compile(templateSource);
261
+ return template(context);
262
+ }
263
+ } catch (err) {
264
+ // Template failed - fall through to inline generation
265
+ this.log(`Config template failed, using fallback: ${err.message}`);
266
+ }
267
+ }
268
+
269
+ // Fallback to inline generation
270
+ return this.generateConfigTomlFallback(context);
271
+ }
272
+
273
+ /**
274
+ * Fallback config.toml generation
275
+ */
276
+ generateConfigTomlFallback(context) {
277
+ const lines = [];
278
+
279
+ lines.push('# WogiFlow Codex Configuration');
280
+ lines.push(`# Generated: ${context.timestamp}`);
281
+ lines.push('');
282
+
283
+ // Approvals - safer default
284
+ lines.push('[approvals]');
285
+ lines.push('mode = "on-request"');
286
+ lines.push('');
287
+
288
+ // MCP Servers
289
+ lines.push('[mcp_servers.wogiflow_memory]');
290
+ lines.push('command = "node"');
291
+ lines.push(`args = ["mcp-memory-server/index.js"]`);
292
+ lines.push(`cwd = "${context.projectRoot}"`);
293
+ lines.push('');
294
+
295
+ // Features
296
+ lines.push('[features]');
297
+ lines.push('child_agents_md = true');
298
+ lines.push('');
299
+
300
+ // Fallback filenames
301
+ lines.push('project_doc_fallback_filenames = ["CLAUDE.md", "GEMINI.md"]');
302
+ lines.push('');
303
+
304
+ return lines.join('\n');
305
+ }
306
+
307
+ /**
308
+ * Convert Claude skills to Codex SKILL.md format
309
+ */
310
+ convertAndSyncSkills() {
311
+ const claudeSkillsDir = path.join(this.projectDir, '.claude', 'skills');
312
+ const codexSkillsDir = path.join(this.projectDir, this.getSkillsPath());
313
+
314
+ if (!fs.existsSync(claudeSkillsDir)) {
315
+ this.log('No Claude skills to convert');
316
+ return;
317
+ }
318
+
319
+ // Ensure Codex skills directory exists with explicit permissions
320
+ if (!fs.existsSync(codexSkillsDir)) {
321
+ fs.mkdirSync(codexSkillsDir, { recursive: true, mode: 0o755 });
322
+ }
323
+
324
+ let skillDirs;
325
+ try {
326
+ skillDirs = fs.readdirSync(claudeSkillsDir, { withFileTypes: true })
327
+ .filter(d => d.isDirectory())
328
+ .map(d => d.name);
329
+ } catch (err) {
330
+ this.log(`Failed to read skills directory: ${err.message}`);
331
+ return;
332
+ }
333
+
334
+ let converted = 0;
335
+ let skipped = 0;
336
+
337
+ for (const skillName of skillDirs) {
338
+ try {
339
+ // SECURITY: Validate skillName to prevent path traversal
340
+ // path.basename() removes any directory components (../, etc.)
341
+ const safeSkillName = path.basename(skillName);
342
+ if (safeSkillName !== skillName) {
343
+ this.log(`Skipping suspicious skill name: ${skillName}`);
344
+ skipped++;
345
+ continue;
346
+ }
347
+
348
+ // Additional validation: only alphanumeric, dash, underscore
349
+ if (!/^[a-zA-Z0-9_-]+$/.test(safeSkillName)) {
350
+ this.log(`Skipping skill with invalid characters: ${skillName}`);
351
+ skipped++;
352
+ continue;
353
+ }
354
+
355
+ const claudeSkillMd = path.join(claudeSkillsDir, safeSkillName, 'skill.md');
356
+
357
+ if (fs.existsSync(claudeSkillMd)) {
358
+ const content = fs.readFileSync(claudeSkillMd, 'utf-8');
359
+ const codexContent = this.convertSkillToCodex(safeSkillName, content);
360
+
361
+ // Create Codex skill directory with explicit permissions
362
+ const codexSkillDir = path.join(codexSkillsDir, safeSkillName);
363
+ if (!fs.existsSync(codexSkillDir)) {
364
+ fs.mkdirSync(codexSkillDir, { recursive: true, mode: 0o755 });
365
+ }
366
+
367
+ // Write SKILL.md (Codex format)
368
+ fs.writeFileSync(path.join(codexSkillDir, 'SKILL.md'), codexContent);
369
+ this.log(`Converted skill: ${safeSkillName}`);
370
+ converted++;
371
+ }
372
+ } catch (err) {
373
+ // Log error but continue with other skills
374
+ this.log(`Failed to convert skill ${skillName}: ${err.message}`);
375
+ skipped++;
376
+ }
377
+ }
378
+
379
+ if (converted > 0 || skipped > 0) {
380
+ this.log(`Skill sync complete: ${converted} converted, ${skipped} skipped`);
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Convert a Claude skill.md to Codex SKILL.md format
386
+ */
387
+ convertSkillToCodex(skillName, claudeContent) {
388
+ const lines = [];
389
+
390
+ // Parse Claude frontmatter if present
391
+ let description = `WogiFlow ${skillName} skill`;
392
+ let body = claudeContent;
393
+
394
+ const frontmatterMatch = claudeContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
395
+ if (frontmatterMatch) {
396
+ const frontmatter = frontmatterMatch[1];
397
+ body = frontmatterMatch[2].trim();
398
+
399
+ const descMatch = frontmatter.match(/description:\s*["']?([^"'\n]+)["']?/);
400
+ if (descMatch) {
401
+ description = descMatch[1];
402
+ }
403
+ }
404
+
405
+ // Generate Codex SKILL.md format
406
+ lines.push('---');
407
+ lines.push(`name: ${skillName}`);
408
+ lines.push(`description: ${description}`);
409
+ lines.push('metadata:');
410
+ lines.push(` short-description: ${description.slice(0, 50)}`);
411
+ lines.push(` source: wogi-flow`);
412
+ lines.push('---');
413
+ lines.push('');
414
+ lines.push(body);
415
+
416
+ return lines.join('\n');
417
+ }
418
+ }
419
+
420
+ // ============================================================
421
+ // Module Exports
422
+ // ============================================================
423
+
424
+ module.exports = CodexBridge;
425
+
426
+ // CLI interface if run directly
427
+ if (require.main === module) {
428
+ const args = process.argv.slice(2);
429
+ const command = args[0];
430
+
431
+ const bridge = new CodexBridge({ verbose: true });
432
+
433
+ switch (command) {
434
+ case 'sync':
435
+ bridge.sync().then(results => {
436
+ console.log('');
437
+ console.log('Codex Bridge Sync Results:');
438
+ console.log(` Success: ${results.success}`);
439
+ console.log(` Duration: ${results.duration}ms`);
440
+ console.log(` Synced: ${results.synced.join(', ')}`);
441
+ if (results.errors.length > 0) {
442
+ console.log(` Errors: ${results.errors.map(e => e.error).join(', ')}`);
443
+ }
444
+ });
445
+ break;
446
+
447
+ case 'status':
448
+ const agentsMdExists = fs.existsSync(path.join(process.cwd(), 'AGENTS.md'));
449
+ const configTomlExists = fs.existsSync(path.join(process.cwd(), '.codex', 'config.toml'));
450
+
451
+ console.log('Codex Bridge Status:');
452
+ console.log(` AGENTS.md: ${agentsMdExists ? '✓ exists' : '✗ missing'}`);
453
+ console.log(` .codex/config.toml: ${configTomlExists ? '✓ exists' : '✗ missing'}`);
454
+ break;
455
+
456
+ default:
457
+ console.log('WogiFlow Codex Bridge');
458
+ console.log('');
459
+ console.log('Commands:');
460
+ console.log(' sync Sync WogiFlow config to Codex format');
461
+ console.log(' status Check current Codex configuration');
462
+ console.log('');
463
+ console.log('Usage:');
464
+ console.log(' node .workflow/bridges/codex-bridge.js sync');
465
+ }
466
+ }