[
  {
    "path": ".gitignore",
    "content": "*.gem\n*.rbc\n/.config\n/coverage/\n/InstalledFiles\n/pkg/\n/spec/reports/\n/spec/examples.txt\n/test/tmp/\n/test/version_tmp/\n/tmp/\n\n# Used by dotenv library to load environment variables.\n# .env\n\n# Ignore Byebug command history file.\n.byebug_history\n\n## Specific to RubyMotion:\n.dat*\n.repl_history\nbuild/\n*.bridgesupport\nbuild-iPhoneOS/\nbuild-iPhoneSimulator/\n\n## Specific to RubyMotion (use of CocoaPods):\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# vendor/Pods/\n\n## Documentation cache and generated files:\n/.yardoc/\n/_yardoc/\n/doc/\n/rdoc/\n\n## Environment normalization:\n/.bundle/\n/vendor/bundle\n/lib/bundler/man/\n\n# for a library or gem, you might want to ignore these files since the code is\n# intended to run in multiple environments; otherwise, check them in:\n# Gemfile.lock\n# .ruby-version\n# .ruby-gemset\n\n# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:\n.rvmrc\n\n# Used by RuboCop. Remote config files pulled in from inherit_from directive.\n# .rubocop-https?--*\n\n# Specs\n.rspec_status\n\n# Dev env\nGemfile.lock\n\n# macOS\n.DS_Store\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "require:\n  - rubocop-rspec\n  - rubocop-rake\n\nAllCops:\n  NewCops: enable\n  TargetRubyVersion: 3.0\n\nStyle/StringLiteralsInInterpolation:\n  EnforcedStyle: double_quotes\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# [hackico-ai] Code of Conduct\n\n## Our Pledge\n\nWe, as members, contributors, and leaders of [Your Community Name], pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our community include:\n\n- Demonstrating empathy and kindness toward others.\n- Being respectful of differing opinions, viewpoints, and experiences.\n- Giving and gracefully accepting constructive feedback.\n- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience.\n- Focusing on what is best not just for us as individuals, but for the overall community.\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of any kind.\n- Trolling, insulting or derogatory comments, and personal or political attacks.\n- Public or private harassment.\n- Publishing others' private information, such as a physical or email address, without their explicit permission.\n- Other conduct which could reasonably be considered inappropriate in a professional setting.\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Yuri Gi](https://github.com/yurigitsu), [Marie Giy](https://github.com/yurigitsu). All complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the reporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of actions.\n\n**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nFor answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource 'https://rubygems.org'\n\ngemspec\n\ngem 'rake'\n\n# Spec\ngem 'activerecord'\ngem 'rspec', '~> 3.0'\ngem 'sqlite3'\n\n# Linter & Static\ngem 'fasterer', '~> 0.11.0'\ngem 'rubocop', '~> 1.21'\ngem 'rubocop-rake'\ngem 'rubocop-rspec', require: false\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 hackico.ai\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ![Ruby](icon.svg) hati-command\n\nThe `hati-command` gem provides a lightweight framework for structuring logic as discrete, callable actions — ideal for agentic AI systems that require modular execution and explicit outcome handling.\n\n## What it provides:\n\n- **hati-command** lets you define commands as service objects or interactors, ready for orchestration by AI agents.\n- **hati-command** returns standardized `Success` and `Failure` results, making it easy to reason about next steps in autonomous workflows.\n- **hati-command** provides built-in error tracing and metadata propagation, enabling reliable debugging, observability, and auditability across execution chains.\n- **hati-command** supports integrated transaction handling, allowing commands to execute safely within database or domain-level transactional boundaries.\n\n## Features\n\n- **Command Execution**: Encapsulate atomic operations with clear inputs and outputs.\n- **Structured Results**: Return `Result` objects with status, value, and metadata.\n- **Deterministic Execution**: Enforce explicit execution → outcome flow.\n- **Failure as Data**: Represent errors as explicit results.\n- **Framework-Agnostic Service Objects**: Works with plain `Ruby` or `Rails`.\n- **Execution Transparency**: Make decision points and failure paths visible.\n- **Automation & AI-Ready**: Suitable for orchestration and agent-driven w\n\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Features](#features)\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n  - [Handling Success](#handling-success)\n  - [Handling Failure](#handling-failure)\n  - [Transactional Behavior](#transactional-behavior-fail-fast-with-failure)\n- [Advanced Usage](#advanced-usage)\n\n  - [Result Customization](#result-customization)\n    - [meta](#meta)\n    - [error](#error)\n    - [trace](#trace)\n  - [Native DB Transaction](#native-db-active-record-transaction)\n  - [Command Configurations](#command-configurations)\n    - [result_inference](#result_inference)\n    - [call_as](#call_as)\n    - [failure](#failure)\n    - [fail_fast](#fail_fast)\n    - [unexpected_err](#unexpected_err)\n    - [ar_transaction](#ar_transaction)\n\n- [Development](#development)\n- [Contributing](#contributing)\n- [License](#license)\n- [Code of Conduct](#code-of-conduct)\n\n## Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n```bash\nbundle add hati-command\n```\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n```bash\ngem install hati-command\n```\n\n## Basic Usage\n\nTo use the `hati-command` gem, you can create a command class that includes the `HatiCommand::Cmd` module.\n\nNote: No need to nest object APIs under `private` as popular template for `Servie Object` designs\n\n    only main caller method is public by design\n\n#### Example\n\n```ruby\nrequire 'hati_command'\n\nclass GreetingCommand\n  include HatiCommand::Cmd\n\n  def call(greeting = nil)\n    message = build_greeting(greeting)\n    return message if message.failure?\n\n    process_message(message)\n  end\n\n  def build_greeting(greeting)\n    greeting ? Success(greeting) : Failure(\"No greeting provided\")\n  end\n\n  def process_message(message)\n    message.success? ? Success(message.upcase) : Failure(\"No message provided\")\n  end\nend\n```\n\n### Command `API`\n\n```ruby\nresult = GreetingCommand.call(\"Hello, World!\") # Outputs: Result\nresult = GreetingCommand.new                   # Outputs: private method `new' called\n```\n\n### Handling `Success`\n\n```ruby\nresult = GreetingCommand.call(\"Hello, World!\")\n\nputs result.success? # Outputs: true\nputs result.failure? # Outputs: false\n\nputs result.success  # Outputs: \"HELLO, WORLD!\"\nputs result.failure  # Outputs: nil\n\nputs result.value    # Outputs: \"HELLO, WORLD!\"\nputs result.result   # Outputs: HatiCommand::Success\n```\n\n### Handling `Failure`\n\n```ruby\nresult = GreetingCommand.call\n\nputs result.failure? # Outputs: true\nputs result.success? # Outputs: false\n\nputs result.failure  # Outputs: \"No message provided\"\nputs result.success  # Outputs: nil\n\nputs result.value    # Outputs: \"No message provided\"\nputs result.result   # Outputs: HatiCommand::Failure\n```\n\n### Transactional Behavior: Fail Fast with `Failure!`\n\n```ruby\nclass GreetingCommand\n  include HatiCommand::Cmd\n\n  # NOTE: Will catch unexpected and wrap to HatiCommand::Failure object\n  #       Requires true || ErrorObject\n  command do\n    unexpected_err true\n  end\n\n  def call(params)\n    message = process_message(params[:message])\n    msg = normalize_message(message, params[:recipients])\n\n    Success(msg)\n  end\n\n  # NOTE: No message passed - auto break an execution\n  def process_message(message)\n    message ? message.upcase : Failure!(\"No message provided\")\n  end\n\n  def normalize_message(message, recipients)\n    Failure!(\"No recipients provided\") if recipients.empty?\n\n    recipients.map { |recipient| \"#{recipient}: #{message}\" }\n  end\nend\n```\n\n```ruby\n# NOTE: No message passed - command exited\n#       Returns Result (Failure) object\nresult = GreetingCommand.call\n\nputs result.failure? # Outputs: true\nputs result.failure  # Outputs: \"No message provided\"\nputs result.value    # Outputs: \"No message provided\"\n```\n\n```ruby\n\nresult = GreetingCommand.call(params.merge(message: \"Hello!\"))\n\nputs result.failure? # Outputs: true\nputs result.failure  # Outputs: \"No recipients provided\"\nputs result.value    # Outputs: \"No recipients provided\"\n```\n\n```ruby\nresult = GreetingCommand.call(params.merge(recipients: [\"Alice\", \"Bob\"]))\n\nputs result.failure? # Outputs: false\nputs result.success  # Outputs: true\nputs result.value    # Outputs: [\"Alice: Hello!\", \"Bob: Hello!\"]\n```\n\n## Advanced Usage\n\nConfigurations and customization allow users to tailor the command to meet their specific needs and preferences\n\n### `Result` Customization\n\nHere are some advanced examples of result customization. Available options are\n\n- `meta` - Hash to attach custom metadata\n- `err` - Message or Error access via `error` method\n- `trace` - By design `Failure!` and `unexpected_err` error's stack top entry\n\n### .meta\n\n```ruby\nclass GreetingCommand\n  include HatiCommand::Cmd\n  # ...\n  def process_message(message)\n    Success(message.upcase, meta: { lang: :eng, length: message.length })\n  end\n  # ...\nend\n```\n\n```ruby\nresult = GreetingCommand.(\"Hello, Advanced World!\")\nputs result.value         # Outputs: \"HELLO, ADVANCED WORLD!\"\n\nputs result.meta[:lang]   # Outputs: :eng\nputs result.meta[:length] # Outputs: 22\nputs result.meta          # Outputs: {:lang=>:eng, :length=>22}\n```\n\n### .error\n\n##### set via `err` access via `error` method. Availiable as param for `#Success` as well (ex. partial success)\n\n```ruby\nclass GreetingCommand\n  include HatiCommand::Cmd\n  # ...\n  def process_message(message)\n    Failure(message, err: \"No message provided\")\n  end\nend\n```\n\n```ruby\nresult = GreetingCommand.call\nputs result.value   # Outputs: nil\nputs result.error   # Outputs: \"No message provided\"\nputs result.trace   # Outputs:\n```\n\n### .trace\n\n##### Available as accessor on `Result` object\n\n```ruby\n1| class DoomedCommand\n2|   include HatiCommand::Cmd\n3|\n4|   def call\n5|     Failure!\n6|   end\n7|   # ...\n8| end\n```\n\n```ruby\nresult = GreetingCommand.call\nputs result.failure? # Outputs: true\nputs result.trace    # Outputs: path/to/cmds/doomed_command.rb:5:in `call'\n```\n\n### Command `Configurations`\n\nProvides options for default failure message or errors. Available configs are:\n\n- `result_inference`(Bool(true)) => implicit Result wrapper\n- `call_as`(Symbol[:call]) => Main call method name\n- `failure`(String | ErrorClass) => Message or Error\n- `fail_fast`(String || ErrorClass) => Message or Error\n- `unexpected_err`(Bool[true]) => Message or Error\n\n#### Native DB Active Record Transaction:\n\n- `ar_transaction`(Array[Symbol], returnable: Bool[true]) => methods to wrap in Transaction, requires 'activerecord'\n\n```ruby\nclass AppService\n  include HatiCommand::Cmd\n\n  command do\n    result_inference true\n    call_as :perform\n    failure \"Default Error\"\n    fail_fast \"Default Fail Fast Error\"\n    unexpected_err BaseServiceError\n  end\n\n  # ...\nend\n\nclass PaymentService < AppService\n  command do\n    ar_transaction :perform\n    unexpected_err PaymentServiceTechnicalError\n  end\n\n  def perform(params)\n    account = Account.lock.find(user_id)\n    Failure(\"User account is inactive\") unless user.active?\n\n    CreditTransaction.create!(user_id: user.id, amount: amount)\n    AuditLog.create!(action: 'add_funds', account: account)\n\n    Success('Funds has been add to account')\n  end\n\n  # ...\nend\n\n```\n\n### result_inference\n\n```ruby\nclass GreetingCommand\n  include HatiCommand::Cmd\n\n  command do\n    result_inference true # Implicitly wraps non-Result as Success\n  end\n\n  def call\n    42\n  end\n  # ...\nend\n```\n\n```ruby\nresult = GreetingCommand.call\nputs result.success  # Outputs: 42\nputs result.failure? # Outputs: false\n```\n\n### call_as\n\n```ruby\nclass GreetingCommand\n  include HatiCommand::Cmd\n\n  command do\n    call_as :execute # E.q. :perform, :run, etc.\n  end\n\n  def execute\n    Success(42)\n  end\n  # ...\nend\n```\n\n```ruby\nresult = GreetingCommand.execute\nputs result.success  # Outputs: 42\nputs result.failure? # Outputs: false\n```\n\n### failure\n\n```ruby\n1 | class DoomedCommand\n2 |   include HatiCommand::Cmd\n3 |\n4 |   command do\n5 |     failure \"Default Error\"\n6 |   end\n7 |\n8 |   def call(error = nil, fail_fast: false)\n9 |     Failure! if fail_fast\n10|\n11|     return Failure(\"Foo\") unless option\n12|\n13|     Failure(error, err: \"Insufficient funds\")\n14|   end\n15|   # ...\n16| end\n```\n\nNOTE: not configured fail fast uses default error\n\n```ruby\nresult = DoomedCommand.call(fail_fast: true)\n\nputs result.failure # Outputs: nil\nputs result.error   # Outputs: \"Default Error\"\nputs result.trace   # Outputs: path/to/cmds/doomed_command.rb:5:in `call'\n\n\nresult = DoomedCommand.call\nputs result.failure # Outputs: \"Foo\"\nputs result.error   # Outputs: \"Default Error\"\n\nresult = DoomedCommand.call('Buzz')\nputs result.failure # Outputs: \"Buzz\"\nputs result.error   # Outputs: \"Insufficient funds\"\n```\n\n### fail_fast\n\n```ruby\n1 | class DoomedCommand\n2 |   include HatiCommand::Cmd\n3 |\n4 |   command do\n5 |     fail_fast \"Default Fail Fast Error\"\n6 |   end\n7 |\n8 |   def call\n9 |     Failure!\n10|   end\n11|   # ...\n12| end\n```\n\n```ruby\nresult = DoomedCommand.call\nputs result.failure # Outputs: nil\nputs result.error   # Outputs: \"Default Fail Fast Error\"\nputs result.trace   # Outputs: path/to/cmds/doomed_command.rb:9:in `call'\n```\n\n### unexpected_err\n\n```ruby\n1 | class GreetingCommand\n2 |   include HatiCommand::Cmd\n3 |\n4 |   command do\n5 |     unexpected_err true\n5 |   end\n6 |\n7 |   def call\n8 |     1 + \"2\"\n9 |   end\n10|   # ...\n11| end\n```\n\n```ruby\nresult = GreetingCommand.call\nputs result.failure # Outputs: nil\nputs result.error   # Outputs: TypeError: no implicit conversion of Integer into String\nputs result.trace   # Outputs: path/to/cmds/greeting_command.rb:9:in `call'\n```\n\n### unexpected_err (wrapped)\n\n```ruby\n1 | class GreetingCommand\n2 |   include HatiCommand::Cmd\n3 |\n4 |   class GreetingError < StandardError; end\n5 |\n6 |   command do\n7 |     unexpected_err GreetingError\n8 |   end\n9 |\n10|   def call\n11|     1 + \"2\"\n12|   end\n13|   # ...\n14| end\n```\n\nNOTE: Original error becomes value (failure)\n\n```ruby\nresult = GreetingCommand.call\n\nputs result.failure # Outputs: TypeError: no implicit conversion of Integer into String\nputs result.error   # Outputs: GreetingError\nputs result.trace   # Outputs: path/to/cmds/greeting_command.rb:12:in `call'\n```\n\n### ar_transaction\n\nWraps listed methods in Transaction with blocking non-Result returns.\nAt this dev stage relies on 'activerecord'\n\n- NOTE: considering extensicve expirience of usage, we recomend to use some naming convention\n  across codebase for such methods, to keep healthy Elegance-to-Explicitness ratio\n\n  #### E.g. suffixes: \\_flow, \\_transaction, \\_task, etc.\n\n- NOTE: `Failure()` works as transaction break, returns only from called method's as Result (Failure) object\n\n- NOTE: `Failure!()` works on Service level same fail_fast immediately halts execution, return from\n\n- NOTE: Unlike `ActiveRecord::Transaction` Implicit non-Result returns will trigger `TransactionError`,\n  blocking partial commit state unless:\n\n```ruby\n  ar_transaction :transactional_method_name, returnable: false # Defaults to true\n```\n\n### Pseudo-Example:\n\n```ruby\n  class PaymentService < AppService\n    command do\n      ar_transaction :add_funds_transaction\n      unexpected_err PaymentServiceTechnicalError\n    end\n\n    def call(params)\n      amount = currency_exchange(params[:amount])\n      debit_transaction = add_funds_transaction(amount)\n\n      return debit_transaction if debit_transaction.success?\n\n      Failure(debit_transaction, err: 'Unable to add funds')\n    end\n\n    def currency_exchange\n      # ...\n    end\n\n    # Whole method evaluates in ActiveRecord::Transaction block\n    def add_funds_transaction(amount)\n      account = Account.lock.find(user_id)\n      Failure(\"User account is inactive\") unless user.active?\n\n      # Fires TransactionError, unless :returnable configuration is disabled\n      return 'I am an Error'\n\n      user.balance += amount\n      user.save\n      Failure('Account debit issue') if user.errors\n\n      CreditTransaction.create!(user_id: user.id, amount: amount)\n      AuditLog.create!(action: 'add_funds', account: account)\n\n      # NOTE: result inference won't work, use only Result objects\n      Success('Great Succeess')\n    end\n\n  # ...\n  end\n```\n\n## Authors\n- [Marie Giy](https://github.com/mariegiy)\n\n## Contributors\n- [yurigitsu](https://github.com/yurigitsu) \n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/hackico-ai/hati-command. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/hackico-ai/hati-command/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the HatCommand project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hackico-ai/hati-command/blob/main/CODE_OF_CONDUCT.md).\n"
  },
  {
    "path": "hati-command.gemspec",
    "content": "# frozen_string_literal: true\n\nlib = File.expand_path('lib', __dir__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\n\nrequire 'hati_command/version'\n\nGem::Specification.new do |spec|\n  spec.name    = 'hati-command'\n  spec.version = HatiCommand::VERSION\n  spec.authors = ['Mariya Giy', 'Yuri Gi']\n  spec.email   = %w[yurigi.pro@gmail.com giy.mariya@gmail.com]\n  spec.license = 'MIT'\n\n  spec.summary = 'A Ruby gem for creating modular, agent-ready service objects and command-pattern interactors, following Railway-oriented design principles with structured, explicit result handling.'\n  spec.description = 'hati-command offers a clear, minimal abstraction for implementing composable service objects and command-pattern interactors. By enforcing explicit Success and Failure result pathways, it aligns well with autonomous system pipelines, decision-chain architectures, and AI-driven orchestration flows.'\n  spec.homepage    = \"https://github.com/hackico-ai/#{spec.name}\"\n\n  spec.required_ruby_version = '>= 3.0.0'\n\n  spec.files  = Dir['CHANGELOG.md', 'LICENSE', 'README.md', 'hati-command.gemspec', 'lib/**/*']\n  spec.bindir = 'bin'\n  spec.executables   = []\n  spec.require_paths = ['lib']\n\n  spec.metadata['repo_homepage']     = spec.homepage\n  spec.metadata['allowed_push_host'] = 'https://rubygems.org'\n\n  spec.metadata['homepage_uri']    = spec.homepage\n  spec.metadata['changelog_uri']   = \"#{spec.homepage}/blob/main/CHANGELOG.md\"\n  spec.metadata['source_code_uri'] = spec.homepage\n  spec.metadata['bug_tracker_uri'] = \"#{spec.homepage}/issues\"\n\n  spec.metadata['rubygems_mfa_required'] = 'true'\nend\n"
  },
  {
    "path": "lib/hati/command.rb",
    "content": "# frozen_string_literal: true\n\n# Compatibility shim for Bundler auto-require\nrequire_relative '../hati_command'\n"
  },
  {
    "path": "lib/hati_command/befehl.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities for creating and managing commands.\n# This module serves as the main namespace for all command-related operations.\nmodule HatiCommand\n  # @module Befehl\n  # Core module for command handling that provides the base functionality for creating commands.\n  # This module is designed to be extended by classes that need command handling capabilities.\n  module Befehl\n    # Extends the base class with command handling functionality\n    # @param base [Class] the class that is extending this module\n    # @return [void]\n    def self.extended(base)\n      base.extend(BefehlClassMethods)\n      def base.inherited(subclass)\n        super\n        subclass.instance_variable_set(:@__command_config, @__command_config.dup)\n      end\n    end\n\n    # @module BefehlClassMethods\n    # Provides class methods for command configuration and execution.\n    # These methods are automatically added to any class that extends Befehl.\n    module BefehlClassMethods\n      # @module namespace as alias\n      ERR = HatiCommand::Errors\n\n      # Configures a command block with specific settings\n      # @yield [void] The configuration block\n      # @return [Hash] The command configuration\n      # @example\n      #   command do\n      #     failure :my_failure_handler\n      #     fail_fast true\n      #   end\n      def command(&block)\n        instance_eval(&block) if block_given?\n      end\n\n      # @return [Hash] The current command configuration settings\n      def command_config\n        __command_config\n      end\n\n      # Sets the result inference behavior for the command.\n      # @param value [Boolean] Indicates whether to enable result inference.\n      # @return [void]\n      def result_inference(value)\n        command_config[:result_inference] = value\n      end\n\n      # Sets the failure handler for the command\n      # @param value [Symbol, Proc] The failure handler to be used\n      # @return [void]\n      def failure(value)\n        command_config[:failure] = value\n      end\n\n      # Sets the fail-fast behavior for the command\n      # @param value [Boolean] Whether to enable fail-fast behavior\n      # @return [void]\n      def fail_fast(value)\n        command_config[:fail_fast] = value\n      end\n\n      # Sets the unexpected error handler for the command\n      # @param value [Symbol, Proc, Boolean] The error handler to be used\n      # @return [void]\n      def unexpected_err(value)\n        command_config[:unexpected_err] = value\n      end\n\n      # This method checks if a caller method has been set; if not, it defaults to `:call`.\n      # @return [Symbol] The name of the method to call.\n      def call_as(value = :call)\n        command_config[:call_as] = value\n\n        singleton_class.send(:alias_method, value, :call)\n      end\n\n      # WIP: experimental\n      # TODO: set of methods\n      def ar_transaction(*cmd_methods, returnable: true)\n        has_ar_defined = defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:transaction)\n        raise ERR::ConfigurationError, 'No ActiveRecord defined' unless has_ar_defined\n\n        has_valid_mthds = cmd_methods.any? { |value| value.is_a?(Symbol) }\n        raise ERR::ConfigurationError, 'Invalid types. Accepts Array[Symbol]' unless has_valid_mthds\n\n        command_config[:ar_transaction] = {\n          methods: cmd_methods,\n          returnable: returnable\n        }\n\n        dynamic_module = Module.new do\n          cmd_methods.each do |method_name|\n            define_method(method_name) do |*args, **kwargs, &block|\n              rez = ActiveRecord::Base.transaction do\n                result = super(*args, **kwargs, &block)\n\n                # Rollbacks to prevent partial transaction state\n                if returnable && !result.is_a?(HatiCommand::Result)\n                  raise ERR::ConfigurationError,\n                        'This command configuration requires explicit Result-return from transaction'\n                end\n\n                # Allows explicit partial commit\n                if result.failure?\n                  raise ERR::TransactionError.new('Transaction brake has been triggered', failure_obj: result.value)\n                end\n\n                result\n              end\n\n              rez\n            rescue ERR::TransactionError => e\n              # TODO: process trace corectly (line of code)\n              HatiCommand::Failure.new(e.failure_obj, err: e.message, trace: e.backtrace&.first)\n            # Every other error including FailFast goes to main caller method\n            rescue ActiveRecord::ActiveRecordError => e\n              # TODO: process trace\n              HatiCommand::Failure.new(e, err: e.message, trace: e.backtrace&.first)\n            end\n          end\n        end\n\n        prepend dynamic_module\n      end\n\n      # Executes the command with the given arguments.\n      #\n      # This method creates a new instance of the command class, yields it to an optional block,\n      # and then calls the instance method with the provided arguments. It handles the result\n      # of the command execution, returning a success or failure result based on the outcome.\n      #\n      # @param args [Array] Arguments to be passed to the instance method.\n      # @yield [Object] Optional block that yields the new instance for additional configuration.\n      # @return [HatiCommand::Result, Object] The result of the command, wrapped in a Result object if applicable.\n      # @raise [HatiCommand::Errors::FailFastError] If a fail-fast condition is triggered.\n      # @raise [StandardError] If an unexpected error occurs and no handler is configured.\n      def call(*args, __command_reciever: nil, **kwargs, &block)\n        result = caller_result(*args, __command_reciever: __command_reciever, **kwargs, &block)\n\n        return result unless command_config[:result_inference]\n        return result if result.is_a?(HatiCommand::Result)\n\n        HatiCommand::Success.new(result)\n      rescue ERR::FailFastError => e\n        handle_fail_fast_error(e)\n      rescue StandardError => e\n        handle_standard_error(e)\n      end\n\n      # TODO: think on opts to hide reciever\n      def caller_result(*args, __command_reciever: nil, **kwargs, &block)\n        # expecting pre-configured reciever if given\n        if __command_reciever\n          obj = __command_reciever\n        else\n          obj = new\n          yield(obj) if !obj && block_given?\n        end\n\n        # TODO: add error if no instance method to call\n        obj.send(command_config[:call_as] || :call, *args, **kwargs, &block)\n      end\n\n      module_function\n\n      # @return [Hash] The current command configuration settings\n      # @api private\n      def __command_config\n        @__command_config ||= {}\n      end\n\n      # Handles fail-fast errors during command execution\n      # @param error [HatiCommand::Errors::FailFastError] The fail-fast error to handle\n      # @return [HatiCommand::Failure] A failure object containing error details\n      # @api private\n      def handle_fail_fast_error(error)\n        failure_obj = error.failure_obj\n        return HatiCommand::Failure.new(error, trace: error.backtrace.first) unless failure_obj\n\n        failure_obj.tap { |err| err.trace = error.backtrace[1] }\n      end\n\n      # Handles standard errors during command execution\n      # @param error [StandardError] The error to handle\n      # @return [HatiCommand::Failure] A failure object containing error details\n      # @raise [StandardError] If no unexpected error handler is configured\n      # @api private\n      def handle_standard_error(error)\n        internal_err = command_config[:unexpected_err]\n        raise error unless internal_err\n\n        err = internal_err.is_a?(TrueClass) ? error : internal_err\n        HatiCommand::Failure.new(error, err: err, trace: error.backtrace.first)\n      end\n\n      def execute_with_transaction_handling?\n        has_ar_defined = defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:transaction)\n\n        !!(command_config.dig(:ar_transaction, :methods) && has_ar_defined)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/callee.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities and callable patterns.\nmodule HatiCommand\n  # @module Callee\n  # Module for adding callable functionality to a class.\n  # This module implements the callable pattern, allowing classes to be called like functions\n  # while maintaining object-oriented principles.\n  #\n  # @example\n  #   class MyCallable\n  #     include HatiCommand::Callee\n  #\n  #     def call(input)\n  #       # Process input\n  #       input.upcase\n  #     end\n  #   end\n  #\n  #   # Can be used as:\n  #   result = MyCallable.call(\"hello\")  # => \"HELLO\"\n  #\n  #   # Or with a block:\n  #   MyCallable.call(\"hello\") do |instance|\n  #     instance.configure(some: :option)\n  #   end\n  module Callee\n    # Extends the including class with callable functionality\n    # @param base [Class] The class including this module\n    # @return [void]\n    # @api private\n    def self.included(base)\n      base.extend(CalleeClassMethods)\n    end\n\n    # Returns the identity of the module\n    # @note This is a work in progress method\n    # @return [String] The module's identity string\n    # @api public\n    def self.whoami\n      'My Name is Callee'\n    end\n\n    # @module CalleeClassMethods\n    # Class methods that are extended to classes including Callee.\n    # Provides the callable interface at the class level.\n    module CalleeClassMethods\n      # This method checks if a caller method has been set; if not, it defaults to `:call`.\n      #\n      # @return [Symbol] The name of the method to call.\n      def __caller_method\n        @__caller_method || :call\n      end\n\n      # Creates a new instance and calls its `call` method with the given arguments.\n      # This method implements the callable pattern, allowing the class to be used\n      # like a function while maintaining object-oriented principles.\n      #\n      # @param args [Array] Arguments to be passed to the instance's call method\n      # @yield [Object] Optional block that yields the new instance before calling\n      # @yieldparam instance [Object] The newly created instance\n      # @return [Object] The result of the instance method call\n      #\n      # @example Without block\n      #   MyCallable.call(arg1, arg2)\n      # @example With configuration block\n      #   MyCallable.call(input) do |instance|\n      #     instance.configure(option: value)\n      #   end\n      def call(...)\n        obj = new\n\n        yield(obj) if block_given?\n\n        obj.send(__caller_method, ...)\n      end\n\n      # This method allows you to configure command call method name such as: :execute, :perform, etc.\n      # Note: method call_as and command main instance method should much\n      # @param method_name [Symbol] The name of the alias to create for the `call` method.\n      # @return [void]\n      #\n      #  @example\n      #   class MyCallable\n      #     include HatiCommand::Callee\n      #\n      #     call_as :execute # :run, :perform, etc.\n      #\n      #     def execute(input) # :run, :perform, etc.\n      #       input.upcase\n      #     end\n      #\n      #   end\n      #   MyCallable.execute(\"hello\")  # => \"HELLO\"\n      #   MyCallable.perform(\"hello\")  # => \"HELLO\"\n      #   MyCallable.run(\"hello\")  # => \"HELLO\"\n      def call_as(method_name)\n        @__caller_method = method_name\n\n        singleton_class.send(:alias_method, method_name, :call)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/cmd.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities with a focus on Railway-oriented programming.\n# This module implements the Railway pattern for better error handling and command flow control.\nmodule HatiCommand\n  # @module Cmd\n  # Dev-friendly extension of the Befehl core module with Railway track API.\n  # This module provides a Railway-oriented programming interface for handling success and failure states\n  # in a functional way, making it easier to chain operations and handle errors gracefully.\n  #\n  # @example\n  #   class MyCommand\n  #     include HatiCommand::Cmd\n  #\n  #     def call(input)\n  #       if valid?(input)\n  #         Success(input)\n  #       else\n  #         Failure(\"Invalid input\")\n  #       end\n  #     end\n  #   end\n  module Cmd\n    # Includes the module in the base class and sets up necessary configurations\n    # @param base [Class] The class including this module\n    # @return [void]\n    def self.included(base)\n      base.extend(HatiCommand::Befehl)\n      base.private_class_method :new\n    end\n\n    # Returns the identity of the module\n    # @note This is a work in progress method\n    # @return [String] The module's identity string\n    # @api public\n    def self.whoami\n      'My Name is Cmd'\n    end\n\n    # Creates a Success monad representing a successful operation\n    # @param value [Object, nil] The value to wrap in the Success monad\n    # @param err [Object, nil] Optional error object\n    # @param meta [Hash] Additional metadata for the success state\n    # @return [HatiCommand::Success] A Success monad containing the result\n    # @example\n    #   Success(\"Operation completed\", meta: { time: Time.now })\n    def Success(value = nil, err: nil, meta: {}) # rubocop:disable Naming/MethodName\n      HatiCommand::Success.new(value, err: err, meta: meta)\n    end\n\n    # Creates a Failure monad representing a failed operation\n    # @param value [Object, nil] The value to wrap in the Failure monad\n    # @param err [Object, nil] Optional error object (falls back to configured default)\n    # @param meta [Hash] Additional metadata for the failure state\n    # @return [HatiCommand::Failure] A Failure monad containing the error details\n    # @example\n    #   Failure(\"Operation failed\", err: StandardError.new, meta: { reason: \"invalid_input\" })\n    def Failure(value = nil, err: nil, meta: {}) # rubocop:disable Naming/MethodName\n      default_err = self.class.command_config[:failure]\n      HatiCommand::Failure.new(value, err: err || default_err, meta: meta)\n    end\n\n    # Creates a Failure monad and immediately raises a FailFastError\n    # @param value [Object, nil] The value to wrap in the Failure monad\n    # @param err [Object, nil] Optional error object (falls back to configured defaults)\n    # @param meta [Hash] Additional metadata for the failure state\n    # @param _opts [Hash] Additional options (currently unused)\n    # @raise [HatiCommand::Errors::FailFastError] Always raises with the created Failure monad\n    # @example\n    #   Failure!(\"Critical error\", err: FatalError.new)\n    def Failure!(value = nil, err: nil, meta: {}, **_opts) # rubocop:disable Naming/MethodName\n      default_error = self.class.command_config[:fail_fast] || self.class.command_config[:failure]\n      error = err || default_error\n\n      failure_obj = HatiCommand::Failure.new(value, err: error, meta: meta)\n      raise HatiCommand::Errors::FailFastError.new('Fail Fast Triggered', failure_obj: failure_obj)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/errors/base_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule HatiCommand\n  module Errors\n    # Custom BaseError class for command issues scenarios in HatiCommand\n    #\n    # @example Raising a BaseError with a message\n    #   raise HatiCommand::Error::BaseError, \"Operation failed\"\n    class BaseError < StandardError\n      DEFAULT_MSG = 'Default message: Oooops! Something went wrong. Please check the logs.'\n\n      attr_reader :failure_obj\n\n      # @param message [String] The error message\n      # @param failure_obj [Object] An optional Error || Failure DTO\n      def initialize(message = nil, failure_obj: nil)\n        msg = build_msg + (message || default_message)\n        super(msg)\n        @failure_obj = failure_obj\n      end\n\n      def error_klass\n        self.class.name\n      end\n\n      def build_msg\n        \"[#{error_klass}] \"\n      end\n\n      def default_message\n        DEFAULT_MSG\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/errors/configuration_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule HatiCommand\n  module Errors\n    # Custom error class for configuration issues scenarios in HatiCommand\n    class ConfigurationError < BaseError\n      def default_message\n        'Invalid configurations'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/errors/fail_fast_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule HatiCommand\n  module Errors\n    # Custom error class for FailFast scenario in HatiCommand\n    class FailFastError < BaseError\n      def default_message\n        'Halt Execution'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/errors/transaction_error.rb",
    "content": "# frozen_string_literal: true\n\nmodule HatiCommand\n  module Errors\n    # Custom error class for Transaction issue scenarios in HatiCommand\n    class TransactionError < BaseError\n      def default_message\n        'Transaction Error has been triggerd'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/failure.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities and result objects.\nmodule HatiCommand\n  # @class Failure\n  # Represents a failure result in the Result pattern.\n  # This class is used to wrap failure values and provide a consistent interface\n  # for handling both successful and failed operations.\n  #\n  # The Failure class is part of the Result pattern implementation, working alongside\n  # the Success class to provide a type-safe way to handle operation outcomes.\n  #\n  # @example Basic usage\n  #   failure = HatiCommand::Failure.new(\"Operation failed\")\n  #   failure.failure?  # => true\n  #   failure.success?  # => false\n  #\n  # @example With error and metadata\n  #   error = StandardError.new(\"Database connection failed\")\n  #   failure = HatiCommand::Failure.new(\n  #     \"Could not save record\",\n  #     err: error,\n  #     meta: { attempted_at: Time.now }\n  #   )\n  #\n  # @example Pattern matching\n  #   case result\n  #   when HatiCommand::Failure\n  #     handle_error(result.failure)\n  #   end\n  #\n  # @see HatiCommand::Success\n  # @see HatiCommand::Result\n  class Failure < Result\n    # Returns the failure value wrapped by this Failure instance.\n    # This method provides access to the actual error value or message\n    # that describes why the operation failed.\n    #\n    # @return [Object] The wrapped failure value\n    # @example\n    #   failure = Failure.new(\"Database error\")\n    #   failure.failure  # => \"Database error\"\n    def failure\n      value\n    end\n\n    # Indicates that this is a failure result.\n    # This method is part of the Result pattern interface and always\n    # returns true for Failure instances.\n    #\n    # @return [Boolean] Always returns true\n    # @example\n    #   failure = Failure.new(\"Error\")\n    #   failure.failure?  # => true\n    def failure?\n      true\n    end\n\n    # Returns nil since a Failure has no success value.\n    # This method is part of the Result pattern interface and always\n    # returns nil for Failure instances.\n    #\n    # @return [nil] Always returns nil\n    # @example\n    #   failure = Failure.new(\"Error\")\n    #   failure.success  # => nil\n    def success\n      nil\n    end\n\n    # Indicates that this is not a success result.\n    # This method is part of the Result pattern interface and always\n    # returns false for Failure instances.\n    #\n    # @return [Boolean] Always returns false\n    # @example\n    #   failure = Failure.new(\"Error\")\n    #   failure.success?  # => false\n    def success?\n      false\n    end\n\n    # Returns the symbolic representation of this result type.\n    # Useful for pattern matching and result type checking.\n    #\n    # @return [Symbol] Always returns :failure\n    # @api public\n    # @example\n    #   failure = Failure.new(\"Error\")\n    #   failure.to_sym  # => :failure\n    def to_sym\n      :failure\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/result.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities and result objects.\nmodule HatiCommand\n  # @class Result\n  # Base class for the Result pattern implementation.\n  # This class serves as the foundation for Success and Failure result types,\n  # providing common functionality and a consistent interface for handling\n  # operation outcomes.\n  #\n  # The Result pattern helps in handling operation outcomes in a type-safe way,\n  # making it explicit whether an operation succeeded or failed, and carrying\n  # additional context like error messages and metadata.\n  #\n  # @abstract Subclass and override {#to_sym} to implement a concrete result type\n  #\n  # @example Basic usage\n  #   result = HatiCommand::Result.new(\"Operation output\")\n  #   result.value  # => \"Operation output\"\n  #\n  # @example With error and metadata\n  #   result = HatiCommand::Result.new(\n  #     \"Operation output\",\n  #     err: \"Warning: partial completion\",\n  #     meta: { duration_ms: 150 }\n  #   )\n  #\n  # @example Using trace information\n  #   result = HatiCommand::Result.new(\"Output\", trace: caller(1..1))\n  #   result.trace  # => [\"path/to/file.rb:42:in `method_name'\"]\n  #\n  # @see HatiCommand::Success\n  # @see HatiCommand::Failure\n  #\n  # @!attribute [r] value\n  #   @return [Object] The wrapped value representing the operation's output\n  #\n  # @!attribute [r] meta\n  #   @return [Hash] Additional metadata associated with the result\n  #\n  # @!attribute [rw] trace\n  #   @return [Array<String>, nil] Execution trace information for debugging\n  class Result\n    attr_reader :value, :meta\n    attr_accessor :trace, :err\n\n    # Initializes a new Result instance with a value and optional context.\n    #\n    # @param value [Object] The value to be wrapped in the result\n    # @param err [String, nil] Optional error message or error object\n    # @param meta [Hash] Optional metadata for additional context\n    # @param trace [Array<String>, nil] Optional execution trace for debugging\n    #\n    # @example Basic initialization\n    #   result = Result.new(\"Success\")\n    #\n    # @example With full context\n    #   result = Result.new(\n    #     \"Partial success\",\n    #     err: \"Some records failed\",\n    #     meta: { processed: 10, failed: 2 },\n    #     trace: caller\n    #   )\n    def initialize(value, err: nil, meta: {}, trace: nil)\n      @value = value\n      @err = err\n      @meta = meta\n      @trace = trace\n    end\n\n    # Returns self to provide a consistent interface across result types.\n    # This method ensures that all result objects can be treated uniformly\n    # when chaining operations.\n    #\n    # @return [HatiCommand::Result] The result instance itself\n    # @api public\n    def result\n      self\n    end\n\n    # Returns the error associated with this result.\n    # This can be used to check for warnings or errors even in successful results.\n    #\n    # @return [String, nil] The error message or object, if any\n    # @raise [StandardError] If accessing the error triggers an error condition\n    # @api public\n    # @example\n    #   result = Result.new(\"Value\", err: \"Warning message\")\n    #   result.error  # => \"Warning message\"\n    def error\n      @err\n    end\n\n    # Returns the symbolic representation of this result type.\n    # This is an abstract method that should be overridden by concrete result types.\n    #\n    # @return [Symbol] Returns :undefined for the base class\n    # @abstract Subclasses must override this method\n    # @api public\n    # @example\n    #   Result.new(\"value\").to_sym  # => :undefined\n    def to_sym\n      :undefined\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/success.rb",
    "content": "# frozen_string_literal: true\n\n# @module HatiCommand\n# Provides command handling functionalities and result objects.\nmodule HatiCommand\n  # @class Success\n  # Represents a successful result in the Result pattern.\n  # This class is used to wrap successful operation values and provide a consistent interface\n  # for handling both successful and failed operations.\n  #\n  # The Success class is part of the Result pattern implementation, working alongside\n  # the Failure class to provide a type-safe way to handle operation outcomes.\n  #\n  # @example Basic usage\n  #   success = HatiCommand::Success.new(\"Operation completed\")\n  #   success.success?  # => true\n  #   success.failure?  # => false\n  #\n  # @example With metadata\n  #   success = HatiCommand::Success.new(\n  #     { id: 123, name: \"Example\" },\n  #     meta: { duration_ms: 50 }\n  #   )\n  #   success.success  # => { id: 123, name: \"Example\" }\n  #\n  # @example Pattern matching\n  #   case result\n  #   when HatiCommand::Success\n  #     process_data(result.success)\n  #   end\n  #\n  # @see HatiCommand::Failure\n  # @see HatiCommand::Result\n  class Success < Result\n    # Returns the success value wrapped by this Success instance.\n    # This method provides access to the actual value or result\n    # that was produced by the successful operation.\n    #\n    # @return [Object] The wrapped success value\n    # @example\n    #   success = Success.new(\"Operation output\")\n    #   success.success  # => \"Operation output\"\n    def success\n      value\n    end\n\n    # Indicates that this is a success result.\n    # This method is part of the Result pattern interface and always\n    # returns true for Success instances.\n    #\n    # @return [Boolean] Always returns true\n    # @example\n    #   success = Success.new(\"Result\")\n    #   success.success?  # => true\n    def success?\n      true\n    end\n\n    # Returns nil since a Success has no failure value.\n    # This method is part of the Result pattern interface and always\n    # returns nil for Success instances.\n    #\n    # @return [nil] Always returns nil\n    # @example\n    #   success = Success.new(\"Result\")\n    #   success.failure  # => nil\n    def failure\n      nil\n    end\n\n    # Indicates that this is not a failure result.\n    # This method is part of the Result pattern interface and always\n    # returns false for Success instances.\n    #\n    # @return [Boolean] Always returns false\n    # @example\n    #   success = Success.new(\"Result\")\n    #   success.failure?  # => false\n    def failure?\n      false\n    end\n\n    # Returns the symbolic representation of this result type.\n    # Useful for pattern matching and result type checking.\n    #\n    # @return [Symbol] Always returns :success\n    # @api public\n    # @example\n    #   success = Success.new(\"Result\")\n    #   success.to_sym  # => :success\n    def to_sym\n      :success\n    end\n  end\nend\n"
  },
  {
    "path": "lib/hati_command/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule HatiCommand\n  VERSION = '0.1.2'\nend\n"
  },
  {
    "path": "lib/hati_command.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'hati_command/version'\n# errors\nrequire 'hati_command/errors/base_error'\nrequire 'hati_command/errors/configuration_error'\nrequire 'hati_command/errors/fail_fast_error'\nrequire 'hati_command/errors/transaction_error'\n# result\nrequire 'hati_command/result'\nrequire 'hati_command/success'\nrequire 'hati_command/failure'\n# core\nrequire 'hati_command/callee'\nrequire 'hati_command/befehl'\n# cmd\nrequire 'hati_command/cmd'\n"
  },
  {
    "path": "spec/integration/hati_command/befehl_ar_transaction_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'support/active_record'\n\nRSpec.describe HatiCommand::Befehl do\n  let(:ar_model) { 'Widget' }\n  let(:befehl_klass) { support_dummy_befehl('DummyBefehl') }\n  let(:ar_command) { 'MyDummyExecBefehl' }\n\n  describe 'ActiveRecord transaction wrapping' do\n    before do\n      stub_const(ar_model, Class.new(ActiveRecord::Base))\n\n      stub_const(\n        ar_command,\n        Class.new(befehl_klass) do\n          command { ar_transaction :call }\n\n          def call(message)\n            Widget.create!(name: message)\n            raise ActiveRecord::Rollback if message == :fail\n\n            HatiCommand::Success.new(message)\n          end\n        end\n      )\n    end\n\n    it 'commits the transaction on success' do\n      expect { MyDummyExecBefehl.call('Widget1') }.to change(Widget, :count).by(1)\n    end\n\n    it 'rolls back the transaction on failure' do\n      expect do\n        MyDummyExecBefehl.call(:fail)\n      rescue StandardError\n        nil\n      end.not_to change(Widget, :count)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/hati_command/befehl_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Befehl do\n  let(:befehl_klass) { support_dummy_befehl('DummyBefehl') }\n\n  before do\n    stub_const(\n      'MyDummyBefehl',\n      Class.new(befehl_klass) do\n        command do\n          fail_fast 'Default Fail Fast message provided'\n          unexpected_err true\n          result_inference true\n        end\n\n        def call(message, fail_fast: false, unexpected_err: false, result_inference: false)\n          raise HatiCommand::Errors::FailFastError.new('Fail Fast Triggered') if fail_fast # rubocop:disable Style/RaiseArgs\n          raise StandardError if unexpected_err\n\n          result_inference ? message : HatiCommand::Success.new(message)\n        end\n      end\n    )\n  end\n\n  describe '.call' do\n    let(:result) { MyDummyBefehl.call('Success!') }\n\n    it 'returns success' do\n      aggregate_failures 'result' do\n        expect(result).to be_a(HatiCommand::Success)\n        expect(result.value).to eq('Success!')\n        expect(result.error).to be_nil\n      end\n    end\n\n    context 'when fail_fast is true' do\n      let(:result) { MyDummyBefehl.call('This is a fail fast message', fail_fast: true) }\n\n      it 'returns failure' do\n        aggregate_failures 'result' do\n          expect(result).to be_a(HatiCommand::Failure)\n          expect(result.value).to be_a(HatiCommand::Errors::FailFastError)\n        end\n      end\n    end\n\n    context 'when unexpected_err is true' do\n      let(:result) { MyDummyBefehl.call('This is a unexpected error message', unexpected_err: true) }\n\n      it 'returns failure' do\n        aggregate_failures 'result' do\n          expect(result).to be_a(HatiCommand::Failure)\n          expect(result.error).to be_a(StandardError)\n        end\n      end\n    end\n\n    context 'when result_inference is true' do\n      let(:result) { MyDummyBefehl.call('This is a result inference message', result_inference: true) }\n\n      it 'returns success' do\n        expect(result).to be_a(HatiCommand::Success)\n      end\n    end\n  end\n\n  describe 'call configuration' do\n    before do\n      stub_const(\n        'MyDummyExecBefehl',\n        Class.new(befehl_klass) do\n          command do\n            call_as :execute\n          end\n\n          def execute(message)\n            HatiCommand::Success.new(message)\n          end\n        end\n      )\n    end\n\n    describe '.execute' do\n      let(:rez_msg) { 'This is a result inference message' }\n      let(:result) { MyDummyExecBefehl.execute(rez_msg) }\n\n      it 'returns success' do\n        aggregate_failures do\n          expect(result).to be_a(HatiCommand::Success)\n          expect(result.value).to eq(rez_msg)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/hati_command/callee_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Callee do\n  context 'when run command' do\n    before do\n      stub_const('DummyCallee', Class.new do\n        include HatiCommand::Callee\n\n        def call(rez)\n          HatiCommand::Success.new(rez)\n        end\n      end)\n    end\n\n    let(:callee_klass) { DummyCallee }\n\n    describe '.call' do\n      let(:result) { callee_klass.call('Success!') }\n\n      it 'returns success' do\n        expect(result.value).to eq('Success!')\n      end\n    end\n  end\n\n  context 'when call is configured' do\n    before do\n      stub_const('DummyExecutee', Class.new do\n        include HatiCommand::Callee\n\n        call_as :execute\n\n        def execute(rez)\n          HatiCommand::Success.new(rez)\n        end\n      end)\n    end\n\n    let(:callee_klass) { DummyExecutee }\n\n    describe '.execute' do\n      let(:result) { DummyExecutee.execute('Success!') }\n\n      it 'returns success' do\n        expect(result.value).to eq('Success!')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/hati_command/cmd_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Cmd do\n  before do\n    support_dummy_cmd('BaseDummyCommand')\n    support_dummy_error('DummyFailFastError')\n    support_dummy_error('DummyInternalError')\n\n    stub_const(\n      'DummyCommand',\n      Class.new(BaseDummyCommand) do\n        command do\n          fail_fast 'Default Fail Fast message provided'\n          unexpected_err DummyInternalError\n        end\n      end\n    )\n\n    stub_const(\n      'MyDummyCommand',\n      Class.new(DummyCommand) do\n        command do\n          fail_fast DummyFailFastError\n        end\n\n        def call(greeting = nil, fail_fast: false, unexpected_err: false)\n          raise 'Oooooooops' if unexpected_err\n\n          salute = build_greeting(greeting)\n          howdy = normalize_salute(salute, fail_fast)\n\n          process_howdy(howdy.value)\n        end\n\n        def build_greeting(greeting)\n          greeting ? Success(greeting) : Failure(greeting)\n        end\n\n        def normalize_salute(salute, fail_fast)\n          Failure!(salute) if fail_fast\n\n          salute.success ? Success(\"#{salute.success}!\") : Failure(salute.failure)\n        end\n\n        def process_howdy(howdy)\n          if howdy\n            Success(howdy.upcase, meta: { lang: :eng, length: howdy.length })\n          else\n            Failure(howdy, err: 'No message provided')\n          end\n        end\n      end\n    )\n  end\n\n  let(:message) { 'Hello, World' }\n\n  describe '.call' do\n    context 'when pipeline success' do\n      let(:raw_message) { 'Hello, World' }\n      let(:message) { 'Hello, World!' }\n      let(:result) { MyDummyCommand.call(raw_message) }\n\n      it 'runs the success command' do\n        aggregate_failures 'result' do\n          expect(result.value).to eq(message.upcase)\n          expect(result.success).to eq(message.upcase)\n        end\n      end\n\n      it 'returns meta' do\n        meta = { lang: :eng, length: message.length }\n\n        expect(result.meta).to eq(meta)\n      end\n    end\n\n    context 'when pipeline failure' do\n      let(:result) { MyDummyCommand.call }\n\n      it 'runs the failure command' do\n        aggregate_failures 'result' do\n          expect(result).to be_failure\n          expect(result.value).to be_nil\n        end\n      end\n\n      it 'returns message' do\n        message = 'No message provided'\n\n        expect(result.error).to eq(message)\n      end\n    end\n\n    context 'when fail-fast' do\n      let(:result) { MyDummyCommand.call(fail_fast: true) }\n\n      it 'returns failure' do\n        expect(result.error).to eq(DummyFailFastError)\n      end\n    end\n\n    context 'when unexpected error' do\n      let(:result) { MyDummyCommand.call(unexpected_err: true) }\n\n      it 'returns failure' do\n        expect(result.error).to eq(DummyInternalError)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'bundler/setup'\nrequire 'hati_command'\n\nRSpec.configure do |config|\n  config.example_status_persistence_file_path = '.rspec_status'\n  config.disable_monkey_patching!\n  config.expect_with :rspec do |c|\n    c.syntax = :expect\n  end\n\n  # performance\n  exclude_support_files = ['active_record']\n\n  Dir[File.join('./spec/support/**/*.rb')].each do |support_file|\n    next if exclude_support_files.include?(support_file)\n\n    require support_file\n  end\n\n  config.include Dummy\nend\n"
  },
  {
    "path": "spec/support/active_record.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'active_record'\n\nActiveRecord::Base.establish_connection(\n  adapter: 'sqlite3',\n  database: ':memory:'\n)\n\nActiveRecord::Schema.define do\n  create_table :widgets, force: true do |t|\n    t.string :name\n    t.timestamps\n  end\nend\n"
  },
  {
    "path": "spec/support/dummy.rb",
    "content": "# frozen_string_literal: true\n\n# NOTE: helper names follow convention 'support_<module_name>_<helper_name>'\n\nmodule Dummy\n  def support_dummy_befehl(name)\n    stub_const(name, Class.new do\n      extend HatiCommand::Befehl\n    end)\n  end\n\n  def support_dummy_cmd(name)\n    stub_const(name, Class.new do\n      include HatiCommand::Cmd\n\n      command do\n        fail_fast 'Base Fail Fast Message'\n      end\n    end)\n  end\n\n  def support_dummy_error(name)\n    stub_const(name, Class.new(StandardError))\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/befehl_config_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Befehl do\n  describe 'command configurations' do\n    let(:command_klass) { 'BefehlClass' }\n    let(:befehl_klass) { command_klass.constantize }\n    let(:command_klass_attr) { 'BefehlClassAttr' }\n    let(:befehl_klass_attr) { command_klass_attr.constantize }\n\n    before do\n      stub_const(command_klass, Class.new do\n        extend HatiCommand::Befehl::BefehlClassMethods\n\n        command do\n          call_as :execute\n          fail_fast 'Befehl Fail Fast Message'\n          failure 'Befehl Failure Message'\n          unexpected_err 'Befehl Unexpected Error'\n          result_inference true\n        end\n      end)\n\n      # no block configs\n      stub_const(command_klass_attr, Class.new do\n        extend HatiCommand::Befehl::BefehlClassMethods\n\n        call_as :execute\n        fail_fast 'Befehl Fail Fast Message'\n        failure 'Befehl Failure Message'\n        unexpected_err 'Befehl Unexpected Error'\n        result_inference true\n      end)\n    end\n\n    describe '.fail_fast' do\n      it 'sets the fail_fast config' do\n        expect(befehl_klass.command_config[:fail_fast]).to eq('Befehl Fail Fast Message')\n      end\n    end\n\n    describe '.unexpected_err' do\n      it 'sets the unexpected_err config' do\n        expect(befehl_klass.command_config[:unexpected_err]).to eq('Befehl Unexpected Error')\n      end\n    end\n\n    describe '.failure' do\n      it 'sets the failure config' do\n        expect(befehl_klass.command_config[:failure]).to eq('Befehl Failure Message')\n      end\n    end\n\n    describe '.result_inference' do\n      it 'sets the result_inference config' do\n        expect(befehl_klass.command_config[:result_inference]).to be(true)\n      end\n    end\n\n    describe '.command_config' do\n      let(:configs) { befehl_klass.command_config }\n      let(:configs_attr) { befehl_klass_attr.command_config }\n\n      it 'returns the configurations' do\n        aggregate_failures 'of command options' do\n          expect(configs[:fail_fast]).to eq('Befehl Fail Fast Message')\n          expect(configs[:failure]).to eq('Befehl Failure Message')\n          expect(configs[:unexpected_err]).to eq('Befehl Unexpected Error')\n          expect(configs[:result_inference]).to be(true)\n          expect(configs[:call_as]).to be(:execute)\n        end\n      end\n\n      it 'has identical no-block and block configurations' do\n        aggregate_failures 'of block and no-block command options' do\n          expect(configs[:fail_fast]).to eq(configs_attr[:fail_fast])\n          expect(configs[:failure]).to eq(configs_attr[:failure])\n          expect(configs[:unexpected_err]).to eq(configs_attr[:unexpected_err])\n          expect(configs[:result_inference]).to be(configs_attr[:result_inference])\n          expect(configs[:call_as]).to be(configs_attr[:call_as])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/callee_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\n# WIP: performance extensions\nRSpec.describe HatiCommand::Callee do\n  subject(:callee_klass) { described_class }\n\n  let(:whoami) { 'My Name is Callee' }\n\n  describe '.whoami' do\n    it 'returns the class name' do\n      expect(callee_klass.whoami).to eq(whoami)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/cmd_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\n# WIP: performance extensions\nRSpec.describe HatiCommand::Cmd do\n  subject(:cmd_klass) { described_class }\n\n  let(:whoami) { 'My Name is Cmd' }\n\n  describe '.whoami' do\n    it 'returns the class name' do\n      expect(cmd_klass.whoami).to eq(whoami)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/errors/base_error_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Errors::BaseError do\n  subject(:error_klass) { described_class }\n\n  describe '#initialize' do\n    it 'initializes with a message' do\n      error = error_klass.new('Operation failed')\n\n      expect(error.message).to eq(\"[#{error_klass}] Operation failed\")\n    end\n\n    it 'initializes with a failure object' do\n      failure_obj = StandardError.new('Booom!')\n      error = error_klass.new('Operation failed', failure_obj: failure_obj)\n\n      expect(error.instance_variable_get(:@failure_obj)).to eq(failure_obj)\n    end\n\n    it 'uses the default message if no message is provided' do\n      error = error_klass.new\n\n      expect(error.message).to eq(\"[#{error_klass}] #{error_klass::DEFAULT_MSG}\")\n    end\n  end\n\n  describe '#error_klass' do\n    it 'returns the class name' do\n      error = error_klass.new\n\n      expect(error.error_klass).to eq(error_klass.name)\n    end\n  end\n\n  describe '#build_msg' do\n    it 'builds the message correctly' do\n      error = error_klass.new('Test message')\n\n      expect(error.build_msg).to eq(\"[#{error_klass}] \")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/errors/configuration_error_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Errors::ConfigurationError do\n  subject(:error_klass) { described_class }\n\n  it 'inherits from BaseError' do\n    expect(error_klass).to be < HatiCommand::Errors::BaseError\n  end\n\n  it 'has the correct default message' do\n    error = error_klass.new\n\n    expect(error.message).to eq(\"[#{error_klass}] #{error.default_message}\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/errors/fail_fast_error_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Errors::FailFastError do\n  subject(:error_klass) { described_class }\n\n  it 'inherits from BaseError' do\n    expect(error_klass).to be < HatiCommand::Errors::BaseError\n  end\n\n  it 'has the correct default message' do\n    error = error_klass.new\n\n    expect(error.message).to eq(\"[#{error_klass}] #{error.default_message}\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/errors/transaction_error_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Errors::TransactionError do\n  subject(:error_klass) { described_class }\n\n  it 'inherits from BaseError' do\n    expect(error_klass).to be < HatiCommand::Errors::BaseError\n  end\n\n  it 'has the correct default message' do\n    error = error_klass.new\n\n    expect(error.message).to eq(\"[#{error_klass}] #{error.default_message}\")\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/failure_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Failure do\n  subject(:failure_instance) { described_class.new(value) }\n\n  let(:value) { 'error_value' }\n\n  describe '#initialize' do\n    it { is_expected.to have_attributes(value: value) }\n  end\n\n  describe '#failure' do\n    it { expect(failure_instance.failure).to eq(value) }\n  end\n\n  describe '#failure?' do\n    it { is_expected.to be_failure }\n  end\n\n  describe '#success' do\n    it { expect(failure_instance.success).to be_nil }\n  end\n\n  describe '#success?' do\n    it { expect(failure_instance.success?).to be false }\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/result_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rspec'\n\nRSpec.describe HatiCommand::Result do\n  let(:value) { 'test_value' }\n\n  describe '#initialize' do\n    it { expect(described_class.new(value).value).to eq(value) }\n  end\n\n  describe '#result' do\n    it 'returns result object' do\n      result = described_class.new(value)\n\n      expect(result.result).to eq(result)\n    end\n  end\n\n  describe '#error' do\n    it 'returns error' do\n      result = described_class.new(value, err: 'test_message')\n\n      expect(result.error).to eq('test_message')\n    end\n  end\n\n  describe '#meta' do\n    it 'returns meta' do\n      result = described_class.new(value, meta: { 'test_meta' => 'test_value' })\n\n      expect(result.meta).to eq({ 'test_meta' => 'test_value' })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/unit/hati_command/success_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\nRSpec.describe HatiCommand::Success do\n  subject(:success_instance) { described_class.new(value) }\n\n  let(:value) { 'success_value' }\n\n  describe '#initialize' do\n    it { is_expected.to have_attributes(value: value) }\n  end\n\n  describe '#result' do\n    it { expect(success_instance.result).to eq(success_instance) }\n  end\n\n  describe '#success' do\n    it { expect(success_instance.success).to eq(value) }\n  end\n\n  describe '#success?' do\n    it { expect(success_instance.success?).to be true }\n  end\n\n  describe '#failure' do\n    it { expect(success_instance.failure).to be_nil }\n  end\n\n  describe '#failure?' do\n    it { expect(success_instance.failure?).to be false }\n  end\nend\n"
  }
]