@orchid-labs/pluxx 0.1.18 → 0.1.19

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/dist/cli/index.js CHANGED
@@ -24681,6 +24681,247 @@ if [[ -f "${installDirVariable}/scripts/bootstrap-runtime.sh" ]]; then
24681
24681
  fi
24682
24682
  `;
24683
24683
  }
24684
+ function renderInstallerCodexPluginHooksSnippet(installDirVariable) {
24685
+ return `
24686
+ export PLUXX_INSTALL_DIR="${installDirVariable}"
24687
+
24688
+ set +e
24689
+ node <<'NODE'
24690
+ const fs = require('fs')
24691
+ const path = require('path')
24692
+
24693
+ const installDir = process.env.PLUXX_INSTALL_DIR
24694
+ if (!installDir) process.exit(0)
24695
+
24696
+ const manifestPath = path.join(installDir, '.codex-plugin/plugin.json')
24697
+ const standardHooksPath = path.join(installDir, 'hooks/hooks.json')
24698
+ let hasPluginHooks = fs.existsSync(standardHooksPath)
24699
+
24700
+ if (fs.existsSync(manifestPath)) {
24701
+ try {
24702
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
24703
+ const manifestHooks = manifest.hooks
24704
+ hasPluginHooks ||= typeof manifestHooks === 'string' && manifestHooks.trim().length > 0
24705
+ hasPluginHooks ||= Array.isArray(manifestHooks) && manifestHooks.length > 0
24706
+ hasPluginHooks ||= manifestHooks && typeof manifestHooks === 'object' && Object.keys(manifestHooks).length > 0
24707
+ hasPluginHooks ||= manifestHooks === true
24708
+ } catch {}
24709
+ }
24710
+
24711
+ process.exit(hasPluginHooks ? 0 : 2)
24712
+ NODE
24713
+ PLUXX_CODEX_BUNDLE_HAS_HOOKS="$?"
24714
+ set -e
24715
+
24716
+ if [[ "$PLUXX_CODEX_BUNDLE_HAS_HOOKS" == "0" ]]; then
24717
+ CODEX_HOME_DIR="\${CODEX_HOME:-$HOME/.codex}"
24718
+ CODEX_CONFIG_PATH="\${PLUXX_CODEX_CONFIG_PATH:-$CODEX_HOME_DIR/config.toml}"
24719
+ PLUXX_CODEX_HOOKS_MODE="\${PLUXX_CODEX_ENABLE_PLUGIN_HOOKS:-prompt}"
24720
+
24721
+ export CODEX_CONFIG_PATH
24722
+ if node <<'NODE'
24723
+ const fs = require('fs')
24724
+ const filepath = process.env.CODEX_CONFIG_PATH
24725
+
24726
+ function stripTomlComment(line) {
24727
+ let quote = null
24728
+ let escaped = false
24729
+ for (let index = 0; index < line.length; index += 1) {
24730
+ const char = line[index]
24731
+ if (escaped) {
24732
+ escaped = false
24733
+ continue
24734
+ }
24735
+ if (quote && char === '\\\\') {
24736
+ escaped = true
24737
+ continue
24738
+ }
24739
+ if (char === '"' || char === "'") {
24740
+ quote = quote === char ? null : quote || char
24741
+ continue
24742
+ }
24743
+ if (!quote && char === '#') return line.slice(0, index)
24744
+ }
24745
+ return line
24746
+ }
24747
+
24748
+ function isTomlTrue(rawValue) {
24749
+ return /^true\\b/i.test(rawValue.trim())
24750
+ }
24751
+
24752
+ let text = ''
24753
+ try {
24754
+ text = fs.readFileSync(filepath, 'utf8')
24755
+ } catch {
24756
+ process.exit(1)
24757
+ }
24758
+ const lines = text.split(/\\r?\\n/)
24759
+ let tableName = ''
24760
+ for (const line of lines) {
24761
+ const trimmed = stripTomlComment(line).trim()
24762
+ if (!trimmed) continue
24763
+ const tableMatch = trimmed.match(/^\\[([^\\]]+)\\]$/)
24764
+ if (tableMatch) {
24765
+ tableName = tableMatch[1].trim()
24766
+ continue
24767
+ }
24768
+ if (tableName === '') {
24769
+ const dottedMatch = trimmed.match(/^features\\.plugin_hooks\\s*=\\s*(.+)$/)
24770
+ if (dottedMatch && isTomlTrue(dottedMatch[1])) process.exit(0)
24771
+ const inlineMatch = trimmed.match(/^features\\s*=\\s*(.+)$/)
24772
+ if (inlineMatch && /\\bplugin_hooks\\s*=\\s*true\\b/i.test(inlineMatch[1])) process.exit(0)
24773
+ }
24774
+ if (tableName !== 'features') continue
24775
+ const match = trimmed.match(/^plugin_hooks\\s*=\\s*(.+)$/)
24776
+ if (match && isTomlTrue(match[1])) process.exit(0)
24777
+ }
24778
+ process.exit(1)
24779
+ NODE
24780
+ then
24781
+ echo "Codex plugin-bundled hooks already enabled in $CODEX_CONFIG_PATH."
24782
+ else
24783
+ PLUXX_ENABLE_CODEX_HOOKS="0"
24784
+ case "$PLUXX_CODEX_HOOKS_MODE" in
24785
+ 1|true|TRUE|yes|YES|always|ALWAYS)
24786
+ PLUXX_ENABLE_CODEX_HOOKS="1"
24787
+ ;;
24788
+ 0|false|FALSE|no|NO|never|NEVER|skip|SKIP)
24789
+ PLUXX_ENABLE_CODEX_HOOKS="0"
24790
+ ;;
24791
+ *)
24792
+ if [[ -r /dev/tty ]]; then
24793
+ echo "This Codex plugin bundle includes startup hooks." >/dev/tty
24794
+ echo "Codex requires [features].plugin_hooks = true before plugin-bundled hooks can run." >/dev/tty
24795
+ read -r -p "Enable Codex plugin-bundled hooks in $CODEX_CONFIG_PATH now? [Y/n] " PLUXX_CODEX_HOOKS_REPLY </dev/tty
24796
+ case "$PLUXX_CODEX_HOOKS_REPLY" in
24797
+ n|N|no|NO)
24798
+ PLUXX_ENABLE_CODEX_HOOKS="0"
24799
+ ;;
24800
+ *)
24801
+ PLUXX_ENABLE_CODEX_HOOKS="1"
24802
+ ;;
24803
+ esac
24804
+ fi
24805
+ ;;
24806
+ esac
24807
+
24808
+ if [[ "$PLUXX_ENABLE_CODEX_HOOKS" == "1" ]]; then
24809
+ mkdir -p "$(dirname "$CODEX_CONFIG_PATH")"
24810
+ node <<'NODE'
24811
+ const fs = require('fs')
24812
+ const path = require('path')
24813
+
24814
+ const filepath = process.env.CODEX_CONFIG_PATH
24815
+
24816
+ function stripTomlComment(line) {
24817
+ let quote = null
24818
+ let escaped = false
24819
+ for (let index = 0; index < line.length; index += 1) {
24820
+ const char = line[index]
24821
+ if (escaped) {
24822
+ escaped = false
24823
+ continue
24824
+ }
24825
+ if (quote && char === '\\\\') {
24826
+ escaped = true
24827
+ continue
24828
+ }
24829
+ if (char === '"' || char === "'") {
24830
+ quote = quote === char ? null : quote || char
24831
+ continue
24832
+ }
24833
+ if (!quote && char === '#') return line.slice(0, index)
24834
+ }
24835
+ return line
24836
+ }
24837
+
24838
+ let text = ''
24839
+ try {
24840
+ text = fs.readFileSync(filepath, 'utf8')
24841
+ } catch {}
24842
+
24843
+ const lines = text.split(/\\r?\\n/)
24844
+ if (lines.length === 1 && lines[0] === '') lines.pop()
24845
+
24846
+ let start = -1
24847
+ let end = lines.length
24848
+ let firstTopLevelFeaturesDotted = -1
24849
+ let topLevelPluginHooksDotted = -1
24850
+ let topLevelInlineFeatures = -1
24851
+ let tableName = ''
24852
+ for (let index = 0; index < lines.length; index += 1) {
24853
+ const trimmed = stripTomlComment(lines[index]).trim()
24854
+ const tableMatch = trimmed.match(/^\\[([^\\]]+)\\]$/)
24855
+ if (tableMatch) tableName = tableMatch[1].trim()
24856
+
24857
+ if (trimmed === '[features]') {
24858
+ start = index
24859
+ break
24860
+ }
24861
+
24862
+ if (tableName === '') {
24863
+ if (/^features\\.[A-Za-z0-9_-]+\\s*=/.test(trimmed) && firstTopLevelFeaturesDotted < 0) {
24864
+ firstTopLevelFeaturesDotted = index
24865
+ }
24866
+ if (/^features\\.plugin_hooks\\s*=/.test(trimmed)) {
24867
+ topLevelPluginHooksDotted = index
24868
+ }
24869
+ if (/^features\\s*=\\s*\\{/.test(trimmed)) {
24870
+ topLevelInlineFeatures = index
24871
+ }
24872
+ }
24873
+ }
24874
+
24875
+ if (start >= 0) {
24876
+ for (let index = start + 1; index < lines.length; index += 1) {
24877
+ if (/^\\s*\\[[^\\]]+\\]/.test(stripTomlComment(lines[index]))) {
24878
+ end = index
24879
+ break
24880
+ }
24881
+ }
24882
+
24883
+ let updated = false
24884
+ for (let index = start + 1; index < end; index += 1) {
24885
+ if (/^plugin_hooks\\s*=/.test(stripTomlComment(lines[index]).trim())) {
24886
+ lines[index] = 'plugin_hooks = true'
24887
+ updated = true
24888
+ }
24889
+ }
24890
+ if (!updated) lines.splice(start + 1, 0, 'plugin_hooks = true')
24891
+ } else if (topLevelPluginHooksDotted >= 0) {
24892
+ lines[topLevelPluginHooksDotted] = 'features.plugin_hooks = true'
24893
+ } else if (firstTopLevelFeaturesDotted >= 0) {
24894
+ lines.splice(firstTopLevelFeaturesDotted + 1, 0, 'features.plugin_hooks = true')
24895
+ } else if (topLevelInlineFeatures >= 0 && lines[topLevelInlineFeatures].includes('}')) {
24896
+ if (/\\bplugin_hooks\\s*=/.test(lines[topLevelInlineFeatures])) {
24897
+ lines[topLevelInlineFeatures] = lines[topLevelInlineFeatures].replace(
24898
+ /\\bplugin_hooks\\s*=\\s*(true|false)\\b/i,
24899
+ 'plugin_hooks = true',
24900
+ )
24901
+ } else {
24902
+ lines[topLevelInlineFeatures] = lines[topLevelInlineFeatures].replace(/}/, ', plugin_hooks = true }')
24903
+ }
24904
+ } else {
24905
+ if (lines.length > 0 && lines[lines.length - 1] !== '') lines.push('')
24906
+ lines.push('[features]', 'plugin_hooks = true')
24907
+ }
24908
+
24909
+ fs.mkdirSync(path.dirname(filepath), { recursive: true })
24910
+ fs.writeFileSync(filepath, lines.join('\\n') + '\\n')
24911
+ NODE
24912
+ echo "Enabled Codex plugin-bundled hooks in $CODEX_CONFIG_PATH."
24913
+ echo "Restart or refresh Codex before relying on plugin startup hooks."
24914
+ else
24915
+ echo "Codex plugin-bundled hooks are not enabled. Startup hooks from this plugin will not run until you add this to $CODEX_CONFIG_PATH:" >&2
24916
+ echo "[features]" >&2
24917
+ echo "plugin_hooks = true" >&2
24918
+ echo "Then restart or refresh Codex before relying on plugin startup hooks." >&2
24919
+ echo "Set PLUXX_CODEX_ENABLE_PLUGIN_HOOKS=1 before running this installer to enable it noninteractively." >&2
24920
+ fi
24921
+ fi
24922
+ fi
24923
+ `;
24924
+ }
24684
24925
  function renderInstallClaudeCodeScript(config) {
24685
24926
  return `#!/usr/bin/env bash
24686
24927
  set -euo pipefail
@@ -24902,6 +25143,7 @@ cp -R "$BUNDLE_DIR" "$INSTALL_DIR"
24902
25143
  ${renderInstallerUserConfigSnippet(config, "codex", "$INSTALL_DIR")}
24903
25144
  ${renderInstallerMcpPathMaterializationSnippet("codex", "$INSTALL_DIR")}
24904
25145
  ${renderInstallerRuntimeBootstrapSnippet("$INSTALL_DIR")}
25146
+ ${renderInstallerCodexPluginHooksSnippet("$INSTALL_DIR")}
24905
25147
 
24906
25148
  mkdir -p "$(dirname "$MARKETPLACE_PATH")"
24907
25149
 
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/cli/publish.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/cli/publish.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -9675,6 +9675,247 @@ if [[ -f "${installDirVariable}/scripts/bootstrap-runtime.sh" ]]; then
9675
9675
  fi
9676
9676
  `;
9677
9677
  }
9678
+ function renderInstallerCodexPluginHooksSnippet(installDirVariable) {
9679
+ return `
9680
+ export PLUXX_INSTALL_DIR="${installDirVariable}"
9681
+
9682
+ set +e
9683
+ node <<'NODE'
9684
+ const fs = require('fs')
9685
+ const path = require('path')
9686
+
9687
+ const installDir = process.env.PLUXX_INSTALL_DIR
9688
+ if (!installDir) process.exit(0)
9689
+
9690
+ const manifestPath = path.join(installDir, '.codex-plugin/plugin.json')
9691
+ const standardHooksPath = path.join(installDir, 'hooks/hooks.json')
9692
+ let hasPluginHooks = fs.existsSync(standardHooksPath)
9693
+
9694
+ if (fs.existsSync(manifestPath)) {
9695
+ try {
9696
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
9697
+ const manifestHooks = manifest.hooks
9698
+ hasPluginHooks ||= typeof manifestHooks === 'string' && manifestHooks.trim().length > 0
9699
+ hasPluginHooks ||= Array.isArray(manifestHooks) && manifestHooks.length > 0
9700
+ hasPluginHooks ||= manifestHooks && typeof manifestHooks === 'object' && Object.keys(manifestHooks).length > 0
9701
+ hasPluginHooks ||= manifestHooks === true
9702
+ } catch {}
9703
+ }
9704
+
9705
+ process.exit(hasPluginHooks ? 0 : 2)
9706
+ NODE
9707
+ PLUXX_CODEX_BUNDLE_HAS_HOOKS="$?"
9708
+ set -e
9709
+
9710
+ if [[ "$PLUXX_CODEX_BUNDLE_HAS_HOOKS" == "0" ]]; then
9711
+ CODEX_HOME_DIR="\${CODEX_HOME:-$HOME/.codex}"
9712
+ CODEX_CONFIG_PATH="\${PLUXX_CODEX_CONFIG_PATH:-$CODEX_HOME_DIR/config.toml}"
9713
+ PLUXX_CODEX_HOOKS_MODE="\${PLUXX_CODEX_ENABLE_PLUGIN_HOOKS:-prompt}"
9714
+
9715
+ export CODEX_CONFIG_PATH
9716
+ if node <<'NODE'
9717
+ const fs = require('fs')
9718
+ const filepath = process.env.CODEX_CONFIG_PATH
9719
+
9720
+ function stripTomlComment(line) {
9721
+ let quote = null
9722
+ let escaped = false
9723
+ for (let index = 0; index < line.length; index += 1) {
9724
+ const char = line[index]
9725
+ if (escaped) {
9726
+ escaped = false
9727
+ continue
9728
+ }
9729
+ if (quote && char === '\\\\') {
9730
+ escaped = true
9731
+ continue
9732
+ }
9733
+ if (char === '"' || char === "'") {
9734
+ quote = quote === char ? null : quote || char
9735
+ continue
9736
+ }
9737
+ if (!quote && char === '#') return line.slice(0, index)
9738
+ }
9739
+ return line
9740
+ }
9741
+
9742
+ function isTomlTrue(rawValue) {
9743
+ return /^true\\b/i.test(rawValue.trim())
9744
+ }
9745
+
9746
+ let text = ''
9747
+ try {
9748
+ text = fs.readFileSync(filepath, 'utf8')
9749
+ } catch {
9750
+ process.exit(1)
9751
+ }
9752
+ const lines = text.split(/\\r?\\n/)
9753
+ let tableName = ''
9754
+ for (const line of lines) {
9755
+ const trimmed = stripTomlComment(line).trim()
9756
+ if (!trimmed) continue
9757
+ const tableMatch = trimmed.match(/^\\[([^\\]]+)\\]$/)
9758
+ if (tableMatch) {
9759
+ tableName = tableMatch[1].trim()
9760
+ continue
9761
+ }
9762
+ if (tableName === '') {
9763
+ const dottedMatch = trimmed.match(/^features\\.plugin_hooks\\s*=\\s*(.+)$/)
9764
+ if (dottedMatch && isTomlTrue(dottedMatch[1])) process.exit(0)
9765
+ const inlineMatch = trimmed.match(/^features\\s*=\\s*(.+)$/)
9766
+ if (inlineMatch && /\\bplugin_hooks\\s*=\\s*true\\b/i.test(inlineMatch[1])) process.exit(0)
9767
+ }
9768
+ if (tableName !== 'features') continue
9769
+ const match = trimmed.match(/^plugin_hooks\\s*=\\s*(.+)$/)
9770
+ if (match && isTomlTrue(match[1])) process.exit(0)
9771
+ }
9772
+ process.exit(1)
9773
+ NODE
9774
+ then
9775
+ echo "Codex plugin-bundled hooks already enabled in $CODEX_CONFIG_PATH."
9776
+ else
9777
+ PLUXX_ENABLE_CODEX_HOOKS="0"
9778
+ case "$PLUXX_CODEX_HOOKS_MODE" in
9779
+ 1|true|TRUE|yes|YES|always|ALWAYS)
9780
+ PLUXX_ENABLE_CODEX_HOOKS="1"
9781
+ ;;
9782
+ 0|false|FALSE|no|NO|never|NEVER|skip|SKIP)
9783
+ PLUXX_ENABLE_CODEX_HOOKS="0"
9784
+ ;;
9785
+ *)
9786
+ if [[ -r /dev/tty ]]; then
9787
+ echo "This Codex plugin bundle includes startup hooks." >/dev/tty
9788
+ echo "Codex requires [features].plugin_hooks = true before plugin-bundled hooks can run." >/dev/tty
9789
+ read -r -p "Enable Codex plugin-bundled hooks in $CODEX_CONFIG_PATH now? [Y/n] " PLUXX_CODEX_HOOKS_REPLY </dev/tty
9790
+ case "$PLUXX_CODEX_HOOKS_REPLY" in
9791
+ n|N|no|NO)
9792
+ PLUXX_ENABLE_CODEX_HOOKS="0"
9793
+ ;;
9794
+ *)
9795
+ PLUXX_ENABLE_CODEX_HOOKS="1"
9796
+ ;;
9797
+ esac
9798
+ fi
9799
+ ;;
9800
+ esac
9801
+
9802
+ if [[ "$PLUXX_ENABLE_CODEX_HOOKS" == "1" ]]; then
9803
+ mkdir -p "$(dirname "$CODEX_CONFIG_PATH")"
9804
+ node <<'NODE'
9805
+ const fs = require('fs')
9806
+ const path = require('path')
9807
+
9808
+ const filepath = process.env.CODEX_CONFIG_PATH
9809
+
9810
+ function stripTomlComment(line) {
9811
+ let quote = null
9812
+ let escaped = false
9813
+ for (let index = 0; index < line.length; index += 1) {
9814
+ const char = line[index]
9815
+ if (escaped) {
9816
+ escaped = false
9817
+ continue
9818
+ }
9819
+ if (quote && char === '\\\\') {
9820
+ escaped = true
9821
+ continue
9822
+ }
9823
+ if (char === '"' || char === "'") {
9824
+ quote = quote === char ? null : quote || char
9825
+ continue
9826
+ }
9827
+ if (!quote && char === '#') return line.slice(0, index)
9828
+ }
9829
+ return line
9830
+ }
9831
+
9832
+ let text = ''
9833
+ try {
9834
+ text = fs.readFileSync(filepath, 'utf8')
9835
+ } catch {}
9836
+
9837
+ const lines = text.split(/\\r?\\n/)
9838
+ if (lines.length === 1 && lines[0] === '') lines.pop()
9839
+
9840
+ let start = -1
9841
+ let end = lines.length
9842
+ let firstTopLevelFeaturesDotted = -1
9843
+ let topLevelPluginHooksDotted = -1
9844
+ let topLevelInlineFeatures = -1
9845
+ let tableName = ''
9846
+ for (let index = 0; index < lines.length; index += 1) {
9847
+ const trimmed = stripTomlComment(lines[index]).trim()
9848
+ const tableMatch = trimmed.match(/^\\[([^\\]]+)\\]$/)
9849
+ if (tableMatch) tableName = tableMatch[1].trim()
9850
+
9851
+ if (trimmed === '[features]') {
9852
+ start = index
9853
+ break
9854
+ }
9855
+
9856
+ if (tableName === '') {
9857
+ if (/^features\\.[A-Za-z0-9_-]+\\s*=/.test(trimmed) && firstTopLevelFeaturesDotted < 0) {
9858
+ firstTopLevelFeaturesDotted = index
9859
+ }
9860
+ if (/^features\\.plugin_hooks\\s*=/.test(trimmed)) {
9861
+ topLevelPluginHooksDotted = index
9862
+ }
9863
+ if (/^features\\s*=\\s*\\{/.test(trimmed)) {
9864
+ topLevelInlineFeatures = index
9865
+ }
9866
+ }
9867
+ }
9868
+
9869
+ if (start >= 0) {
9870
+ for (let index = start + 1; index < lines.length; index += 1) {
9871
+ if (/^\\s*\\[[^\\]]+\\]/.test(stripTomlComment(lines[index]))) {
9872
+ end = index
9873
+ break
9874
+ }
9875
+ }
9876
+
9877
+ let updated = false
9878
+ for (let index = start + 1; index < end; index += 1) {
9879
+ if (/^plugin_hooks\\s*=/.test(stripTomlComment(lines[index]).trim())) {
9880
+ lines[index] = 'plugin_hooks = true'
9881
+ updated = true
9882
+ }
9883
+ }
9884
+ if (!updated) lines.splice(start + 1, 0, 'plugin_hooks = true')
9885
+ } else if (topLevelPluginHooksDotted >= 0) {
9886
+ lines[topLevelPluginHooksDotted] = 'features.plugin_hooks = true'
9887
+ } else if (firstTopLevelFeaturesDotted >= 0) {
9888
+ lines.splice(firstTopLevelFeaturesDotted + 1, 0, 'features.plugin_hooks = true')
9889
+ } else if (topLevelInlineFeatures >= 0 && lines[topLevelInlineFeatures].includes('}')) {
9890
+ if (/\\bplugin_hooks\\s*=/.test(lines[topLevelInlineFeatures])) {
9891
+ lines[topLevelInlineFeatures] = lines[topLevelInlineFeatures].replace(
9892
+ /\\bplugin_hooks\\s*=\\s*(true|false)\\b/i,
9893
+ 'plugin_hooks = true',
9894
+ )
9895
+ } else {
9896
+ lines[topLevelInlineFeatures] = lines[topLevelInlineFeatures].replace(/}/, ', plugin_hooks = true }')
9897
+ }
9898
+ } else {
9899
+ if (lines.length > 0 && lines[lines.length - 1] !== '') lines.push('')
9900
+ lines.push('[features]', 'plugin_hooks = true')
9901
+ }
9902
+
9903
+ fs.mkdirSync(path.dirname(filepath), { recursive: true })
9904
+ fs.writeFileSync(filepath, lines.join('\\n') + '\\n')
9905
+ NODE
9906
+ echo "Enabled Codex plugin-bundled hooks in $CODEX_CONFIG_PATH."
9907
+ echo "Restart or refresh Codex before relying on plugin startup hooks."
9908
+ else
9909
+ echo "Codex plugin-bundled hooks are not enabled. Startup hooks from this plugin will not run until you add this to $CODEX_CONFIG_PATH:" >&2
9910
+ echo "[features]" >&2
9911
+ echo "plugin_hooks = true" >&2
9912
+ echo "Then restart or refresh Codex before relying on plugin startup hooks." >&2
9913
+ echo "Set PLUXX_CODEX_ENABLE_PLUGIN_HOOKS=1 before running this installer to enable it noninteractively." >&2
9914
+ fi
9915
+ fi
9916
+ fi
9917
+ `;
9918
+ }
9678
9919
  function renderInstallClaudeCodeScript(config) {
9679
9920
  return `#!/usr/bin/env bash
9680
9921
  set -euo pipefail
@@ -9896,6 +10137,7 @@ cp -R "$BUNDLE_DIR" "$INSTALL_DIR"
9896
10137
  ${renderInstallerUserConfigSnippet(config, "codex", "$INSTALL_DIR")}
9897
10138
  ${renderInstallerMcpPathMaterializationSnippet("codex", "$INSTALL_DIR")}
9898
10139
  ${renderInstallerRuntimeBootstrapSnippet("$INSTALL_DIR")}
10140
+ ${renderInstallerCodexPluginHooksSnippet("$INSTALL_DIR")}
9899
10141
 
9900
10142
  mkdir -p "$(dirname "$MARKETPLACE_PATH")"
9901
10143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchid-labs/pluxx",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Build AI agent plugins once. Prime-time on Claude Code, Cursor, Codex, and OpenCode, with beta generators for additional hosts.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",