claude-code-pack 1.0.1 → 1.2.0

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 CHANGED
@@ -88,9 +88,21 @@ Everything is fetched fresh from GitHub, so you always get the latest version.
88
88
 
89
89
  | Setting | Value |
90
90
  |---------|-------|
91
- | **Model** | `opus` |
91
+ | **Model (Claude Code)** | `opus` |
92
+ | **Model (Codex)** | `gpt-5.4` (reasoning effort: `xhigh`) |
92
93
  | **Statusline** | Custom bash script with context window, 5h/7d rate limit bars, and color-coded usage indicators |
93
94
 
95
+ ### Codex Integration
96
+
97
+ The installer also generates a `~/.codex/config.toml` that **reuses the same MCP servers and skills** as Claude Code — no duplicate configuration needed.
98
+
99
+ | What | How it's shared |
100
+ |------|----------------|
101
+ | **MCP servers** | Generated into `config.toml` from the same `mcpServers` in `claude-pack.config.json` |
102
+ | **Skills** | Codex `[[skills.config]]` points to `~/.claude/skills/` (the same directory Claude Code uses) |
103
+
104
+ This means when you add a new MCP server or skill to the pack, both Claude Code and Codex pick it up.
105
+
94
106
  ## Commands
95
107
 
96
108
  ```bash
@@ -103,6 +115,7 @@ claude-pack install --force # Overwrite existing installations
103
115
  claude-pack install --skip-plugins # Skip plugin installation
104
116
  claude-pack install --skip-skills # Skip skill installation
105
117
  claude-pack install --skip-settings # Skip settings merge
118
+ claude-pack install --skip-codex # Skip Codex config generation
106
119
  claude-pack install --dry-run # Preview without changes
107
120
  ```
108
121
 
@@ -11,6 +11,7 @@ const flags = {
11
11
  skipPlugins: args.includes('--skip-plugins'),
12
12
  skipSkills: args.includes('--skip-skills'),
13
13
  skipSettings: args.includes('--skip-settings'),
14
+ skipCodex: args.includes('--skip-codex'),
14
15
  };
15
16
 
16
17
  switch (command) {
@@ -44,5 +45,6 @@ Flags:
44
45
  --skip-plugins Skip plugin installation
45
46
  --skip-skills Skip skill installation
46
47
  --skip-settings Skip settings merge
48
+ --skip-codex Skip Codex config generation
47
49
  `);
48
50
  }
@@ -87,5 +87,12 @@
87
87
  },
88
88
  "assets": {
89
89
  "statusline": "assets/statusline-command.sh"
90
+ },
91
+ "codex": {
92
+ "enabled": true,
93
+ "model": "gpt-5.4",
94
+ "model_reasoning_effort": "xhigh",
95
+ "configPath": "$HOME/.codex/config.toml",
96
+ "skillsPath": "$HOME/.claude/skills"
90
97
  }
91
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-pack",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Portable Claude Code setup — installs plugins, skills, MCP servers, and statusline on any machine",
5
5
  "type": "module",
6
6
  "bin": {
package/src/install.mjs CHANGED
@@ -361,6 +361,7 @@ async function installSettings(config, flags) {
361
361
  }
362
362
 
363
363
  // Merge MCP servers
364
+ let resolvedFsPath = null;
364
365
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
365
366
  console.log('\n🔌 MCP Servers');
366
367
  if (!settings.mcpServers) settings.mcpServers = {};
@@ -377,6 +378,7 @@ async function installSettings(config, flags) {
377
378
  const answer = await prompt(` Use "${home}" as filesystem root? [Y/n/custom path] `);
378
379
 
379
380
  if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
381
+ resolvedFsPath = home;
380
382
  const resolved = JSON.parse(JSON.stringify(serverConfig));
381
383
  resolved.args = resolved.args.map((a) => a === '$HOME' ? home : a);
382
384
  settings.mcpServers[name] = resolved;
@@ -392,6 +394,7 @@ async function installSettings(config, flags) {
392
394
  } else {
393
395
  // User provided a custom path
394
396
  const customPath = answer.replace(/^~/, home);
397
+ resolvedFsPath = customPath;
395
398
  const resolved = JSON.parse(JSON.stringify(serverConfig));
396
399
  resolved.args = resolved.args.map((a) => a === '$HOME' ? customPath : a);
397
400
  settings.mcpServers[name] = resolved;
@@ -413,6 +416,118 @@ async function installSettings(config, flags) {
413
416
  if (!flags.dryRun) {
414
417
  writeJSON(SETTINGS_PATH, settings);
415
418
  }
419
+
420
+ return resolvedFsPath;
421
+ }
422
+
423
+ // ─── Codex Config Generation ───────────────────────────────────────────
424
+
425
+ function tomlValue(val) {
426
+ if (typeof val === 'string') return `"${val}"`;
427
+ if (typeof val === 'boolean') return val ? 'true' : 'false';
428
+ if (typeof val === 'number') return String(val);
429
+ if (Array.isArray(val)) {
430
+ const items = val.map(tomlValue);
431
+ // Use multiline for long arrays
432
+ if (items.join(', ').length > 60) {
433
+ return '[\n ' + items.join(',\n ') + '\n]';
434
+ }
435
+ return '[' + items.join(', ') + ']';
436
+ }
437
+ return `"${val}"`;
438
+ }
439
+
440
+ function generateCodexToml(config, resolvedFsPath) {
441
+ const codex = config.codex;
442
+ const home = homedir();
443
+ const lines = [];
444
+
445
+ lines.push(`model = "${codex.model}"`);
446
+ if (codex.model_reasoning_effort) {
447
+ lines.push(`model_reasoning_effort = "${codex.model_reasoning_effort}"`);
448
+ }
449
+ lines.push('');
450
+
451
+ // Generate MCP server sections from the shared config
452
+ for (const [name, serverConfig] of Object.entries(config.mcpServers || {})) {
453
+ lines.push(`[mcp_servers.${name}]`);
454
+ lines.push('enabled = true');
455
+
456
+ if (serverConfig.type === 'http' || serverConfig.url) {
457
+ lines.push(`url = "${serverConfig.url}"`);
458
+ } else if (serverConfig.command) {
459
+ let command = serverConfig.command;
460
+ const args = serverConfig.args || [];
461
+
462
+ // For npx commands, resolve to full node/npx path for Codex compatibility
463
+ let resolvedArgs = args.map((a) => {
464
+ if (a === '$HOME') return resolvedFsPath || home;
465
+ return a;
466
+ });
467
+
468
+ lines.push(`command = "${command}"`);
469
+ if (resolvedArgs.length > 0) {
470
+ lines.push(`args = ${tomlValue(resolvedArgs)}`);
471
+ }
472
+ }
473
+
474
+ // Environment variables
475
+ if (serverConfig.env && Object.keys(serverConfig.env).length > 0) {
476
+ lines.push('');
477
+ lines.push(`[mcp_servers.${name}.env]`);
478
+ for (const [k, v] of Object.entries(serverConfig.env)) {
479
+ lines.push(`${k} = "${v}"`);
480
+ }
481
+ }
482
+
483
+ lines.push('');
484
+ }
485
+
486
+ // Skills — point to the shared ~/.claude/skills directory
487
+ const skillsPath = (codex.skillsPath || '$HOME/.claude/skills').replace(/\$HOME/g, home);
488
+ lines.push('[[skills.config]]');
489
+ lines.push(`path = "${skillsPath}"`);
490
+ lines.push('enabled = true');
491
+ lines.push('');
492
+
493
+ return lines.join('\n');
494
+ }
495
+
496
+ async function installCodex(config, flags, resolvedFsPath) {
497
+ if (!config.codex?.enabled) return;
498
+
499
+ console.log('\n🤖 Codex');
500
+
501
+ const home = homedir();
502
+ const configPath = (config.codex.configPath || '$HOME/.codex/config.toml').replace(/\$HOME/g, home);
503
+ const configDir = dirname(configPath);
504
+
505
+ if (flags.dryRun) {
506
+ log('○', `Would generate Codex config at ${configPath}`);
507
+ log('○', `Model: ${config.codex.model}`);
508
+ log('○', `Skills path: ~/.claude/skills (shared with Claude Code)`);
509
+ const mcpCount = Object.keys(config.mcpServers || {}).length;
510
+ log('○', `MCP servers: ${mcpCount} (same as Claude Code)`);
511
+ return;
512
+ }
513
+
514
+ const toml = generateCodexToml(config, resolvedFsPath);
515
+ ensureDir(configDir);
516
+
517
+ const exists = existsSync(configPath);
518
+ if (exists && !flags.force) {
519
+ const answer = await prompt(` Codex config exists at ${configPath}. Overwrite? [y/N] `);
520
+ if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
521
+ log('⊘', 'Codex config skipped (use --force to overwrite)');
522
+ return;
523
+ }
524
+ }
525
+
526
+ writeFileSync(configPath, toml);
527
+ log('✓', `Codex config written to ${configPath}`);
528
+ log('✓', `Model: ${config.codex.model}`);
529
+ log('✓', `Skills: ~/.claude/skills (shared with Claude Code)`);
530
+ log('✓', `MCP servers: ${Object.keys(config.mcpServers || {}).length} configured`);
416
531
  }
417
532
 
418
533
  // ─── Main Install ──────────────────────────────────────────────────────
@@ -446,13 +561,19 @@ export async function install(flags = {}) {
446
561
 
447
562
  if (!flags.skipPlugins) installPlugins(config, flags);
448
563
  if (!flags.skipSkills) installSkills(config, flags);
449
- if (!flags.skipSettings) await installSettings(config, flags);
564
+ let resolvedFsPath = null;
565
+ if (!flags.skipSettings) {
566
+ resolvedFsPath = await installSettings(config, flags);
567
+ }
568
+ if (!flags.skipCodex) {
569
+ await installCodex(config, flags, resolvedFsPath);
570
+ }
450
571
 
451
572
  console.log('\n────────────────────────────────────────────');
452
573
  if (flags.dryRun) {
453
574
  console.log(' Dry run complete. Run without --dry-run to apply.');
454
575
  } else {
455
- console.log(' ✓ All done! Restart Claude Code to pick up changes.');
576
+ console.log(' ✓ All done! Restart Claude Code / Codex to pick up changes.');
456
577
  }
457
578
  console.log('');
458
579
  }