oh-my-customcodex 0.5.7 → 0.5.9

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 (38) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/index.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/.claude/agents/arch-speckit-agent.md +1 -1
  6. package/templates/.claude/agents/sys-memory-keeper.md +9 -9
  7. package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +4 -1
  8. package/templates/.claude/ontology/agents.yaml +16 -4
  9. package/templates/.claude/ontology/skills.yaml +9 -9
  10. package/templates/.claude/rules/MUST-agent-teams.md +85 -246
  11. package/templates/.claude/rules/SHOULD-memory-integration.md +4 -5
  12. package/templates/.claude/skills/de-lead-routing/SKILL.md +6 -13
  13. package/templates/.claude/skills/dev-lead-routing/SKILL.md +6 -13
  14. package/templates/.claude/skills/intent-detection/SKILL.md +7 -9
  15. package/templates/.claude/skills/memory-management/SKILL.md +7 -12
  16. package/templates/.claude/skills/memory-recall/SKILL.md +2 -2
  17. package/templates/.claude/skills/memory-save/SKILL.md +2 -2
  18. package/templates/.claude/skills/peer-messaging/SKILL.md +2 -2
  19. package/templates/.claude/skills/research/SKILL.md +8 -23
  20. package/templates/.claude/skills/roundtable-debate/SKILL.md +3 -4
  21. package/templates/.claude/skills/structured-dev-cycle/SKILL.md +7 -10
  22. package/templates/AGENTS.md.en +1 -2
  23. package/templates/AGENTS.md.ko +1 -2
  24. package/templates/CLAUDE.md +2 -2
  25. package/templates/CLAUDE.md.en +1 -2
  26. package/templates/CLAUDE.md.ko +1 -2
  27. package/templates/README.md +2 -2
  28. package/templates/guides/agentmemory-migration/measure-step-zero.md +13 -113
  29. package/templates/guides/agentmemory-migration/phase-1-coexist.md +18 -252
  30. package/templates/guides/index.yaml +1 -1
  31. package/templates/guides/multi-agent-debate-patterns/README.md +1 -1
  32. package/templates/guides/multi-provider-exec/README.md +9 -79
  33. package/templates/manifest.json +3 -3
  34. package/templates/.claude/skills/agora/SKILL.md +0 -209
  35. package/templates/.claude/skills/codex-exec/SKILL.md +0 -218
  36. package/templates/.claude/skills/codex-exec/scripts/codex-wrapper.cjs +0 -433
  37. package/templates/.claude/skills/gemini-exec/SKILL.md +0 -215
  38. package/templates/.claude/skills/gemini-exec/scripts/gemini-wrapper.cjs +0 -485
@@ -1,218 +0,0 @@
1
- ---
2
- name: codex-exec
3
- description: Execute OpenAI Codex CLI prompts and return results
4
- scope: core
5
- argument-hint: "<prompt> [--json] [--output <path>] [--model <name>] [--timeout <ms>] [--effort <level>]"
6
- user-invocable: true
7
- ---
8
-
9
- # Codex Exec Skill
10
-
11
- Execute OpenAI Codex CLI prompts in non-interactive mode and return structured results. Enables Claude + Codex hybrid workflows.
12
-
13
- ## Options
14
-
15
- ```
16
- <prompt> Required. The prompt to send to Codex CLI
17
- --json Return structured JSON Lines output
18
- --output <path> Save final message to file
19
- --model <name> Model override (default: Codex CLI default model)
20
- --timeout <ms> Execution timeout (default: 120000, max: 600000)
21
- --full-auto Enable auto-approval mode (codex -a full-auto)
22
- --working-dir Working directory for Codex execution
23
- --effort <level> Set reasoning effort level (minimal, low, medium, high, xhigh)
24
- Maps to Codex CLI's model_reasoning_effort config
25
- Default: uses Codex CLI's configured default
26
- Recommended: xhigh for research/analysis tasks
27
- ```
28
-
29
- ## Workflow
30
-
31
- ```
32
- 1. Pre-checks
33
- - Verify `codex` binary is installed (which codex || npx codex --version)
34
- - Verify authentication (`OPENAI_API_KEY`, `CODEX_API_KEY`, or stored `codex login` / ChatGPT login)
35
- 2. Build command
36
- - Base: codex exec --ephemeral "<prompt>"
37
- - Apply options: --json, --model, --full-auto, -C <dir>
38
- - Set --working-dir if specified
39
- 3. Execute
40
- - Run via Bash tool with timeout (default 2min, max 10min)
41
- - Or use helper script: node .codex/skills/codex-exec/scripts/codex-wrapper.cjs
42
- 4. Parse output
43
- - Text mode: return raw stdout
44
- - JSON mode: parse JSON Lines, extract final assistant message
45
- 5. Report results
46
- - Format output with execution metadata
47
- ```
48
-
49
- ## Safety Defaults
50
-
51
- - `--ephemeral`: No session persistence (conversations not saved)
52
- - Default mode: Normal approval (Codex prompts for confirmation)
53
- - Override with `--full-auto` only when explicitly requested
54
-
55
- ## Output Format
56
-
57
- ### Success (Text Mode)
58
- ```
59
- [Codex Exec] Completed
60
-
61
- Model: (default)
62
- Duration: 23.4s
63
- Working Dir: /path/to/project
64
-
65
- --- Output ---
66
- {codex response text}
67
- ```
68
-
69
- ### Success (JSON Mode)
70
- ```
71
- [Codex Exec] Completed (JSON)
72
-
73
- Model: (default)
74
- Duration: 23.4s
75
- Events: 12
76
-
77
- --- Final Message ---
78
- {extracted final assistant message}
79
- ```
80
-
81
- ### Failure
82
- ```
83
- [Codex Exec] Failed
84
-
85
- Error: {error_message}
86
- Exit Code: {code}
87
- Suggested Fix: {suggestion}
88
- ```
89
-
90
- ## Helper Script
91
-
92
- For complex executions, use the wrapper script:
93
- ```bash
94
- node .codex/skills/codex-exec/scripts/codex-wrapper.cjs --prompt "your prompt" [options]
95
- ```
96
-
97
- The wrapper provides:
98
- - Environment validation (binary + auth checks)
99
- - Safe command construction
100
- - JSON Lines parsing with event extraction
101
- - Structured JSON output
102
- - Timeout handling with graceful termination
103
-
104
- ## Examples
105
-
106
- ```bash
107
- # Simple text prompt
108
- codex-exec "explain what this project does"
109
-
110
- # JSON output with model override
111
- codex-exec "list all TODO items" --json
112
-
113
- # Save output to file
114
- codex-exec "generate a README" --output ./README.md
115
-
116
- # Full auto mode with custom timeout
117
- codex-exec "fix the failing tests" --full-auto --timeout 300000
118
-
119
- # Specify working directory
120
- codex-exec "analyze the codebase" --working-dir /path/to/project
121
- ```
122
-
123
- ## Integration
124
-
125
- Works with the orchestrator pattern:
126
- - Main conversation delegates Codex execution via this skill
127
- - Results are returned to the main conversation for further processing
128
- - Can be chained with other skills (e.g., dev-review after Codex generates code)
129
-
130
- ## Availability Check
131
-
132
- codex-exec requires the Codex CLI binary to be installed and authenticated. The skill is only usable when:
133
-
134
- 1. `codex` binary is found in PATH (`which codex` succeeds)
135
- 2. Authentication is valid (`OPENAI_API_KEY`, `CODEX_API_KEY`, or stored auth from `codex login --api-key` / ChatGPT login)
136
-
137
- If either check fails, this skill cannot be used. Fall back to Claude agents for the task.
138
-
139
- > **Note**: This skill is invoked via `/codex-exec` command, delegated by the orchestrator, or suggested by routing skills when codex is available. The intent-detection system can trigger it for research (xhigh) and code generation (hybrid) workflows.
140
-
141
- ## Agent Teams Integration
142
-
143
- When used within Agent Teams (requires explicit invocation):
144
-
145
- 1. **As delegated task**: orchestrator explicitly delegates codex-exec for code generation
146
- 2. **Hybrid workflow**: Claude team member analyzes → orchestrator invokes codex-exec → Claude reviews
147
- 3. **Iteration**: Team messaging enables review-fix cycles between Claude and Codex outputs
148
-
149
- ```
150
- Orchestrator delegates generation task
151
- → /codex-exec invoked explicitly
152
- → Output returned to orchestrator
153
- → Reviewer validates quality
154
- → Iterate if needed
155
- ```
156
-
157
- ## Research Workflow
158
-
159
- When the orchestrator or intent-detection detects a research/information gathering request (routing_rule in agent-triggers.yaml):
160
-
161
- 1. **Check Codex availability**: Verify `codex` binary plus `OPENAI_API_KEY`, `CODEX_API_KEY`, or stored `codex login` auth
162
- 2. **If available**: Execute with xhigh reasoning effort for thorough research
163
- 3. **If unavailable**: Fall back to Claude's WebFetch/WebSearch
164
-
165
- ### Research Command Pattern
166
-
167
- ```
168
- /codex-exec "Research and analyze: {topic}. Provide structured findings with sources." --effort xhigh --full-auto --json
169
- ```
170
-
171
- ### Effort Level Guide
172
-
173
- | Level | Use Case | Speed | Depth |
174
- |-------|----------|-------|-------|
175
- | minimal | Quick lookups | Fastest | Surface |
176
- | low | Simple queries | Fast | Basic |
177
- | medium | General tasks | Balanced | Standard |
178
- | high | Complex analysis | Slower | Deep |
179
- | xhigh | Research & investigation | Slowest | Maximum |
180
-
181
- ## Code Generation Workflow
182
-
183
- When routing skills detect a code generation task and codex is available:
184
-
185
- 1. **Check availability**: Verify the codex CLI directly (`command -v codex`) or via current session diagnostics
186
- 2. **If available + new file creation**: Suggest hybrid workflow
187
- 3. **Hybrid pattern**:
188
- - codex-exec generates initial code (fast, broad generation)
189
- - Claude expert reviews for quality, patterns, best practices
190
- - Iterate if needed
191
-
192
- ### Suitable Tasks
193
- - New file scaffolding
194
- - Boilerplate generation
195
- - Test stub creation
196
- - Documentation generation
197
-
198
- ### Unsuitable Tasks
199
- - Modifying existing code (Claude expert better at understanding context)
200
- - Architecture decisions (requires reasoning, not generation)
201
- - Bug fixes (requires deep code understanding)
202
-
203
- ### Code Generation Command Pattern
204
- ```
205
- /codex-exec "Generate {description} following {framework} best practices" --effort high --full-auto
206
- ```
207
-
208
- ## Browser Verify Workflow
209
-
210
- For frontend or browser-visible changes, use a Build + Vision + Verify loop instead of stopping at a successful build:
211
-
212
- 1. Build or start the local dev server.
213
- 2. Open the target in the available browser automation surface.
214
- 3. Capture screenshot evidence and console/network errors.
215
- 4. If the visual state or console is wrong, run `codex-exec` with the concrete evidence and repeat.
216
- 5. Stop only when build, browser render, and error checks all pass.
217
-
218
- This pattern composes with the Codex App Browser Use plugin or any local browser MCP. Keep the loop evidence-driven: screenshot, console output, network status, and the exact command that produced the build.
@@ -1,433 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * codex-wrapper.js
5
- *
6
- * Node.js wrapper for OpenAI Codex CLI (non-interactive execution).
7
- * Executes codex in ephemeral mode with structured JSON output.
8
- *
9
- * Usage:
10
- * node codex-wrapper.js --prompt "your prompt" [options]
11
- *
12
- * Options:
13
- * --prompt <text> Required: prompt to execute
14
- * --json Enable JSON Lines output from codex
15
- * --output <path> Save final message to file
16
- * --model <name> Specify model (default: o3)
17
- * --timeout <ms> Execution timeout in milliseconds (default: 120000, max: 600000)
18
- * --full-auto Use full-auto approval mode (default: -a never)
19
- * --working-dir <dir> Set working directory for execution
20
- *
21
- * Output (JSON to stdout):
22
- * Success: { "success": true, "output": "...", "duration_ms": 1234, ... }
23
- * Failure: { "success": false, "error": "...", "stderr": "...", ... }
24
- *
25
- * Exit codes:
26
- * 0 = success
27
- * 1 = execution error
28
- * 2 = validation error (missing binary/auth)
29
- */
30
-
31
- const { spawn, execFileSync } = require('child_process');
32
- const fs = require('fs');
33
- const path = require('path');
34
- const os = require('os');
35
-
36
- // Configuration
37
- const DEFAULT_TIMEOUT_MS = 120000; // 2 minutes
38
- const MAX_TIMEOUT_MS = 600000; // 10 minutes
39
- const KILL_GRACE_PERIOD_MS = 5000; // 5 seconds for graceful shutdown
40
-
41
- /**
42
- * Parse command line arguments
43
- * @returns {Object} Parsed arguments
44
- */
45
- function parseArgs() {
46
- const args = {
47
- prompt: null,
48
- json: false,
49
- output: null,
50
- model: null,
51
- timeout: DEFAULT_TIMEOUT_MS,
52
- fullAuto: false,
53
- workingDir: null,
54
- effort: null,
55
- };
56
-
57
- for (let i = 2; i < process.argv.length; i++) {
58
- const arg = process.argv[i];
59
-
60
- switch (arg) {
61
- case '--prompt':
62
- if (i + 1 < process.argv.length) {
63
- args.prompt = process.argv[++i];
64
- }
65
- break;
66
- case '--json':
67
- args.json = true;
68
- break;
69
- case '--output':
70
- if (i + 1 < process.argv.length) {
71
- args.output = process.argv[++i];
72
- }
73
- break;
74
- case '--model':
75
- if (i + 1 < process.argv.length) {
76
- args.model = process.argv[++i];
77
- }
78
- break;
79
- case '--timeout':
80
- if (i + 1 < process.argv.length) {
81
- const timeoutValue = parseInt(process.argv[++i], 10);
82
- if (!isNaN(timeoutValue)) {
83
- args.timeout = Math.min(timeoutValue, MAX_TIMEOUT_MS);
84
- }
85
- }
86
- break;
87
- case '--full-auto':
88
- args.fullAuto = true;
89
- break;
90
- case '--working-dir':
91
- if (i + 1 < process.argv.length) {
92
- args.workingDir = process.argv[++i];
93
- }
94
- break;
95
- case '--effort':
96
- case '--reasoning-effort':
97
- if (i + 1 < process.argv.length) {
98
- args.effort = process.argv[++i];
99
- }
100
- break;
101
- }
102
- }
103
-
104
- return args;
105
- }
106
-
107
- /**
108
- * Validate environment for codex execution
109
- * @returns {Object} Validation result { valid: boolean, errors: string[] }
110
- */
111
- function validateEnvironment() {
112
- const errors = [];
113
-
114
- // Check for codex binary
115
- try {
116
- execFileSync('which', ['codex'], { stdio: 'pipe' });
117
- } catch (error) {
118
- // Try common installation paths
119
- const commonPaths = [
120
- '/usr/local/bin/codex',
121
- path.join(os.homedir(), '.local', 'bin', 'codex'),
122
- path.join(os.homedir(), 'bin', 'codex'),
123
- ];
124
-
125
- const codexExists = commonPaths.some(p => fs.existsSync(p));
126
- if (!codexExists) {
127
- errors.push('codex binary not found in PATH or common locations');
128
- }
129
- }
130
-
131
- // OPENAI_API_KEY/CODEX_API_KEY are optional when codex has stored auth from `codex login`.
132
- if (!process.env.OPENAI_API_KEY && !process.env.CODEX_API_KEY) {
133
- console.error(
134
- '[codex-wrapper] Note: no OPENAI_API_KEY/CODEX_API_KEY set, relying on stored codex login or ChatGPT auth'
135
- );
136
- }
137
-
138
- return {
139
- valid: errors.length === 0,
140
- errors,
141
- };
142
- }
143
-
144
- /**
145
- * Build codex command array
146
- * @param {Object} options - Command options
147
- * @returns {Object} Command structure { binary: string, args: string[] }
148
- */
149
- function buildCommand(options) {
150
- const args = ['exec', '--ephemeral'];
151
-
152
- // Approval mode (default: normal, --full-auto: automatic execution)
153
- if (options.fullAuto) {
154
- args.push('--full-auto');
155
- }
156
-
157
- // JSON output
158
- if (options.json) {
159
- args.push('--json');
160
- }
161
-
162
- // Model selection
163
- if (options.model) {
164
- args.push('--model', options.model);
165
- }
166
-
167
- // Working directory
168
- if (options.workingDir) {
169
- args.push('-C', options.workingDir);
170
- }
171
-
172
- // Reasoning effort (maps to -c model_reasoning_effort="value")
173
- if (options.effort) {
174
- const validEfforts = ['minimal', 'low', 'medium', 'high', 'xhigh'];
175
- if (validEfforts.includes(options.effort)) {
176
- args.push('-c', `model_reasoning_effort="${options.effort}"`);
177
- } else {
178
- process.stderr.write(`Warning: Invalid effort level "${options.effort}". Valid: ${validEfforts.join(', ')}\n`);
179
- }
180
- }
181
-
182
- // Add prompt as last argument
183
- args.push(options.prompt);
184
-
185
- return {
186
- binary: 'codex',
187
- args,
188
- };
189
- }
190
-
191
- /**
192
- * Execute codex command
193
- * @param {string} binary - Binary to execute
194
- * @param {string[]} args - Command arguments
195
- * @param {number} timeout - Timeout in milliseconds
196
- * @param {string|null} workingDir - Working directory
197
- * @returns {Promise<Object>} Execution result
198
- */
199
- function executeCodex(binary, args, timeout, workingDir = null) {
200
- return new Promise((resolve) => {
201
- const startTime = Date.now();
202
- let stdout = '';
203
- let stderr = '';
204
- let timedOut = false;
205
-
206
- const spawnOptions = {
207
- cwd: workingDir || process.cwd(),
208
- env: process.env,
209
- stdio: ['ignore', 'pipe', 'pipe'],
210
- };
211
-
212
- const child = spawn(binary, args, spawnOptions);
213
-
214
- // Collect output
215
- child.stdout.on('data', (data) => {
216
- stdout += data.toString();
217
- });
218
-
219
- child.stderr.on('data', (data) => {
220
- stderr += data.toString();
221
- });
222
-
223
- // Set timeout
224
- const timeoutHandle = setTimeout(() => {
225
- timedOut = true;
226
- console.error('[codex-wrapper] Timeout reached, terminating process...', { file: 'stderr' });
227
-
228
- // Graceful termination attempt
229
- child.kill('SIGTERM');
230
-
231
- // Force kill after grace period
232
- setTimeout(() => {
233
- if (!child.killed) {
234
- console.error('[codex-wrapper] Force killing process...', { file: 'stderr' });
235
- child.kill('SIGKILL');
236
- }
237
- }, KILL_GRACE_PERIOD_MS);
238
- }, timeout);
239
-
240
- // Handle process exit
241
- child.on('close', (exitCode) => {
242
- clearTimeout(timeoutHandle);
243
- const durationMs = Date.now() - startTime;
244
-
245
- resolve({
246
- exitCode: exitCode !== null ? exitCode : 1,
247
- stdout,
248
- stderr,
249
- timedOut,
250
- durationMs,
251
- });
252
- });
253
-
254
- // Handle spawn errors
255
- child.on('error', (error) => {
256
- clearTimeout(timeoutHandle);
257
- const durationMs = Date.now() - startTime;
258
-
259
- resolve({
260
- exitCode: 1,
261
- stdout,
262
- stderr: stderr + '\nSpawn error: ' + error.message,
263
- timedOut: false,
264
- durationMs,
265
- });
266
- });
267
- });
268
- }
269
-
270
- /**
271
- * Parse JSON Lines output from codex
272
- * @param {string} output - Raw output string
273
- * @returns {Object} Parsed result { events: object[], finalMessage: string|null, parseErrors: string[] }
274
- */
275
- function parseJsonLines(output) {
276
- const lines = output.split('\n').filter(line => line.trim().length > 0);
277
- const events = [];
278
- const parseErrors = [];
279
- let finalMessage = null;
280
-
281
- for (const line of lines) {
282
- try {
283
- const event = JSON.parse(line);
284
- events.push(event);
285
-
286
- // Codex CLI v0.99.0 format: item.completed events with agent_message type
287
- if (event.type === 'item.completed' && event.item) {
288
- if (event.item.type === 'agent_message' && event.item.text) {
289
- finalMessage = event.item.text;
290
- }
291
- }
292
- // Look for assistant message in various event structures (fallback for future API changes)
293
- else if (event.type === 'assistant_message' && event.content) {
294
- finalMessage = event.content;
295
- } else if (event.message && event.message.role === 'assistant') {
296
- finalMessage = event.message.content || event.message.text;
297
- } else if (event.role === 'assistant' && event.content) {
298
- finalMessage = event.content;
299
- }
300
- } catch (error) {
301
- parseErrors.push(`Failed to parse line: ${error.message}`);
302
- }
303
- }
304
-
305
- return {
306
- events,
307
- finalMessage,
308
- parseErrors,
309
- };
310
- }
311
-
312
- /**
313
- * Main execution function
314
- */
315
- async function main() {
316
- const args = parseArgs();
317
-
318
- // Validate required arguments
319
- if (!args.prompt) {
320
- const result = {
321
- success: false,
322
- error: 'Missing required argument: --prompt',
323
- exit_code: 2,
324
- };
325
- console.log(JSON.stringify(result, null, 2));
326
- process.exit(2);
327
- }
328
-
329
- // Validate environment
330
- const validation = validateEnvironment();
331
- if (!validation.valid) {
332
- const result = {
333
- success: false,
334
- error: 'Environment validation failed',
335
- validation_errors: validation.errors,
336
- exit_code: 2,
337
- };
338
- console.log(JSON.stringify(result, null, 2));
339
- process.exit(2);
340
- }
341
-
342
- console.error(`[codex-wrapper] Executing codex with timeout: ${args.timeout}ms`);
343
- if (args.workingDir) {
344
- console.error(`[codex-wrapper] Working directory: ${args.workingDir}`);
345
- }
346
-
347
- // Build command
348
- const command = buildCommand(args);
349
- console.error(`[codex-wrapper] Command: ${command.binary} ${command.args.join(' ')}`);
350
-
351
- // Execute
352
- const execResult = await executeCodex(
353
- command.binary,
354
- command.args,
355
- args.timeout,
356
- args.workingDir
357
- );
358
-
359
- // Process result
360
- let output = null;
361
- let eventsCount = 0;
362
-
363
- if (args.json && execResult.stdout) {
364
- const parsed = parseJsonLines(execResult.stdout);
365
- eventsCount = parsed.events.length;
366
- output = parsed.finalMessage;
367
-
368
- if (parsed.parseErrors.length > 0) {
369
- console.error('[codex-wrapper] JSON parse errors:', parsed.parseErrors.join('; '));
370
- }
371
- } else {
372
- output = execResult.stdout.trim();
373
- }
374
-
375
- // Determine success
376
- const success = execResult.exitCode === 0 && !execResult.timedOut;
377
-
378
- // Build result object
379
- const result = {
380
- success,
381
- duration_ms: execResult.durationMs,
382
- exit_code: execResult.exitCode,
383
- };
384
-
385
- if (success) {
386
- result.output = output || execResult.stdout;
387
- result.model = args.model || 'o3';
388
- if (args.json) {
389
- result.events_count = eventsCount;
390
- }
391
- } else {
392
- if (execResult.timedOut) {
393
- result.error = `Execution timed out after ${args.timeout}ms`;
394
- } else {
395
- result.error = 'Execution failed';
396
- }
397
- if (execResult.stderr) {
398
- result.stderr = execResult.stderr.trim();
399
- }
400
- }
401
-
402
- // Write output file if requested
403
- if (args.output && output) {
404
- try {
405
- const outputDir = path.dirname(args.output);
406
- if (!fs.existsSync(outputDir)) {
407
- fs.mkdirSync(outputDir, { recursive: true });
408
- }
409
- fs.writeFileSync(args.output, output, 'utf-8');
410
- console.error(`[codex-wrapper] Output written to: ${args.output}`);
411
- } catch (error) {
412
- console.error(`[codex-wrapper] Failed to write output file: ${error.message}`);
413
- result.output_file_error = error.message;
414
- }
415
- }
416
-
417
- // Output JSON result to stdout
418
- console.log(JSON.stringify(result, null, 2));
419
-
420
- process.exit(result.exit_code);
421
- }
422
-
423
- // Run
424
- main().catch(error => {
425
- const result = {
426
- success: false,
427
- error: 'Unexpected error: ' + error.message,
428
- stack: error.stack,
429
- exit_code: 1,
430
- };
431
- console.log(JSON.stringify(result, null, 2));
432
- process.exit(1);
433
- });