return whether a path is a sub-path
Return whether a directory is a subdirectory of another directory
test package to figure out subdir usage
Automatically load AGENTS.md context from subdirectories in pi coding agent
> read all 'component.json' in subdir, merging 'static' property
An output formatter for markdownlint-cli2 that produces the same output as markdownlint-cli
Check if a directory is a subdirectory
Convert objects into directory structures and back again
Get an array of recursive directory contents
MCP Server para o core protegido do Claude Agents Ecosystem v1.4.6 — 54 agentes especializados. v1.4.6 RELEASE MAJOR: 13 issues consolidadas (255-267) — Spawn fixes + Pacote A Modularidade (file-size-guard, executor-file-scope, agents.md per-subdir, templ
No description provided.
MCP Server para busca semântica na Knowledge Base do Claude Agents v1.4.6 (ChromaDB + FastEmbed BGE-small-en-v1.5 — ADR-001). v1.1.6 alinha com release MAJOR v1.4.6 — Code Hygiene (Pacote A: file-size-guard, executor-file-scope, agents.md per-subdir; Paco
A lib for parsing the URL of Git Repositories.
A node.js git library with some cool features like synchronizing all the branches from a git remote to a local subdir
Quokka plugin to resolve imports from subdirectories modules, while in scratch mode.
Copy the preload file during build to subdir for custom tiles
An output formatter for markdownlint-cli2 that summarizes the results
Tap plugin to provide t.testdir() and t.fixture()
Fill in the package details in `package.json`; replace `<@scope/package>` and `<binary>`. Make the binary using either the `Makefile` or through `Xcode`. Once the binary is located in a subdir of `_DerivedData`, then `make dist`. It should then be ready t
helper functions for Node.js
Fill in the package details in `package.json`; replace `<@scope/package>` and `<binary>`. Make the binary using either the `Makefile` or through `Xcode`. Once the binary is located in a subdir of `_DerivedData`, then `make dist`. It should then be ready t
Filter which files your browserify transforms will affect using glob patterns
Fill in the package details in `package.json`; replace `<@scope/package>` and `<binary>`. Make the binary using either the `Makefile` or through `Xcode`. Once the binary is located in a subdir of `_DerivedData`, then `make dist`. It should then be ready t
use to remove baidu yun temp files at current directly and it`s subdir files
Recursively remove empty sub-directories
A program for backup & restoration of btrfs subvolumes.
Clone any sub-directory of a Github repository to your local machine.
Command-line interface for OSS Porter: A tool to extract and sync projects from internal to public Git repositories.
A (dot)file manager
A complete, zero-dependency implementation of the XDG Base Directory Specification (v0.8) for Linux and Unix systems.
CLI that ingests Claude Code transcript JSONL files into a DuckDB database for usage / cost analysis.
Easy and fast project switching in your shell!
A gentle digital gravedigger to lovingly archive your old files.
Helper program to build and install c-like libraries
Provide convenient methods for path operations
Fast, secure & intelligent local backup tool with AES-256 encryption and Zstd compression
a capistrano strategy to deploy subdir with copy strategy.
Rucursive gem invocation
Capistrano tasks for deploying one or more Ruby on Rails apps from within subdirectories of a repository
Introduce Capistrano::Deploy::Strategy::CopySubdir
This program takes an MP3 audiobook and renames the subdirs and files using a consistent naming convention. It also updates the ID3v1 and ID3v2 tags for artist, album, track number, track title, genre, and year.
Enscapsulates directory naming scheme for carrierwave's file storage, enabling creation of directory structure that will be efficient and survive subdirs limit.
Just type "railsversions" in your console to see which apps use which version of Rails. The apps have to be located at /u/apps and may be deployed via capistrano (i.e. having a "current" subdir).
Command-line tool that automatises photo/video uploads to Flickr. Entering 'flickru <directory>' in your command line, any photos under 'directory' (and subdirs) are uploaded to your Flickr account (interactively entered the first time you start flickru). Photos are identified by case-insensitive extensions: GIF, JPEG, JPG, PNG, and TIFF. Videos are identified by case-insensitive extensions: AVI, MPEG, and MPG. flickru automatically sets the following Flickr metadata: (1) date taken: file last-modification time, unless JPEG/TIFF Exif metadatum 'date_time_original' is found (Flickr understands it natively). (2) privacy policy: private, visible by friends & family, hidden for public searches (3) safety level: safe (4) permissions: friends & family can add comments to the photo and its notes; nobody can add notes and tags to the photo (5) description: for videos longer than 90s (Flickr's longest allowed duration) but shorter than 500MB (Flickr's maximum permisible size), it will contain an annotation about its large duration. (6) title: extracted from the parent directory name (7) geolocation & accuracy: extracted from the parent directory name, unless JPEG/TIFF Exif GPS metadata is found (Flickr understands them natively). Before uploading photos, please, make sure that you have correctly named each photos parent directory according to the name format 'TITLE[@LOCATION[#PRECISION]]', where: (1) TITLE is the desired title for the photos stored in the directory. If no LOCATION is given, flickru tries to extract the location from Wikipedia page TITLE. (2) LOCATION is the location of the photos, specified as: (a) the Wikipedia page name (whitespaces allowed) of the location or (b) its coordinates LATITUDE,LONGITUDE (3) PRECISION is the Flickr geolocation precision. Flickru sets it to one of the following case insentitive literals: 'street', 'city', 'region', 'country', 'world'. Photos are classified into photosets. If the photoset does not exist, flickru creates it. This photoset is named after its grandparent directory. The photoset is arranged by 'date taken' (older first). To see some examples on the directory structure recognised by flickru, please explore the subdirectories under 'var/ts'. GitHub : http://github.com/jesuspv/flickru RubyGems: http://rubygems.org/gems/flickru
== Confidently Build Terminal Apps Rooibos[https://rooibos.run] helps you build interactive terminal applications. Keep your code understandable and testable as it scales. Rooibos handles keyboard, mouse, and async work so you can focus on behavior and user experience. gem install rooibos <i>Currently in beta. APIs may change before 1.0.</i> === Get Started in Seconds rooibos new my_app cd my_app rooibos run That's it. You have a working app with keyboard navigation, mouse support, and clickable buttons. Open <tt>lib/my_app.rb</tt> to make it your own. --- === The Pattern \Rooibos uses Model-View-Update, the architecture behind Elm[https://guide.elm-lang.org/architecture/], Redux[https://redux.js.org/], and {Bubble Tea}[https://github.com/charmbracelet/bubbletea]. State lives in one place. Updates flow in one direction. The runtime handles rendering and runs background work for you. --- === Hello, MVU The simplest \Rooibos app. Press any key to increment the counter. Press <tt>Ctrl</tt>+<tt>C</tt> to quit. require "rooibos" module Counter # Init: How do you create the initial model? Init = -> { 0 } # View: What does the user see? View = -> (model, tui) { tui.paragraph(text: <<~END) } Current count: #{model}. Press any key to increment. Press Ctrl+C to quit. END # Update: What happens when things change? Update = -> (message, model) { if message.ctrl_c? Rooibos::Command.exit elsif message.key? model + 1 end } end Rooibos.run(Counter) That's the whole pattern: Model holds state, Init creates it, View renders it, and Update changes it. The runtime handles everything else. --- === Your First Real Application A file browser in sixty lines. It opens files, navigates directories, handles errors, styles directories and hidden files differently, and supports vim-style keyboard shortcuts. If you can do this much with this little code, imagine how easy _your_ app will be to build. require "rooibos" module FileBrowser # Model: What state does your app need? Model = Data.define(:path, :entries, :selected, :error) Init = -> { path = Dir.pwd entries = Entries[path] Ractor.make_shareable( # Ensures thread safety Model.new(path:, entries:, selected: entries.first, error: nil)) } View = -> (model, tui) { tui.block( titles: [model.error || model.path, { content: KEYS, position: :bottom, alignment: :right}], borders: [:all], border_style: if model.error then tui.style(fg: :red) else nil end, children: [tui.list(items: model.entries.map(&ListItem[model, tui]), selected_index: model.entries.index(model.selected), highlight_symbol: "", highlight_style: tui.style(modifiers: [:reversed]))] ) } Update = -> (message, model) { return model.with(error: ERROR) if message.error? model = model.with(error: nil) if model.error && message.key? if message.ctrl_c? || message.q? then Rooibos::Command.exit elsif message.home? || message.g? then model.with(selected: model.entries.first) elsif message.end? || message.G? then model.with(selected: model.entries.last) elsif message.up_arrow? || message.k? then Select[:-, model] elsif message.down_arrow? || message.j? then Select[:+, model] elsif message.enter? then Open[model] elsif message.escape? then Navigate[File.dirname(model.path), model] end } private # Lines below this are implementation details KEYS = "↑/↓/Home/End: Select | Enter: Open | Esc: Navigate Up | q: Quit" ERROR = "Sorry, opening the selected file failed." ListItem = -> (model, tui) { -> (name) { modifiers = name.start_with?(".") ? [:dim] : [] fg = :blue if name.end_with?("/") tui.list_item(content: name, style: tui.style(fg:, modifiers:)) } } Select = -> (operator, model) { new_index = model.entries.index(model.selected).public_send(operator, 1) model.with(selected: model.entries[new_index.clamp(0, model.entries.length - 1)]) } Open = -> (model) { full = File.join(model.path, model.selected.delete_suffix("/")) model.selected.end_with?("/") ? Navigate[full, model] : Rooibos::Command.open(full) } Navigate = -> (path, model) { entries = Entries[path] model.with(path:, entries:, selected: entries.first, error: nil) } Entries = -> (path) { Dir.children(path).map { |name| File.directory?(File.join(path, name)) ? "#{name}/" : name }.sort_by { |name| [name.end_with?("/") ? 0 : 1, name.downcase] } } end Rooibos.run(FileBrowser) --- === Batteries Included ==== Commands Applications fetch data, run shell commands, and set timers. \Rooibos Commands run off the main thread and send results back as messages. <b>HTTP requests:</b> Update = -> (message, model) { case message in :fetch_users [model.with(loading: true), Rooibos::Command.http(:get, "/api/users", :got_users)] in { type: :http, envelope: :got_users, status: 200, body: } model.with(loading: false, users: JSON.parse(body)) in { type: :http, envelope: :got_users, status: } model.with(error: "HTTP #{status}") end } <b>Shell commands:</b> Update = -> (message, model) { case message in :list_files Rooibos::Command.system("ls -la", :listed_files) in { type: :system, envelope: :listed_files, stdout:, status: 0 } model.with(files: stdout.lines.map(&:chomp)) in { type: :system, envelope: :listed_files, stderr:, status: } model.with(error: stderr) end } <b>Timers:</b> Update = -> (message, model) { case message in { type: :timer, envelope: :tick, elapsed: } [model.with(frame: model.frame + 1), Rooibos::Command.wait(1.0 / 24, :tick)] end } <b>And more!</b> \Rooibos includes <tt>all</tt>, <tt>batch</tt>, <tt>bubble</tt>, <tt>cancel</tt>, <tt>custom</tt>, <tt>deliver</tt>, <tt>exit</tt>, <tt>http</tt>, <tt>map</tt>, <tt>open</tt>, <tt>system</tt>, <tt>tick</tt>, and <tt>wait</tt> commands. You can also define your own custom commands for complex orchestration. Every command produces a message, and Update handles it the same way. ==== Testing \Rooibos makes TUIs so easy to test, you'll save more time by writing tests than by not testing. <b>Unit test Update, View, and Init.</b> No terminal needed. Test helpers included. def test_moves_selection_down_with_j model = Ractor.make_shareable(FileBrowser::Model.new( path: "/", entries: %w[bin exe lib], selected: "bin", error: nil)) message = RatatuiRuby::Event::Key.new(code: "j") result = FileBrowser::Update.call(message, model) assert_equal "exe", result.selected end <b>Style assertions.</b> Draw to a headless terminal, verify colors and modifiers. def test_directories_are_blue with_test_terminal(60, 10) do model = Ractor.make_shareable(FileBrowser::Model.new( path: "/", entries: %w[file.txt subdir/], selected: "file.txt", error: nil)) widget = FileBrowser::View.call(model, RatatuiRuby::TUI.new) RatatuiRuby.draw { |frame| frame.render_widget(widget, frame.area) } assert_blue(1, 2) # "subdir/" at column 1, row 2 end end <b>System tests.</b> Inject events, run the full app, snapshot the result. def test_selection_moves_down with_test_terminal(120, 30) do Dir.mktmpdir do |dir| FileUtils.touch(File.join(dir, "a")) FileUtils.touch(File.join(dir, "b")) FileUtils.touch(File.join(dir, "c")) inject_key(:down) inject_key(:ctrl_c) # Tests use explicit params to inject deterministic initial state. Rooibos.run( model: Ractor.make_shareable(FileBrowser::Model.new( path: dir, entries: %w[a b c], selected: "a", error: nil)), view: FileBrowser::View, update: FileBrowser::Update ) assert_snapshots("selection_moved_down") do |lines| title = "┌/tmp/test#{'─' * 107}┐" lines.map do |l| l.gsub(/┌#{Regexp.escape(dir)}[^┐]*┐/, title) end end end end end Snapshots record both plain text and ANSI colors. Normalization blocks mask dynamic content (timestamps, temp paths) for cross-platform reproducibility. Run <tt>UPDATE_SNAPSHOTS=1 rake test</tt> to regenerate baselines. ==== Scale Up Large applications decompose into fragments. Each fragment has its own Model, View, Update, and Init. Parents compose children. The pattern scales. The Router DSL eliminates boilerplate: module Dashboard include Rooibos::Router route :stats, to: StatsPanel route :network, to: NetworkPanel receive_events :ctrl_c, -> { Rooibos::Command.exit } only when: -> (_message, model) { !model.modal_open } do receive_events :q, -> { Rooibos::Command.exit } forward_events :s, to: :stats, as: :fetch forward_events :p, to: :network, as: :ping end Update = from_router # ... Model, Init, View below end Declare routes and event handlers. The router generates Update for you. Use guards to ignore messages when needed. ==== CLI The <tt>rooibos</tt> command scaffolds projects and runs applications. rooibos new my_app # Generate project structure rooibos run # Run the app in current directory Generated apps include tests, type signatures, and a working welcome screen with keyboard and mouse support. --- === The Ecosystem \Rooibos builds on RatatuiRuby[https://www.ratatui-ruby.dev], a Rubygem built on Ratatui[https://ratatui.rs]. You get native performance with the joy of Ruby. \Rooibos is one way to manage state and composition. Kit is another. ==== Rooibos[https://www.rooibos.run] Model-View-Update architecture. Inspired by Elm, Bubble Tea, and React + Redux. Your UI is a pure function of state. - Functional programming with MVU - Commands work off the main thread - Messages, not callbacks, drive updates ==== {Kit}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-3-the-object-path--kit] (Coming Soon) Component-based architecture. Encapsulate state, input handling, and rendering in reusable pieces. - OOP with stateful components - Separate UI state from domain logic - Built-in focus management & click handling Both use the same widget library and rendering engine. Pick the paradigm that fits your brain. --- === Links [Get Started] {Getting Started}[https://www.rooibos.run/docs/trunk/doc/getting_started/index_md.html], {Tutorial}[https://www.rooibos.run/docs/trunk/doc/tutorial/index_md.html], {Examples}[https://www.rooibos.run/docs/trunk/examples/app_fractal_dashboard/README_md.html] [Coming From...] {React/Redux}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_react_developers_md.html], {BubbleTea}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_go_developers_md.html], {Textual}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_python_developers_md.html] [Learn More] {Essentials}[https://www.rooibos.run/docs/trunk/doc/essentials/index_md.html], {Scaling Up}[https://www.rooibos.run/docs/trunk/doc/scaling_up/index_md.html], {Best Practices}[https://www.rooibos.run/docs/trunk/doc/best_practices/index_md.html], {Troubleshooting}[https://www.rooibos.run/docs/trunk/doc/troubleshooting/index_md.html] [Community] {Forum}[https://forum.setdef.com/c/rooibos], {Announcements}[https://forum.setdef.com/tags/c/rooibos/announcement], {Bug Tracker}[https://forum.setdef.com/tags/c/rooibos/bug], {Contribution Guide}[https://github.com/setdef/Rooibos/blob/trunk/CONTRIBUTING.md], {Code of Conduct}[https://github.com/setdef/Rooibos/blob/trunk/CODE_OF_CONDUCT.md] --- [Website] https://rooibos.run [Source] https://github.com/setdef/Rooibos [RubyGems] https://rubygems.org/gems/rooibos © 2026 Kerrick Long · Library: LGPL-3.0-or-later · Website: CC-BY-NC-ND-4.0 · Snippets: MIT-0
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.
No description provided.