wogiflow 1.0.47 → 1.0.48
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/.claude/commands/wogi-review-fix.md +241 -0
- package/.workflow/bridges/codex-bridge.js +441 -0
- package/.workflow/bridges/cursor-bridge.js +548 -0
- package/.workflow/bridges/gemini-bridge.js +611 -0
- package/.workflow/bridges/index.js +41 -4
- package/.workflow/bridges/kimi-bridge.js +425 -0
- package/.workflow/bridges/opencode-bridge.js +800 -0
- package/package.json +1 -1
- package/scripts/hooks/adapters/cursor.js +421 -0
- package/scripts/hooks/entry/cursor/before-submit-prompt.js +102 -0
- package/.claude/rules/README.md +0 -60
- package/.claude/rules/architecture/component-reuse.md +0 -38
- package/.claude/rules/architecture/document-structure.md +0 -76
- package/.claude/rules/architecture/feature-refactoring-cleanup.md +0 -87
- package/.claude/rules/architecture/model-management.md +0 -35
- package/.claude/rules/code-style/naming-conventions.md +0 -55
- package/.claude/rules/security/security-patterns.md +0 -143
- package/.workflow/specs/architecture.md.template +0 -24
- package/.workflow/specs/stack.md.template +0 -33
- package/.workflow/specs/testing.md.template +0 -36
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Comprehensive code review with **automatic fixing**. Runs the full `/wogi-review` process, then automatically fixes all identified issues and re-verifies.
|
|
2
|
+
|
|
3
|
+
**Triggers**: `/wogi-review-fix`, "review and fix", "fix all issues"
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
/wogi-review-fix # Review + auto-fix all issues
|
|
9
|
+
/wogi-review-fix --dry-run # Show what would be fixed (no changes)
|
|
10
|
+
/wogi-review-fix --no-verify # Skip re-verification after fixes
|
|
11
|
+
/wogi-review-fix --commits 3 # Review last 3 commits + fix
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
18
|
+
│ /wogi-review-fix │
|
|
19
|
+
├─────────────────────────────────────────────────────────────┤
|
|
20
|
+
│ PHASE 1: REVIEW (same as /wogi-review) │
|
|
21
|
+
│ 1. Identify changed files (git diff) │
|
|
22
|
+
│ 2. Run verification gates (lint, typecheck, tests) │
|
|
23
|
+
│ 3. Run AI review (parallel or multi-pass) │
|
|
24
|
+
│ 4. Consolidate findings │
|
|
25
|
+
│ │
|
|
26
|
+
│ PHASE 2: AUTO-FIX │
|
|
27
|
+
│ 5. Categorize issues (auto-fixable vs manual) │
|
|
28
|
+
│ 6. For each auto-fixable issue: │
|
|
29
|
+
│ → Read file │
|
|
30
|
+
│ → Apply fix │
|
|
31
|
+
│ → Verify syntax (node --check) │
|
|
32
|
+
│ → Track result │
|
|
33
|
+
│ │
|
|
34
|
+
│ PHASE 3: RE-VERIFY │
|
|
35
|
+
│ 7. Run verification gates again │
|
|
36
|
+
│ 8. Report: Fixed N, Manual M, Verification PASS/FAIL │
|
|
37
|
+
└─────────────────────────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Auto-Fixable vs Manual Issues
|
|
41
|
+
|
|
42
|
+
### Auto-Fixable (will be fixed automatically)
|
|
43
|
+
|
|
44
|
+
| Issue Type | Fix Method |
|
|
45
|
+
|------------|------------|
|
|
46
|
+
| Unused imports | Remove the import line |
|
|
47
|
+
| Console.log in production | Remove or convert to proper logger |
|
|
48
|
+
| Missing try-catch (simple) | Wrap operation in try-catch |
|
|
49
|
+
| Naming convention violation | Rename file/variable to match convention |
|
|
50
|
+
| Missing null check (simple) | Add optional chaining `?.` or guard |
|
|
51
|
+
| Dead code / unreachable | Remove the dead code |
|
|
52
|
+
| Duplicate code (small) | Extract to shared function |
|
|
53
|
+
|
|
54
|
+
### Manual (will be listed for user attention)
|
|
55
|
+
|
|
56
|
+
| Issue Type | Why Manual |
|
|
57
|
+
|------------|------------|
|
|
58
|
+
| Logic bugs | Requires understanding intent |
|
|
59
|
+
| Security vulnerabilities | Requires careful review |
|
|
60
|
+
| Architecture issues | Requires design decisions |
|
|
61
|
+
| Breaking API changes | Requires coordination |
|
|
62
|
+
| Complex refactors | Requires validation |
|
|
63
|
+
|
|
64
|
+
## Execution Steps
|
|
65
|
+
|
|
66
|
+
### Step 1: Run Full Review
|
|
67
|
+
|
|
68
|
+
Execute the standard `/wogi-review` process:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Get changed files
|
|
72
|
+
git diff --name-only HEAD
|
|
73
|
+
|
|
74
|
+
# Run verification gates
|
|
75
|
+
npm run lint 2>&1 | head -50
|
|
76
|
+
npm run typecheck 2>&1 | head -50
|
|
77
|
+
|
|
78
|
+
# Run AI review (parallel or multi-pass based on file count)
|
|
79
|
+
# Collect all findings with file:line:issue:severity:suggestion
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Step 2: Categorize Findings
|
|
83
|
+
|
|
84
|
+
After review completes, categorize each finding:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Example finding structure
|
|
88
|
+
{
|
|
89
|
+
file: "src/utils.ts",
|
|
90
|
+
line: 45,
|
|
91
|
+
issue: "Unused import 'lodash'",
|
|
92
|
+
severity: "low",
|
|
93
|
+
category: "unused-import", // Auto-fixable
|
|
94
|
+
suggestion: "Remove the unused import"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Categorization rules:**
|
|
99
|
+
- `unused-import` → Auto-fix
|
|
100
|
+
- `console-log` → Auto-fix
|
|
101
|
+
- `missing-null-check` (single line) → Auto-fix
|
|
102
|
+
- `naming-convention` → Auto-fix
|
|
103
|
+
- `logic-error` → Manual
|
|
104
|
+
- `security-*` → Manual
|
|
105
|
+
- `architecture-*` → Manual
|
|
106
|
+
|
|
107
|
+
### Step 3: Fix Loop
|
|
108
|
+
|
|
109
|
+
For each auto-fixable issue, in order of file (to batch edits):
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
For each file with issues:
|
|
113
|
+
1. Read the file
|
|
114
|
+
2. For each issue in this file:
|
|
115
|
+
a. Apply the fix using Edit tool
|
|
116
|
+
b. Log: "Fixed: [issue] in [file:line]"
|
|
117
|
+
3. Verify file syntax: node --check [file] (for JS/TS)
|
|
118
|
+
4. If syntax fails:
|
|
119
|
+
- Rollback edit
|
|
120
|
+
- Move issue to "Manual" list
|
|
121
|
+
- Log: "Fix failed, moved to manual: [issue]"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 4: Re-Verification
|
|
125
|
+
|
|
126
|
+
After all fixes applied:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Run verification gates again
|
|
130
|
+
npm run lint 2>&1 | head -50
|
|
131
|
+
npm run typecheck 2>&1 | head -50
|
|
132
|
+
npm run test 2>&1 | head -50 # If tests exist
|
|
133
|
+
|
|
134
|
+
# Syntax check all modified files
|
|
135
|
+
node --check [modified files]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step 5: Summary Report
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
╔══════════════════════════════════════════════════════════╗
|
|
142
|
+
║ Review + Fix Complete ║
|
|
143
|
+
╚══════════════════════════════════════════════════════════╝
|
|
144
|
+
|
|
145
|
+
═══════════════════════════════════════════════════════════
|
|
146
|
+
FIXES APPLIED (12 issues)
|
|
147
|
+
═══════════════════════════════════════════════════════════
|
|
148
|
+
✓ src/utils.ts:45 - Removed unused import 'lodash'
|
|
149
|
+
✓ src/api.ts:23 - Removed console.log
|
|
150
|
+
✓ src/api.ts:67 - Added null check
|
|
151
|
+
✓ src/components/Button.tsx:12 - Removed unused import
|
|
152
|
+
... (8 more)
|
|
153
|
+
|
|
154
|
+
═══════════════════════════════════════════════════════════
|
|
155
|
+
MANUAL ATTENTION NEEDED (3 issues)
|
|
156
|
+
═══════════════════════════════════════════════════════════
|
|
157
|
+
⚠ src/auth.ts:89 - Potential SQL injection (security)
|
|
158
|
+
→ Review: User input not sanitized before query
|
|
159
|
+
⚠ src/api.ts:134 - Race condition (logic)
|
|
160
|
+
→ Review: Async operation may complete out of order
|
|
161
|
+
⚠ src/utils.ts:200 - Breaking API change (architecture)
|
|
162
|
+
→ Review: Function signature changed, check callers
|
|
163
|
+
|
|
164
|
+
═══════════════════════════════════════════════════════════
|
|
165
|
+
RE-VERIFICATION
|
|
166
|
+
═══════════════════════════════════════════════════════════
|
|
167
|
+
✓ Lint: passed
|
|
168
|
+
✓ TypeCheck: passed
|
|
169
|
+
✓ Syntax: all files valid
|
|
170
|
+
|
|
171
|
+
═══════════════════════════════════════════════════════════
|
|
172
|
+
SUMMARY
|
|
173
|
+
═══════════════════════════════════════════════════════════
|
|
174
|
+
Total issues found: 15
|
|
175
|
+
Auto-fixed: 12
|
|
176
|
+
Manual review needed: 3
|
|
177
|
+
Verification: PASSED
|
|
178
|
+
|
|
179
|
+
Files modified: 4
|
|
180
|
+
• src/utils.ts (3 fixes)
|
|
181
|
+
• src/api.ts (5 fixes)
|
|
182
|
+
• src/components/Button.tsx (2 fixes)
|
|
183
|
+
• src/auth.ts (2 fixes)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Options
|
|
187
|
+
|
|
188
|
+
| Flag | Description |
|
|
189
|
+
|------|-------------|
|
|
190
|
+
| `--dry-run` | Show what would be fixed without making changes |
|
|
191
|
+
| `--no-verify` | Skip re-verification after fixes |
|
|
192
|
+
| `--commits N` | Include last N commits in review scope |
|
|
193
|
+
| `--staged` | Only review staged changes |
|
|
194
|
+
| `--skip-manual` | Don't show manual issues in report |
|
|
195
|
+
|
|
196
|
+
## Dry Run Mode
|
|
197
|
+
|
|
198
|
+
With `--dry-run`, shows the fix plan without applying:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
═══════════════════════════════════════════════════════════
|
|
202
|
+
DRY RUN - WOULD FIX (12 issues)
|
|
203
|
+
═══════════════════════════════════════════════════════════
|
|
204
|
+
• src/utils.ts:45 - Would remove unused import 'lodash'
|
|
205
|
+
• src/api.ts:23 - Would remove console.log
|
|
206
|
+
• src/api.ts:67 - Would add null check: user?.profile
|
|
207
|
+
...
|
|
208
|
+
|
|
209
|
+
Run without --dry-run to apply these fixes.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Comparison with /wogi-review
|
|
213
|
+
|
|
214
|
+
| Aspect | `/wogi-review` | `/wogi-review-fix` |
|
|
215
|
+
|--------|----------------|-------------------|
|
|
216
|
+
| Reviews code | ✓ | ✓ |
|
|
217
|
+
| Lists issues | ✓ | ✓ |
|
|
218
|
+
| Fixes issues | ✗ | ✓ (auto-fixable) |
|
|
219
|
+
| Re-verifies | ✗ | ✓ |
|
|
220
|
+
| End state | Issues listed | Issues resolved |
|
|
221
|
+
|
|
222
|
+
## When to Use
|
|
223
|
+
|
|
224
|
+
**Use `/wogi-review`** when:
|
|
225
|
+
- You want to see issues before deciding to fix
|
|
226
|
+
- You're reviewing someone else's code
|
|
227
|
+
- You want to understand the codebase state
|
|
228
|
+
|
|
229
|
+
**Use `/wogi-review-fix`** when:
|
|
230
|
+
- You want issues fixed immediately
|
|
231
|
+
- You trust the auto-fix for common issues
|
|
232
|
+
- You're cleaning up after a large change
|
|
233
|
+
- You want a "fix everything" single command
|
|
234
|
+
|
|
235
|
+
## Safety Guarantees
|
|
236
|
+
|
|
237
|
+
1. **Syntax verification** - Every fix is syntax-checked before moving on
|
|
238
|
+
2. **Rollback on failure** - If a fix breaks syntax, it's reverted
|
|
239
|
+
3. **Manual escalation** - Complex issues are never auto-fixed
|
|
240
|
+
4. **Security issues untouched** - Security findings always require manual review
|
|
241
|
+
5. **Git-friendly** - All changes can be reviewed in `git diff` before commit
|
|
@@ -0,0 +1,441 @@
|
|
|
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
|
+
* Generate AGENTS.md content
|
|
52
|
+
*/
|
|
53
|
+
generateRulesContent(config) {
|
|
54
|
+
const context = this.buildContext(config);
|
|
55
|
+
|
|
56
|
+
// Try to use Handlebars template with proper error handling
|
|
57
|
+
if (Handlebars) {
|
|
58
|
+
try {
|
|
59
|
+
const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'agents-md.hbs');
|
|
60
|
+
if (fs.existsSync(templatePath)) {
|
|
61
|
+
const templateSource = fs.readFileSync(templatePath, 'utf-8');
|
|
62
|
+
const template = Handlebars.compile(templateSource);
|
|
63
|
+
return template(context);
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// Template failed - fall through to inline generation
|
|
67
|
+
this.log(`Template generation failed, using fallback: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback to inline generation
|
|
72
|
+
return this.generateAgentsMdFallback(context);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Codex-specific setup: generate config.toml
|
|
77
|
+
*/
|
|
78
|
+
async setupCliSpecific(config) {
|
|
79
|
+
// Generate config.toml
|
|
80
|
+
const configTomlPath = path.join(this.projectDir, this.getCliFolder(), 'config.toml');
|
|
81
|
+
const configTomlContent = this.generateConfigToml(config);
|
|
82
|
+
fs.writeFileSync(configTomlPath, configTomlContent);
|
|
83
|
+
this.log('Generated .codex/config.toml');
|
|
84
|
+
|
|
85
|
+
// Convert Claude skills to Codex format
|
|
86
|
+
this.convertAndSyncSkills();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ==================== Codex-Specific Methods ====================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build template context
|
|
93
|
+
*/
|
|
94
|
+
buildContext(config) {
|
|
95
|
+
// Load decisions.md for coding rules
|
|
96
|
+
const decisionsPath = path.join(this.projectDir, this.workflowDir, 'state', 'decisions.md');
|
|
97
|
+
let decisions = '';
|
|
98
|
+
try {
|
|
99
|
+
decisions = fs.readFileSync(decisionsPath, 'utf-8');
|
|
100
|
+
} catch (err) {
|
|
101
|
+
// File may not exist or be unreadable - continue with empty decisions
|
|
102
|
+
if (err.code !== 'ENOENT') {
|
|
103
|
+
this.log(`Failed to read decisions.md: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Get installed skills
|
|
108
|
+
const skills = config.skills?.installed || [];
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
projectName: path.basename(this.projectDir),
|
|
112
|
+
projectRoot: this.projectDir,
|
|
113
|
+
timestamp: new Date().toISOString(),
|
|
114
|
+
config,
|
|
115
|
+
decisions,
|
|
116
|
+
skills,
|
|
117
|
+
enforcement: config.enforcement || {},
|
|
118
|
+
research: config.research || {},
|
|
119
|
+
qualityGates: config.qualityGates || {},
|
|
120
|
+
commits: config.commits || {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Fallback AGENTS.md generation (no Handlebars)
|
|
126
|
+
*/
|
|
127
|
+
generateAgentsMdFallback(context) {
|
|
128
|
+
const lines = [];
|
|
129
|
+
|
|
130
|
+
lines.push('# WogiFlow Project Instructions');
|
|
131
|
+
lines.push('');
|
|
132
|
+
lines.push(`> Generated by WogiFlow Codex Bridge - ${context.timestamp}`);
|
|
133
|
+
lines.push('');
|
|
134
|
+
lines.push('## Core Principles');
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push('1. **State files are memory** - Read `.workflow/state/` first');
|
|
137
|
+
lines.push('2. **Config drives behavior** - Follow `.workflow/config.json` rules');
|
|
138
|
+
lines.push('3. **Log every change** - Append to `request-log.md`');
|
|
139
|
+
lines.push('4. **Reuse components** - Check `app-map.md` before creating');
|
|
140
|
+
lines.push('5. **Learn from feedback** - Update instructions when corrected');
|
|
141
|
+
lines.push('');
|
|
142
|
+
|
|
143
|
+
// Task Gating
|
|
144
|
+
if (context.enforcement?.strictMode) {
|
|
145
|
+
lines.push('## Task Gating (MANDATORY)');
|
|
146
|
+
lines.push('');
|
|
147
|
+
lines.push('**STOP. Before ANY implementation:**');
|
|
148
|
+
lines.push('1. Check `.workflow/state/ready.json` for existing tasks');
|
|
149
|
+
lines.push('2. If no task exists, create one with `/wogi-story`');
|
|
150
|
+
lines.push('3. Start with `/wogi-start TASK-XXX`');
|
|
151
|
+
lines.push('');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Research Protocol
|
|
155
|
+
if (context.research?.enabled) {
|
|
156
|
+
lines.push('## Research Protocol');
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push('For capability/feasibility/existence questions:');
|
|
159
|
+
lines.push('1. Search local files thoroughly (Glob, Grep)');
|
|
160
|
+
lines.push('2. Web search for current documentation');
|
|
161
|
+
lines.push('3. List assumptions and verify each');
|
|
162
|
+
lines.push('4. Cite sources for all claims');
|
|
163
|
+
lines.push('5. State confidence level (HIGH/MEDIUM/LOW)');
|
|
164
|
+
lines.push('');
|
|
165
|
+
lines.push('**FORBIDDEN:** Claiming "X doesn\'t exist" without exhaustive search.');
|
|
166
|
+
lines.push('');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Essential Commands
|
|
170
|
+
lines.push('## Essential Commands');
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push('| Command | Purpose |');
|
|
173
|
+
lines.push('|---------|---------|');
|
|
174
|
+
lines.push('| `/wogi-ready` | Show available tasks |');
|
|
175
|
+
lines.push('| `/wogi-start TASK-X` | Start task with context |');
|
|
176
|
+
lines.push('| `/wogi-story "title"` | Create story with AC |');
|
|
177
|
+
lines.push('| `/wogi-status` | Project overview |');
|
|
178
|
+
lines.push('| `/wogi-research "q"` | Research before answering |');
|
|
179
|
+
lines.push('');
|
|
180
|
+
|
|
181
|
+
// Request Logging
|
|
182
|
+
lines.push('## Request Logging');
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('After EVERY request that changes files:');
|
|
185
|
+
lines.push('```markdown');
|
|
186
|
+
lines.push('### R-[XXX] | [YYYY-MM-DD HH:MM]');
|
|
187
|
+
lines.push('**Type**: new | fix | change | refactor');
|
|
188
|
+
lines.push('**Tags**: #screen:[name] #component:[name]');
|
|
189
|
+
lines.push('**Request**: "[what user asked]"');
|
|
190
|
+
lines.push('**Result**: [what was done]');
|
|
191
|
+
lines.push('**Files**: [files changed]');
|
|
192
|
+
lines.push('```');
|
|
193
|
+
lines.push('');
|
|
194
|
+
|
|
195
|
+
// Component Reuse
|
|
196
|
+
lines.push('## Component Reuse');
|
|
197
|
+
lines.push('');
|
|
198
|
+
lines.push('**Before creating ANY component:**');
|
|
199
|
+
lines.push('1. Check `app-map.md`');
|
|
200
|
+
lines.push('2. Search codebase for existing');
|
|
201
|
+
lines.push('3. Priority: Use existing → Add variant → Extend → Create new');
|
|
202
|
+
lines.push('');
|
|
203
|
+
|
|
204
|
+
// File Locations
|
|
205
|
+
lines.push('## File Locations');
|
|
206
|
+
lines.push('');
|
|
207
|
+
lines.push('| What | Where |');
|
|
208
|
+
lines.push('|------|-------|');
|
|
209
|
+
lines.push('| Config | `.workflow/config.json` |');
|
|
210
|
+
lines.push('| Tasks | `.workflow/state/ready.json` |');
|
|
211
|
+
lines.push('| Logs | `.workflow/state/request-log.md` |');
|
|
212
|
+
lines.push('| Components | `.workflow/state/app-map.md` |');
|
|
213
|
+
lines.push('| Rules | `.workflow/state/decisions.md` |');
|
|
214
|
+
lines.push('');
|
|
215
|
+
|
|
216
|
+
lines.push('---');
|
|
217
|
+
lines.push('');
|
|
218
|
+
lines.push('*Note: Enforcement is advisory. Codex lacks pre-operation hooks.*');
|
|
219
|
+
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Generate .codex/config.toml content
|
|
225
|
+
*/
|
|
226
|
+
generateConfigToml(config) {
|
|
227
|
+
const context = this.buildContext(config);
|
|
228
|
+
|
|
229
|
+
// Try to use Handlebars template with proper error handling
|
|
230
|
+
if (Handlebars) {
|
|
231
|
+
try {
|
|
232
|
+
const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'codex-config.hbs');
|
|
233
|
+
if (fs.existsSync(templatePath)) {
|
|
234
|
+
const templateSource = fs.readFileSync(templatePath, 'utf-8');
|
|
235
|
+
const template = Handlebars.compile(templateSource);
|
|
236
|
+
return template(context);
|
|
237
|
+
}
|
|
238
|
+
} catch (err) {
|
|
239
|
+
// Template failed - fall through to inline generation
|
|
240
|
+
this.log(`Config template failed, using fallback: ${err.message}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Fallback to inline generation
|
|
245
|
+
return this.generateConfigTomlFallback(context);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Fallback config.toml generation
|
|
250
|
+
*/
|
|
251
|
+
generateConfigTomlFallback(context) {
|
|
252
|
+
const lines = [];
|
|
253
|
+
|
|
254
|
+
lines.push('# WogiFlow Codex Configuration');
|
|
255
|
+
lines.push(`# Generated: ${context.timestamp}`);
|
|
256
|
+
lines.push('');
|
|
257
|
+
|
|
258
|
+
// Approvals - safer default
|
|
259
|
+
lines.push('[approvals]');
|
|
260
|
+
lines.push('mode = "on-request"');
|
|
261
|
+
lines.push('');
|
|
262
|
+
|
|
263
|
+
// MCP Servers
|
|
264
|
+
lines.push('[mcp_servers.wogiflow_memory]');
|
|
265
|
+
lines.push('command = "node"');
|
|
266
|
+
lines.push(`args = ["mcp-memory-server/index.js"]`);
|
|
267
|
+
lines.push(`cwd = "${context.projectRoot}"`);
|
|
268
|
+
lines.push('');
|
|
269
|
+
|
|
270
|
+
// Features
|
|
271
|
+
lines.push('[features]');
|
|
272
|
+
lines.push('child_agents_md = true');
|
|
273
|
+
lines.push('');
|
|
274
|
+
|
|
275
|
+
// Fallback filenames
|
|
276
|
+
lines.push('project_doc_fallback_filenames = ["CLAUDE.md", "GEMINI.md"]');
|
|
277
|
+
lines.push('');
|
|
278
|
+
|
|
279
|
+
return lines.join('\n');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Convert Claude skills to Codex SKILL.md format
|
|
284
|
+
*/
|
|
285
|
+
convertAndSyncSkills() {
|
|
286
|
+
const claudeSkillsDir = path.join(this.projectDir, '.claude', 'skills');
|
|
287
|
+
const codexSkillsDir = path.join(this.projectDir, this.getSkillsPath());
|
|
288
|
+
|
|
289
|
+
if (!fs.existsSync(claudeSkillsDir)) {
|
|
290
|
+
this.log('No Claude skills to convert');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Ensure Codex skills directory exists with explicit permissions
|
|
295
|
+
if (!fs.existsSync(codexSkillsDir)) {
|
|
296
|
+
fs.mkdirSync(codexSkillsDir, { recursive: true, mode: 0o755 });
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let skillDirs;
|
|
300
|
+
try {
|
|
301
|
+
skillDirs = fs.readdirSync(claudeSkillsDir, { withFileTypes: true })
|
|
302
|
+
.filter(d => d.isDirectory())
|
|
303
|
+
.map(d => d.name);
|
|
304
|
+
} catch (err) {
|
|
305
|
+
this.log(`Failed to read skills directory: ${err.message}`);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
let converted = 0;
|
|
310
|
+
let skipped = 0;
|
|
311
|
+
|
|
312
|
+
for (const skillName of skillDirs) {
|
|
313
|
+
try {
|
|
314
|
+
// SECURITY: Validate skillName to prevent path traversal
|
|
315
|
+
// path.basename() removes any directory components (../, etc.)
|
|
316
|
+
const safeSkillName = path.basename(skillName);
|
|
317
|
+
if (safeSkillName !== skillName) {
|
|
318
|
+
this.log(`Skipping suspicious skill name: ${skillName}`);
|
|
319
|
+
skipped++;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Additional validation: only alphanumeric, dash, underscore
|
|
324
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(safeSkillName)) {
|
|
325
|
+
this.log(`Skipping skill with invalid characters: ${skillName}`);
|
|
326
|
+
skipped++;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const claudeSkillMd = path.join(claudeSkillsDir, safeSkillName, 'skill.md');
|
|
331
|
+
|
|
332
|
+
if (fs.existsSync(claudeSkillMd)) {
|
|
333
|
+
const content = fs.readFileSync(claudeSkillMd, 'utf-8');
|
|
334
|
+
const codexContent = this.convertSkillToCodex(safeSkillName, content);
|
|
335
|
+
|
|
336
|
+
// Create Codex skill directory with explicit permissions
|
|
337
|
+
const codexSkillDir = path.join(codexSkillsDir, safeSkillName);
|
|
338
|
+
if (!fs.existsSync(codexSkillDir)) {
|
|
339
|
+
fs.mkdirSync(codexSkillDir, { recursive: true, mode: 0o755 });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Write SKILL.md (Codex format)
|
|
343
|
+
fs.writeFileSync(path.join(codexSkillDir, 'SKILL.md'), codexContent);
|
|
344
|
+
this.log(`Converted skill: ${safeSkillName}`);
|
|
345
|
+
converted++;
|
|
346
|
+
}
|
|
347
|
+
} catch (err) {
|
|
348
|
+
// Log error but continue with other skills
|
|
349
|
+
this.log(`Failed to convert skill ${skillName}: ${err.message}`);
|
|
350
|
+
skipped++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (converted > 0 || skipped > 0) {
|
|
355
|
+
this.log(`Skill sync complete: ${converted} converted, ${skipped} skipped`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Convert a Claude skill.md to Codex SKILL.md format
|
|
361
|
+
*/
|
|
362
|
+
convertSkillToCodex(skillName, claudeContent) {
|
|
363
|
+
const lines = [];
|
|
364
|
+
|
|
365
|
+
// Parse Claude frontmatter if present
|
|
366
|
+
let description = `WogiFlow ${skillName} skill`;
|
|
367
|
+
let body = claudeContent;
|
|
368
|
+
|
|
369
|
+
const frontmatterMatch = claudeContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
370
|
+
if (frontmatterMatch) {
|
|
371
|
+
const frontmatter = frontmatterMatch[1];
|
|
372
|
+
body = frontmatterMatch[2].trim();
|
|
373
|
+
|
|
374
|
+
const descMatch = frontmatter.match(/description:\s*["']?([^"'\n]+)["']?/);
|
|
375
|
+
if (descMatch) {
|
|
376
|
+
description = descMatch[1];
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Generate Codex SKILL.md format
|
|
381
|
+
lines.push('---');
|
|
382
|
+
lines.push(`name: ${skillName}`);
|
|
383
|
+
lines.push(`description: ${description}`);
|
|
384
|
+
lines.push('metadata:');
|
|
385
|
+
lines.push(` short-description: ${description.slice(0, 50)}`);
|
|
386
|
+
lines.push(` source: wogi-flow`);
|
|
387
|
+
lines.push('---');
|
|
388
|
+
lines.push('');
|
|
389
|
+
lines.push(body);
|
|
390
|
+
|
|
391
|
+
return lines.join('\n');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ============================================================
|
|
396
|
+
// Module Exports
|
|
397
|
+
// ============================================================
|
|
398
|
+
|
|
399
|
+
module.exports = CodexBridge;
|
|
400
|
+
|
|
401
|
+
// CLI interface if run directly
|
|
402
|
+
if (require.main === module) {
|
|
403
|
+
const args = process.argv.slice(2);
|
|
404
|
+
const command = args[0];
|
|
405
|
+
|
|
406
|
+
const bridge = new CodexBridge({ verbose: true });
|
|
407
|
+
|
|
408
|
+
switch (command) {
|
|
409
|
+
case 'sync':
|
|
410
|
+
bridge.sync().then(results => {
|
|
411
|
+
console.log('');
|
|
412
|
+
console.log('Codex Bridge Sync Results:');
|
|
413
|
+
console.log(` Success: ${results.success}`);
|
|
414
|
+
console.log(` Duration: ${results.duration}ms`);
|
|
415
|
+
console.log(` Synced: ${results.synced.join(', ')}`);
|
|
416
|
+
if (results.errors.length > 0) {
|
|
417
|
+
console.log(` Errors: ${results.errors.map(e => e.error).join(', ')}`);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
break;
|
|
421
|
+
|
|
422
|
+
case 'status':
|
|
423
|
+
const agentsMdExists = fs.existsSync(path.join(process.cwd(), 'AGENTS.md'));
|
|
424
|
+
const configTomlExists = fs.existsSync(path.join(process.cwd(), '.codex', 'config.toml'));
|
|
425
|
+
|
|
426
|
+
console.log('Codex Bridge Status:');
|
|
427
|
+
console.log(` AGENTS.md: ${agentsMdExists ? '✓ exists' : '✗ missing'}`);
|
|
428
|
+
console.log(` .codex/config.toml: ${configTomlExists ? '✓ exists' : '✗ missing'}`);
|
|
429
|
+
break;
|
|
430
|
+
|
|
431
|
+
default:
|
|
432
|
+
console.log('WogiFlow Codex Bridge');
|
|
433
|
+
console.log('');
|
|
434
|
+
console.log('Commands:');
|
|
435
|
+
console.log(' sync Sync WogiFlow config to Codex format');
|
|
436
|
+
console.log(' status Check current Codex configuration');
|
|
437
|
+
console.log('');
|
|
438
|
+
console.log('Usage:');
|
|
439
|
+
console.log(' node .workflow/bridges/codex-bridge.js sync');
|
|
440
|
+
}
|
|
441
|
+
}
|