bead 0.1.0__py3-none-any.whl

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 (231) hide show
  1. bead/__init__.py +11 -0
  2. bead/__main__.py +11 -0
  3. bead/active_learning/__init__.py +15 -0
  4. bead/active_learning/config.py +231 -0
  5. bead/active_learning/loop.py +566 -0
  6. bead/active_learning/models/__init__.py +24 -0
  7. bead/active_learning/models/base.py +852 -0
  8. bead/active_learning/models/binary.py +910 -0
  9. bead/active_learning/models/categorical.py +943 -0
  10. bead/active_learning/models/cloze.py +862 -0
  11. bead/active_learning/models/forced_choice.py +956 -0
  12. bead/active_learning/models/free_text.py +773 -0
  13. bead/active_learning/models/lora.py +365 -0
  14. bead/active_learning/models/magnitude.py +835 -0
  15. bead/active_learning/models/multi_select.py +795 -0
  16. bead/active_learning/models/ordinal_scale.py +811 -0
  17. bead/active_learning/models/peft_adapter.py +155 -0
  18. bead/active_learning/models/random_effects.py +639 -0
  19. bead/active_learning/selection.py +354 -0
  20. bead/active_learning/strategies.py +391 -0
  21. bead/active_learning/trainers/__init__.py +26 -0
  22. bead/active_learning/trainers/base.py +210 -0
  23. bead/active_learning/trainers/data_collator.py +172 -0
  24. bead/active_learning/trainers/dataset_utils.py +261 -0
  25. bead/active_learning/trainers/huggingface.py +304 -0
  26. bead/active_learning/trainers/lightning.py +324 -0
  27. bead/active_learning/trainers/metrics.py +424 -0
  28. bead/active_learning/trainers/mixed_effects.py +551 -0
  29. bead/active_learning/trainers/model_wrapper.py +509 -0
  30. bead/active_learning/trainers/registry.py +104 -0
  31. bead/adapters/__init__.py +11 -0
  32. bead/adapters/huggingface.py +61 -0
  33. bead/behavioral/__init__.py +116 -0
  34. bead/behavioral/analytics.py +646 -0
  35. bead/behavioral/extraction.py +343 -0
  36. bead/behavioral/merging.py +343 -0
  37. bead/cli/__init__.py +11 -0
  38. bead/cli/active_learning.py +513 -0
  39. bead/cli/active_learning_commands.py +779 -0
  40. bead/cli/completion.py +359 -0
  41. bead/cli/config.py +624 -0
  42. bead/cli/constraint_builders.py +286 -0
  43. bead/cli/deployment.py +859 -0
  44. bead/cli/deployment_trials.py +493 -0
  45. bead/cli/deployment_ui.py +332 -0
  46. bead/cli/display.py +378 -0
  47. bead/cli/items.py +960 -0
  48. bead/cli/items_factories.py +776 -0
  49. bead/cli/list_constraints.py +714 -0
  50. bead/cli/lists.py +490 -0
  51. bead/cli/main.py +430 -0
  52. bead/cli/models.py +877 -0
  53. bead/cli/resource_loaders.py +621 -0
  54. bead/cli/resources.py +1036 -0
  55. bead/cli/shell.py +356 -0
  56. bead/cli/simulate.py +840 -0
  57. bead/cli/templates.py +1158 -0
  58. bead/cli/training.py +1080 -0
  59. bead/cli/utils.py +614 -0
  60. bead/cli/workflow.py +1273 -0
  61. bead/config/__init__.py +68 -0
  62. bead/config/active_learning.py +1009 -0
  63. bead/config/config.py +192 -0
  64. bead/config/defaults.py +118 -0
  65. bead/config/deployment.py +217 -0
  66. bead/config/env.py +147 -0
  67. bead/config/item.py +45 -0
  68. bead/config/list.py +193 -0
  69. bead/config/loader.py +149 -0
  70. bead/config/logging.py +42 -0
  71. bead/config/model.py +49 -0
  72. bead/config/paths.py +46 -0
  73. bead/config/profiles.py +320 -0
  74. bead/config/resources.py +47 -0
  75. bead/config/serialization.py +210 -0
  76. bead/config/simulation.py +206 -0
  77. bead/config/template.py +238 -0
  78. bead/config/validation.py +267 -0
  79. bead/data/__init__.py +65 -0
  80. bead/data/base.py +87 -0
  81. bead/data/identifiers.py +97 -0
  82. bead/data/language_codes.py +61 -0
  83. bead/data/metadata.py +270 -0
  84. bead/data/range.py +123 -0
  85. bead/data/repository.py +358 -0
  86. bead/data/serialization.py +249 -0
  87. bead/data/timestamps.py +89 -0
  88. bead/data/validation.py +349 -0
  89. bead/data_collection/__init__.py +11 -0
  90. bead/data_collection/jatos.py +223 -0
  91. bead/data_collection/merger.py +154 -0
  92. bead/data_collection/prolific.py +198 -0
  93. bead/deployment/__init__.py +5 -0
  94. bead/deployment/distribution.py +402 -0
  95. bead/deployment/jatos/__init__.py +1 -0
  96. bead/deployment/jatos/api.py +200 -0
  97. bead/deployment/jatos/exporter.py +210 -0
  98. bead/deployment/jspsych/__init__.py +9 -0
  99. bead/deployment/jspsych/biome.json +44 -0
  100. bead/deployment/jspsych/config.py +411 -0
  101. bead/deployment/jspsych/generator.py +598 -0
  102. bead/deployment/jspsych/package.json +51 -0
  103. bead/deployment/jspsych/pnpm-lock.yaml +2141 -0
  104. bead/deployment/jspsych/randomizer.py +299 -0
  105. bead/deployment/jspsych/src/lib/list-distributor.test.ts +327 -0
  106. bead/deployment/jspsych/src/lib/list-distributor.ts +1282 -0
  107. bead/deployment/jspsych/src/lib/randomizer.test.ts +232 -0
  108. bead/deployment/jspsych/src/lib/randomizer.ts +367 -0
  109. bead/deployment/jspsych/src/plugins/cloze-dropdown.ts +252 -0
  110. bead/deployment/jspsych/src/plugins/forced-choice.ts +265 -0
  111. bead/deployment/jspsych/src/plugins/plugins.test.ts +141 -0
  112. bead/deployment/jspsych/src/plugins/rating.ts +248 -0
  113. bead/deployment/jspsych/src/slopit/index.ts +9 -0
  114. bead/deployment/jspsych/src/types/jatos.d.ts +256 -0
  115. bead/deployment/jspsych/src/types/jspsych.d.ts +228 -0
  116. bead/deployment/jspsych/templates/experiment.css +1 -0
  117. bead/deployment/jspsych/templates/experiment.js.template +289 -0
  118. bead/deployment/jspsych/templates/index.html +51 -0
  119. bead/deployment/jspsych/templates/randomizer.js +241 -0
  120. bead/deployment/jspsych/templates/randomizer.js.template +313 -0
  121. bead/deployment/jspsych/trials.py +723 -0
  122. bead/deployment/jspsych/tsconfig.json +23 -0
  123. bead/deployment/jspsych/tsup.config.ts +30 -0
  124. bead/deployment/jspsych/ui/__init__.py +1 -0
  125. bead/deployment/jspsych/ui/components.py +383 -0
  126. bead/deployment/jspsych/ui/styles.py +411 -0
  127. bead/dsl/__init__.py +80 -0
  128. bead/dsl/ast.py +168 -0
  129. bead/dsl/context.py +178 -0
  130. bead/dsl/errors.py +71 -0
  131. bead/dsl/evaluator.py +570 -0
  132. bead/dsl/grammar.lark +81 -0
  133. bead/dsl/parser.py +231 -0
  134. bead/dsl/stdlib.py +929 -0
  135. bead/evaluation/__init__.py +13 -0
  136. bead/evaluation/convergence.py +485 -0
  137. bead/evaluation/interannotator.py +398 -0
  138. bead/items/__init__.py +40 -0
  139. bead/items/adapters/__init__.py +70 -0
  140. bead/items/adapters/anthropic.py +224 -0
  141. bead/items/adapters/api_utils.py +167 -0
  142. bead/items/adapters/base.py +216 -0
  143. bead/items/adapters/google.py +259 -0
  144. bead/items/adapters/huggingface.py +1074 -0
  145. bead/items/adapters/openai.py +323 -0
  146. bead/items/adapters/registry.py +202 -0
  147. bead/items/adapters/sentence_transformers.py +224 -0
  148. bead/items/adapters/togetherai.py +309 -0
  149. bead/items/binary.py +515 -0
  150. bead/items/cache.py +558 -0
  151. bead/items/categorical.py +593 -0
  152. bead/items/cloze.py +757 -0
  153. bead/items/constructor.py +784 -0
  154. bead/items/forced_choice.py +413 -0
  155. bead/items/free_text.py +681 -0
  156. bead/items/generation.py +432 -0
  157. bead/items/item.py +396 -0
  158. bead/items/item_template.py +787 -0
  159. bead/items/magnitude.py +573 -0
  160. bead/items/multi_select.py +621 -0
  161. bead/items/ordinal_scale.py +569 -0
  162. bead/items/scoring.py +448 -0
  163. bead/items/validation.py +723 -0
  164. bead/lists/__init__.py +30 -0
  165. bead/lists/balancer.py +263 -0
  166. bead/lists/constraints.py +1067 -0
  167. bead/lists/experiment_list.py +286 -0
  168. bead/lists/list_collection.py +378 -0
  169. bead/lists/partitioner.py +1141 -0
  170. bead/lists/stratification.py +254 -0
  171. bead/participants/__init__.py +73 -0
  172. bead/participants/collection.py +699 -0
  173. bead/participants/merging.py +312 -0
  174. bead/participants/metadata_spec.py +491 -0
  175. bead/participants/models.py +276 -0
  176. bead/resources/__init__.py +29 -0
  177. bead/resources/adapters/__init__.py +19 -0
  178. bead/resources/adapters/base.py +104 -0
  179. bead/resources/adapters/cache.py +128 -0
  180. bead/resources/adapters/glazing.py +508 -0
  181. bead/resources/adapters/registry.py +117 -0
  182. bead/resources/adapters/unimorph.py +796 -0
  183. bead/resources/classification.py +856 -0
  184. bead/resources/constraint_builders.py +329 -0
  185. bead/resources/constraints.py +165 -0
  186. bead/resources/lexical_item.py +223 -0
  187. bead/resources/lexicon.py +744 -0
  188. bead/resources/loaders.py +209 -0
  189. bead/resources/template.py +441 -0
  190. bead/resources/template_collection.py +707 -0
  191. bead/resources/template_generation.py +349 -0
  192. bead/simulation/__init__.py +29 -0
  193. bead/simulation/annotators/__init__.py +15 -0
  194. bead/simulation/annotators/base.py +175 -0
  195. bead/simulation/annotators/distance_based.py +135 -0
  196. bead/simulation/annotators/lm_based.py +114 -0
  197. bead/simulation/annotators/oracle.py +182 -0
  198. bead/simulation/annotators/random.py +181 -0
  199. bead/simulation/dsl_extension/__init__.py +3 -0
  200. bead/simulation/noise_models/__init__.py +13 -0
  201. bead/simulation/noise_models/base.py +42 -0
  202. bead/simulation/noise_models/random_noise.py +82 -0
  203. bead/simulation/noise_models/systematic.py +132 -0
  204. bead/simulation/noise_models/temperature.py +86 -0
  205. bead/simulation/runner.py +144 -0
  206. bead/simulation/strategies/__init__.py +23 -0
  207. bead/simulation/strategies/base.py +123 -0
  208. bead/simulation/strategies/binary.py +103 -0
  209. bead/simulation/strategies/categorical.py +123 -0
  210. bead/simulation/strategies/cloze.py +224 -0
  211. bead/simulation/strategies/forced_choice.py +127 -0
  212. bead/simulation/strategies/free_text.py +105 -0
  213. bead/simulation/strategies/magnitude.py +116 -0
  214. bead/simulation/strategies/multi_select.py +129 -0
  215. bead/simulation/strategies/ordinal_scale.py +131 -0
  216. bead/templates/__init__.py +27 -0
  217. bead/templates/adapters/__init__.py +17 -0
  218. bead/templates/adapters/base.py +128 -0
  219. bead/templates/adapters/cache.py +178 -0
  220. bead/templates/adapters/huggingface.py +312 -0
  221. bead/templates/combinatorics.py +103 -0
  222. bead/templates/filler.py +605 -0
  223. bead/templates/renderers.py +177 -0
  224. bead/templates/resolver.py +178 -0
  225. bead/templates/strategies.py +1806 -0
  226. bead/templates/streaming.py +195 -0
  227. bead-0.1.0.dist-info/METADATA +212 -0
  228. bead-0.1.0.dist-info/RECORD +231 -0
  229. bead-0.1.0.dist-info/WHEEL +4 -0
  230. bead-0.1.0.dist-info/entry_points.txt +2 -0
  231. bead-0.1.0.dist-info/licenses/LICENSE +21 -0
bead/cli/completion.py ADDED
@@ -0,0 +1,359 @@
1
+ """Shell completion generators for the bead CLI.
2
+
3
+ This module provides commands for generating and installing shell completion
4
+ scripts for bash, zsh, and fish shells.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ import click
15
+ from rich.console import Console
16
+
17
+ from bead.cli.utils import print_error, print_info, print_success
18
+
19
+ console = Console()
20
+
21
+
22
+ @click.group()
23
+ def completion() -> None:
24
+ """Generate shell completion scripts.
25
+
26
+ Provides tab completion for bead commands in bash, zsh, and fish shells.
27
+
28
+ Examples
29
+ --------
30
+ Generate bash completion:
31
+ $ bead completion bash > ~/.bash_completion.d/bead
32
+
33
+ Generate zsh completion:
34
+ $ bead completion zsh > ~/.zsh/completion/_bead
35
+
36
+ Generate fish completion:
37
+ $ bead completion fish > ~/.config/fish/completions/bead.fish
38
+
39
+ Auto-install for current shell:
40
+ $ bead completion install
41
+ """
42
+
43
+
44
+ @completion.command()
45
+ def bash() -> None:
46
+ """Generate bash completion script.
47
+
48
+ Outputs a bash completion script that can be sourced in your shell
49
+ configuration or installed to /etc/bash_completion.d/.
50
+
51
+ Examples
52
+ --------
53
+ Install for current user:
54
+ $ bead completion bash > ~/.bash_completion.d/bead
55
+ $ source ~/.bash_completion.d/bead
56
+
57
+ Install system-wide (requires sudo):
58
+ $ sudo bead completion bash > /etc/bash_completion.d/bead
59
+
60
+ Add to .bashrc:
61
+ $ bead completion bash >> ~/.bashrc
62
+ """
63
+ # Generate bash completion using Click's built-in support
64
+ completion_script = """
65
+ # Bash completion for bead
66
+ # Generated by bead completion bash
67
+
68
+ _bead_completion() {
69
+ local IFS=$'\\n'
70
+ local response
71
+
72
+ response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \\
73
+ _BEAD_COMPLETE=bash_complete bead 2>/dev/null)
74
+
75
+ for completion in $response; do
76
+ IFS=',' read type value <<< "$completion"
77
+
78
+ if [[ $type == 'dir' ]]; then
79
+ COMPREPLY=()
80
+ compopt -o dirnames
81
+ elif [[ $type == 'file' ]]; then
82
+ COMPREPLY=()
83
+ compopt -o default
84
+ elif [[ $type == 'plain' ]]; then
85
+ COMPREPLY+=($value)
86
+ fi
87
+ done
88
+
89
+ return 0
90
+ }
91
+
92
+ complete -o nosort -F _bead_completion bead
93
+ """
94
+ click.echo(completion_script)
95
+
96
+ # Print hints to stderr
97
+ sys.stderr.write("\n# Bash completion script generated\n")
98
+ sys.stderr.write(
99
+ "# Install with: bead completion bash > ~/.bash_completion.d/bead\n"
100
+ )
101
+
102
+
103
+ @completion.command()
104
+ def zsh() -> None:
105
+ """Generate zsh completion script.
106
+
107
+ Outputs a zsh completion script that can be placed in your $fpath.
108
+
109
+ Examples
110
+ --------
111
+ Install for current user:
112
+ $ mkdir -p ~/.zsh/completion
113
+ $ bead completion zsh > ~/.zsh/completion/_bead
114
+
115
+ Add to .zshrc if not already present:
116
+ $ echo 'fpath=(~/.zsh/completion $fpath)' >> ~/.zshrc
117
+ $ echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
118
+
119
+ System-wide install (requires sudo):
120
+ $ sudo bead completion zsh > /usr/share/zsh/site-functions/_bead
121
+ """
122
+ # Generate zsh completion using Click's built-in support
123
+ completion_script = """#compdef bead
124
+
125
+ # Zsh completion for bead
126
+ # Generated by bead completion zsh
127
+
128
+ _bead() {
129
+ local -a completions
130
+ local -a completions_with_descriptions
131
+ local -a response
132
+ (( ! $+commands[bead] )) && return 1
133
+
134
+ response=("${(@f)$(env COMP_WORDS="${words[*]}" \\
135
+ COMP_CWORD=$((CURRENT-1)) _BEAD_COMPLETE=zsh_complete bead 2>/dev/null)}")
136
+
137
+ for type key descr in ${response}; do
138
+ if [[ "$type" == "plain" ]]; then
139
+ if [[ "$descr" == "_" ]]; then
140
+ completions+=("$key")
141
+ else
142
+ completions_with_descriptions+=("$key":"$descr")
143
+ fi
144
+ elif [[ "$type" == "dir" ]]; then
145
+ _path_files -/
146
+ elif [[ "$type" == "file" ]]; then
147
+ _path_files -f
148
+ fi
149
+ done
150
+
151
+ if [ -n "$completions_with_descriptions" ]; then
152
+ _describe -V unsorted completions_with_descriptions -U
153
+ fi
154
+
155
+ if [ -n "$completions" ]; then
156
+ compadd -U -V unsorted -a completions
157
+ fi
158
+ }
159
+
160
+ compdef _bead bead
161
+ """
162
+ click.echo(completion_script)
163
+
164
+ # Print hints to stderr
165
+ sys.stderr.write("\n# Zsh completion script generated\n")
166
+ sys.stderr.write("# Install with: bead completion zsh > ~/.zsh/completion/_bead\n")
167
+
168
+
169
+ @completion.command()
170
+ def fish() -> None:
171
+ """Generate fish completion script.
172
+
173
+ Outputs a fish completion script for the fish shell.
174
+
175
+ Examples
176
+ --------
177
+ Install for current user:
178
+ $ bead completion fish > ~/.config/fish/completions/bead.fish
179
+
180
+ Reload completions:
181
+ $ source ~/.config/fish/completions/bead.fish
182
+ """
183
+ # Generate fish completion using Click's built-in support
184
+ completion_script = """# Fish completion for bead
185
+ # Generated by bead completion fish
186
+
187
+ function __fish_bead_complete
188
+ set -lx COMP_WORDS (commandline -opc)
189
+ set -lx COMP_CWORD (count $COMP_WORDS)
190
+ set -lx _BEAD_COMPLETE fish_complete
191
+ bead 2>/dev/null
192
+ end
193
+
194
+ complete -c bead -f -a "(__fish_bead_complete)"
195
+ """
196
+ click.echo(completion_script)
197
+
198
+ # Print hints to stderr
199
+ sys.stderr.write("\n# Fish completion script generated\n")
200
+ sys.stderr.write(
201
+ "# Install with: bead completion fish > ~/.config/fish/completions/bead.fish\n"
202
+ )
203
+
204
+
205
+ @completion.command()
206
+ def install() -> None:
207
+ """Auto-detect shell and install completion.
208
+
209
+ Detects the current shell from the SHELL environment variable and
210
+ installs the appropriate completion script automatically.
211
+
212
+ Examples
213
+ --------
214
+ Auto-install for current shell:
215
+ $ bead completion install
216
+
217
+ This command will:
218
+ 1. Detect your current shell (bash, zsh, or fish)
219
+ 2. Generate the appropriate completion script
220
+ 3. Install it to the standard location
221
+ 4. Update your shell configuration if needed
222
+ """
223
+ # Detect shell
224
+ shell_path = os.environ.get("SHELL", "")
225
+ shell_name = Path(shell_path).name if shell_path else ""
226
+
227
+ if not shell_name:
228
+ print_error("Could not detect shell from $SHELL environment variable")
229
+ print_info("Use specific commands: bead completion bash|zsh|fish")
230
+ sys.exit(1)
231
+
232
+ console.print(f"[cyan]Detected shell: {shell_name}[/cyan]")
233
+
234
+ try:
235
+ if shell_name == "bash":
236
+ _install_bash()
237
+ elif shell_name == "zsh":
238
+ _install_zsh()
239
+ elif shell_name == "fish":
240
+ _install_fish()
241
+ else:
242
+ print_error(f"Unsupported shell: {shell_name}")
243
+ print_info("Supported shells: bash, zsh, fish")
244
+ sys.exit(1)
245
+ except Exception as e:
246
+ print_error(f"Installation failed: {e}")
247
+ print_info("Try manual installation with: bead completion " + shell_name)
248
+ sys.exit(1)
249
+
250
+
251
+ def _install_bash() -> None:
252
+ """Install bash completion script."""
253
+ home = Path.home()
254
+ completion_dir = home / ".bash_completion.d"
255
+ completion_file = completion_dir / "bead"
256
+
257
+ # Create directory if needed
258
+ completion_dir.mkdir(parents=True, exist_ok=True)
259
+
260
+ # Generate and write completion script
261
+ result = subprocess.run(
262
+ ["bead", "completion", "bash"],
263
+ capture_output=True,
264
+ text=True,
265
+ check=True,
266
+ )
267
+
268
+ completion_file.write_text(result.stdout)
269
+ print_success(f"Installed bash completion: {completion_file}")
270
+
271
+ # Check if sourced in .bashrc
272
+ bashrc = home / ".bashrc"
273
+ if bashrc.exists():
274
+ bashrc_content = bashrc.read_text()
275
+ source_line = f"source {completion_file}"
276
+
277
+ if source_line not in bashrc_content:
278
+ console.print("\n[yellow]Add to .bashrc:[/yellow]")
279
+ console.print(f" echo '{source_line}' >> ~/.bashrc")
280
+ console.print(" source ~/.bashrc")
281
+ else:
282
+ print_info("Already sourced in .bashrc")
283
+ else:
284
+ console.print("\n[yellow]Add to .bashrc:[/yellow]")
285
+ console.print(f" echo 'source {completion_file}' >> ~/.bashrc")
286
+ console.print(" source ~/.bashrc")
287
+
288
+
289
+ def _install_zsh() -> None:
290
+ """Install zsh completion script."""
291
+ home = Path.home()
292
+ completion_dir = home / ".zsh" / "completion"
293
+ completion_file = completion_dir / "_bead"
294
+
295
+ # Create directory if needed
296
+ completion_dir.mkdir(parents=True, exist_ok=True)
297
+
298
+ # Generate and write completion script
299
+ result = subprocess.run(
300
+ ["bead", "completion", "zsh"],
301
+ capture_output=True,
302
+ text=True,
303
+ check=True,
304
+ )
305
+
306
+ completion_file.write_text(result.stdout)
307
+ print_success(f"Installed zsh completion: {completion_file}")
308
+
309
+ # Check if fpath configured in .zshrc
310
+ zshrc = home / ".zshrc"
311
+ if zshrc.exists():
312
+ zshrc_content = zshrc.read_text()
313
+ fpath_line = "fpath=(~/.zsh/completion $fpath)"
314
+ compinit_line = "autoload -Uz compinit && compinit"
315
+
316
+ needs_update = False
317
+ if fpath_line not in zshrc_content:
318
+ console.print("\n[yellow]Add to .zshrc:[/yellow]")
319
+ console.print(f" echo '{fpath_line}' >> ~/.zshrc")
320
+ needs_update = True
321
+
322
+ if compinit_line not in zshrc_content:
323
+ console.print(f" echo '{compinit_line}' >> ~/.zshrc")
324
+ needs_update = True
325
+
326
+ if needs_update:
327
+ console.print(" source ~/.zshrc")
328
+ else:
329
+ print_info("Already configured in .zshrc")
330
+ else:
331
+ console.print("\n[yellow]Add to .zshrc:[/yellow]")
332
+ console.print(" echo 'fpath=(~/.zsh/completion $fpath)' >> ~/.zshrc")
333
+ console.print(" echo 'autoload -Uz compinit && compinit' >> ~/.zshrc")
334
+ console.print(" source ~/.zshrc")
335
+
336
+
337
+ def _install_fish() -> None:
338
+ """Install fish completion script."""
339
+ home = Path.home()
340
+ completion_dir = home / ".config" / "fish" / "completions"
341
+ completion_file = completion_dir / "bead.fish"
342
+
343
+ # Create directory if needed
344
+ completion_dir.mkdir(parents=True, exist_ok=True)
345
+
346
+ # Generate and write completion script
347
+ result = subprocess.run(
348
+ ["bead", "completion", "fish"],
349
+ capture_output=True,
350
+ text=True,
351
+ check=True,
352
+ )
353
+
354
+ completion_file.write_text(result.stdout)
355
+ print_success(f"Installed fish completion: {completion_file}")
356
+
357
+ console.print("\n[yellow]Reload completions:[/yellow]")
358
+ console.print(f" source {completion_file}")
359
+ print_info("Or restart your shell")