diff --git a/config.defaults.toml b/config.defaults.toml index 56e3353..f3f6cf3 100644 --- a/config.defaults.toml +++ b/config.defaults.toml @@ -6,6 +6,11 @@ version = "1.0.0" name = "provisioning-system" [paths] +generate = "generate" +run_clusters = "clusters" +run_taskservs = "taskservs" +extensions = "{{paths.base}}/.provisioning-extensions" +infra = "{{paths.base}}/infra" base = "/Users/Akasha/repo-cnz/src/provisioning" kloud = "{{paths.base}}/infra" providers = "{{paths.base}}/providers" @@ -17,6 +22,9 @@ tools = "{{paths.base}}/tools" core = "{{paths.base}}/core" [paths.files] +defs = "defs.toml" +req_versions = "{{paths.core}}/versions.yaml" +vars = "{{paths.base}}/vars.yaml" settings = "{{paths.base}}/kcl/settings.k" keys = "{{paths.base}}/keys.yaml" requirements = "{{paths.base}}/requirements.yaml" @@ -81,4 +89,48 @@ interface = "CLI" # API or CLI [providers.local] api_url = "" auth = "" -interface = "CLI" # API or CLI \ No newline at end of file +interface = "CLI" # API or CLI + +# Tool Detection and Plugin Configuration +[tools] +use_kcl = false +use_kcl_plugin = false +use_tera_plugin = false + +# AI Integration Configuration +[ai] +enabled = false +provider = "openai" +api_key = "" +model = "gpt-4" +timeout = 30 + +# SSH Configuration +[ssh] +user = "" +options = ["StrictHostKeyChecking=accept-new", "UserKnownHostsFile=/dev/null"] +timeout = 30 +debug = false + +# Extension System Configuration +[extensions] +path = "" +mode = "full" +profile = "" +allowed = "" +blocked = "" +custom_providers = "" +custom_taskservs = "" + +# Key Management Service Configuration +[kms] +server = "" +auth_method = "certificate" +client_cert = "" +client_key = "" +ca_cert = "" +api_token = "" +username = "" +password = "" +timeout = 30 +verify_ssl = true diff --git a/core/nulib/env.nu b/core/nulib/env.nu index 028684a..89d01a4 100644 --- a/core/nulib/env.nu +++ b/core/nulib/env.nu @@ -1,9 +1,8 @@ use std -use lib_provisioning/context.nu setup_user_context +use lib_provisioning/config/accessor.nu * export-env { - let context = (setup_user_context) - $env.PROVISIONING = ($env.PROVISIONING? | default - ($context | get -o "provisioning" | default ("/" | path join "usr" |path join "local" | path join "provisioning") | into string)) + let config = (get-config) + $env.PROVISIONING = (config-get "paths.base" "/usr/local/provisioning" --config $config) $env.PROVISIONING_CORE = ($env.PROVISIONING | path join "core") if ($env.PROVISIONING_CORE | path exists) == false { print $"🛑 ($env.PROVISIONING_CORE) not found. Review PROVISIONING environment setting" @@ -15,24 +14,19 @@ export-env { $env.PROVISIONING_RESOURCES = ($env.PROVISIONING | path join "resources" ) $env.PROVISIONING_NOTIFY_ICON = ($env.PROVISIONING_RESOURCES | path join "images"| path join "cloudnative.png") - $env.PROVISIONING_DEBUG = ($env | get -o PROVISIONING_DEBUG | default false | into bool) - $env.PROVISIONING_METADATA = ($env | get -o PROVISIONING_METADATA | default - ($context | get -o "metadata" | default false) | into bool) + $env.PROVISIONING_DEBUG = (config-get "debug.enabled" false --config $config) + $env.PROVISIONING_METADATA = (config-get "debug.metadata" false --config $config) - $env.PROVISIONING_DEBUG_CHECK = ($env | get -o PROVISIONING_DEBUG_CHECK | default false | into bool) - $env.PROVISIONING_DEBUG_REMOTE = ($env | get -o PROVISIONING_DEBUG_REMOTE | default false | into bool) - $env.PROVISIONING_LOG_LEVEL = ($env | get -o NU_LOG_LEVEL_DEBUG | default - ($context | get -o "log_level" | default "") | into string) + $env.PROVISIONING_DEBUG_CHECK = (config-get "debug.check" false --config $config) + $env.PROVISIONING_DEBUG_REMOTE = (config-get "debug.remote" false --config $config) + $env.PROVISIONING_LOG_LEVEL = (config-get "debug.log_level" "" --config $config) - $env.PROVISIONING_NO_TERMINAL = match ($env | get -o PROVISIONING_NO_TERMINAL | default "") { - "true" | "True" => true, - _ => false - } - $env.PROVISIONING_ARGS = ($env | get -o PROVISIONING_ARGS | default "") - $env.PROVISIONING_MODULE = ($env | get -o PROVISIONING_MODULE | default "") - $env.PROVISIONING_NAME = ($env | get -o PROVISIONING_NAME | default "provisioning") + $env.PROVISIONING_NO_TERMINAL = (config-get "debug.no_terminal" false --config $config) + $env.PROVISIONING_ARGS = ($env.PROVISIONING_ARGS? | default "") + $env.PROVISIONING_MODULE = ($env.PROVISIONING_MODULE? | default "") + $env.PROVISIONING_NAME = (config-get "core.name" "provisioning" --config $config) - $env.PROVISIONING_FILEVIEWER = ($env | get -o PROVISIONING_FILEVIEWER | default "bat") + $env.PROVISIONING_FILEVIEWER = (config-get "output.file_viewer" "bat" --config $config) $env.PROVISIONING_METADATA = if ($env.PROVISIONING_ARGS? | str contains "--xm" ) { true } else { $env.PROVISIONING_METADATA } $env.PROVISIONING_DEBUG_CHECK = if ($env.PROVISIONING_ARGS? | str contains "--xc" ) { true } else { $env.PROVISIONING_DEBUG_CHECK } @@ -42,16 +36,16 @@ export-env { if $env.PROVISIONING_LOG_LEVEL == "debug" or $env.PROVISIONING_LOG_LEVEL == "DEBUG" { $env.NU_LOG_LEVEL = "DEBUG" } else { $env.NU_LOG_LEVEL = ""} $env.PROVISIONING_INFRA_PATH = ($env.PROVISIONING_KLOUD_PATH? | default - ($context | get -o "infra_path" | default $env.PWD ) | into string) + (config-get "paths.infra" | default $env.PWD ) | into string) - $env.PROVISIONING_DFLT_SET = ($context | get -o "dflt_set" | default "settings.k" | into string) + $env.PROVISIONING_DFLT_SET = (config-get "paths.files.settings" | default "settings.k" | into string) $env.NOW = (date now | format date "%Y_%m_%d_%H_%M_%S") - $env.PROVISIONING_MATCH_DATE = ($env | get -o PROVISIONING_MATCH_DATE | default "%Y_%m") + $env.PROVISIONING_MATCH_DATE = ($env.PROVISIONING_MATCH_DATE? | default "%Y_%m") #$env.PROVISIONING_MATCH_CMD = "v" - $env.PROVISIONING_WK_FORMAT = ($context | get -o "wk_format" | default "yaml" | into string) + $env.PROVISIONING_WK_FORMAT = (config-get "output.format" | default "yaml" | into string) $env.PROVISIONING_REQ_VERSIONS = ($env.PROVISIONING | path join "core" | path join "versions.yaml") $env.PROVISIONING_TOOLS_PATH = ($env.PROVISIONING | path join "core" | path join "tools") @@ -64,8 +58,7 @@ export-env { $env.PROVISIONING_GENERATE_DIRPATH = "generate" $env.PROVISIONING_GENERATE_DEFSFILE = "defs.toml" - $env.PROVISIONING_KEYS_PATH = ($env | get -o PROVISIONING_KEYS_PATH | default - ($context | get -o "keys_path" | default ".keys.k") | into string) + $env.PROVISIONING_KEYS_PATH = (config-get "paths.files.keys" ".keys.k" --config $config) $env.PROVISIONING_USE_KCL = if (^bash -c "type -P kcl" | is-not-empty) { true } else { false } $env.PROVISIONING_USE_KCL_PLUGIN = if ( (version).installed_plugins | str contains "kcl" ) { true } else { false } @@ -77,28 +70,28 @@ export-env { #let infra = ($env.PROVISIONING_ARGS | split row "-k" | get -o 1 | split row " " | get -o 1 | default "") #$env.CURR_KLOUD = if $infra == "" { (^pwd) } else { $infra } - $env.PROVISIONING_USE_SOPS = ($context | get -o "use_sops" | default "age" | into string) - $env.PROVISIONING_USE_KMS = ($context | get -o "use_kms" | default "" | into string) - $env.PROVISIONING_SECRET_PROVIDER = ($context | get -o "secret_provider" | default "sops" | into string) + $env.PROVISIONING_USE_SOPS = (config-get "sops.use_sops" | default "age" | into string) + $env.PROVISIONING_USE_KMS = (config-get "sops.use_kms" | default "" | into string) + $env.PROVISIONING_SECRET_PROVIDER = (config-get "sops.secret_provider" | default "sops" | into string) # AI Configuration - $env.PROVISIONING_AI_ENABLED = ($context | get -o "ai_enabled" | default false | into bool | into string) - $env.PROVISIONING_AI_PROVIDER = ($context | get -o "ai_provider" | default "openai" | into string) + $env.PROVISIONING_AI_ENABLED = (config-get "ai.enabled" | default false | into bool | into string) + $env.PROVISIONING_AI_PROVIDER = (config-get "ai.provider" | default "openai" | into string) $env.PROVISIONING_LAST_ERROR = "" - $env.PROVISIONING_KLOUD_PATH = ($env | get -o "PROVISIONING_KLOUD_PATH" | default "") + $env.PROVISIONING_KLOUD_PATH = ($env.PROVISIONING_KLOUD_PATH? | default "") # For SOPS if settings below fails -> look at: sops_env.nu loaded when is need to set env context - let curr_infra = ($context | get -o "infra" | default "" ) + let curr_infra = (config-get "paths.infra" "" --config $config) if $curr_infra != "" { $env.CURRENT_INFRA_PATH = $curr_infra } - let sops_path = ($context | get -o "sops_path" | default "" | str replace "KLOUD_PATH" $env.PROVISIONING_KLOUD_PATH) + let sops_path = (config-get "sops.config_path" | default "" | str replace "KLOUD_PATH" $env.PROVISIONING_KLOUD_PATH) if $sops_path != "" { $env.PROVISIONING_SOPS = $sops_path } else if $env.CURRENT_KLOUD_PATH? != null and ($env.CURRENT_INFRA_PATH | is -not-empty) { $env.PROVISIONING_SOPS = (get_def_sops $env.CURRENT_KLOUD_PATH) } - let kage_path = ($context | get -o "kage_path" | default "" | str replace "KLOUD_PATH" $env.PROVISIONING_KLOUD_PATH) + let kage_path = (config-get "sops.key_path" | default "" | str replace "KLOUD_PATH" $env.PROVISIONING_KLOUD_PATH) if $kage_path != "" { $env.PROVISIONING_KAGE = $kage_path } else if $env.CURRENT_KLOUD_PATH? != null and ($env.CURRENT_INFRA_PATH | is-not-empty) { @@ -114,7 +107,7 @@ export-env { exit 1 } } - $env.PROVISIONING_OUT = ($env | get -o PROVISIONING_OUT| default "") + $env.PROVISIONING_OUT = ($env.PROVISIONING_OUT? | default "") if ($env.PROVISIONING_OUT | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = true # if ($env.PROVISIONING_OUT | str ends-with ".yaml") or ($env.PROVISIONING_OUT | str ends-with ".yml") { @@ -139,19 +132,19 @@ export-env { # Extension System Configuration $env.PROVISIONING_EXTENSIONS_PATH = ($env.PROVISIONING_EXTENSIONS_PATH? | default - ($context | get -o "extensions_path" | default "") | into string) + (config-get "extensions.path" | default "") | into string) $env.PROVISIONING_EXTENSION_MODE = ($env.PROVISIONING_EXTENSION_MODE? | default - ($context | get -o "extension_mode" | default "full") | into string) + (config-get "extensions.mode" | default "full") | into string) $env.PROVISIONING_PROFILE = ($env.PROVISIONING_PROFILE? | default - ($context | get -o "profile" | default "") | into string) + (config-get "extensions.profile" | default "") | into string) $env.PROVISIONING_ALLOWED_EXTENSIONS = ($env.PROVISIONING_ALLOWED_EXTENSIONS? | default - ($context | get -o "allowed_extensions" | default "") | into string) + (config-get "extensions.allowed" | default "") | into string) $env.PROVISIONING_BLOCKED_EXTENSIONS = ($env.PROVISIONING_BLOCKED_EXTENSIONS? | default - ($context | get -o "blocked_extensions" | default "") | into string) + (config-get "extensions.blocked" | default "") | into string) # Custom paths for extensions $env.PROVISIONING_CUSTOM_PROVIDERS = ($env.PROVISIONING_CUSTOM_PROVIDERS? | default "" | into string) @@ -213,18 +206,18 @@ export def "show_env" [ PROVISIONING_KEYS_PATH: $env.PROVISIONING_KEYS_PATH, PROVISIONING_USE_KCL: $"($env.PROVISIONING_USE_KCL)", - PROVISIONING_J2_PARSER: ($env | get -o PROVISIONING_J2_PARSER | default ""), + PROVISIONING_J2_PARSER: ($env.PROVISIONING_J2_PARSER? | default ""), PROVISIONING_URL: $env.PROVISIONING_URL, PROVISIONING_USE_SOPS: $env.PROVISIONING_USE_SOPS, PROVISIONING_LAST_ERROR: $env.PROVISIONING_LAST_ERROR, - CURRENT_KLOUD_PATH: ($env | get -o CURRENT_INFRA_PATH | default ""), + CURRENT_KLOUD_PATH: ($env.CURRENT_INFRA_PATH? | default ""), - PROVISIONING_SOPS: ($env | get -o PROVISIONING_SOPS | default ""), + PROVISIONING_SOPS: ($env.PROVISIONING_SOPS? | default ""), - PROVISIONING_KAGE: ($env | get -o PROVISIONING_KAGE | default ""), + PROVISIONING_KAGE: ($env.PROVISIONING_KAGE? | default ""), PROVISIONING_OUT: $env.PROVISIONING_OUT, }; diff --git a/core/nulib/lib_provisioning/utils/git-commit-msg.nu b/core/nulib/lib_provisioning/utils/git-commit-msg.nu new file mode 100644 index 0000000..64d7984 --- /dev/null +++ b/core/nulib/lib_provisioning/utils/git-commit-msg.nu @@ -0,0 +1,149 @@ +# Git commit message generator +# Generates a commit message file based on current changes without creating a commit + +# Generate commit message file based on staged and unstaged changes +export def "generate-commit-message" [ + --file (-f): string = "COMMIT_MSG.txt" # Output file for commit message + --staged (-s): bool = false # Only consider staged changes + --unstaged (-u): bool = false # Only consider unstaged changes +] -> nothing { + # Determine what changes to analyze + let analyze_staged = if $staged or (not $unstaged) { true } else { false } + let analyze_unstaged = if $unstaged or (not $staged) { true } else { false } + + # Get git status + let git_status = (git status --porcelain | lines | where { $in | str length > 0 }) + + if ($git_status | is-empty) { + print "No changes to commit" + return + } + + # Analyze changes + mut changes = [] + mut files_modified = [] + mut files_added = [] + mut files_deleted = [] + + for line in $git_status { + let status_code = ($line | str substring 0..2) + let file_path = ($line | str substring 3..) + + # Parse git status codes + match $status_code { + " M" => { if $analyze_unstaged { $files_modified = ($files_modified | append $file_path) } } + "M " => { if $analyze_staged { $files_modified = ($files_modified | append $file_path) } } + "MM" => { $files_modified = ($files_modified | append $file_path) } + "A " => { if $analyze_staged { $files_added = ($files_added | append $file_path) } } + "??" => { if $analyze_unstaged { $files_added = ($files_added | append $file_path) } } + " D" => { if $analyze_unstaged { $files_deleted = ($files_deleted | append $file_path) } } + "D " => { if $analyze_staged { $files_deleted = ($files_deleted | append $file_path) } } + _ => {} + } + } + + # Get recent commit messages for style reference + let recent_commits = try { + git log --oneline -5 | lines + } catch { + [] + } + + # Analyze file types and changes + let config_files = ($files_modified | where { $in | str ends-with ".toml" or $in | str ends-with ".nu" or $in | str ends-with ".yaml" }) + let core_files = ($files_modified | where { $in | str contains "core/" }) + let provider_files = ($files_modified | where { $in | str contains "provider" }) + + # Generate commit message based on changes + mut commit_type = "chore" + mut commit_scope = "" + mut commit_description = "" + + # Determine commit type and scope + if (not ($files_added | is-empty)) { + $commit_type = "feat" + $commit_description = "add new functionality" + } else if (not ($files_deleted | is-empty)) { + $commit_type = "refactor" + $commit_description = "remove unused components" + } else if (not ($config_files | is-empty)) { + $commit_type = "config" + $commit_scope = "system" + $commit_description = "update configuration settings" + } else if (not ($core_files | is-empty)) { + $commit_type = "refactor" + $commit_scope = "core" + $commit_description = "improve core functionality" + } else if (not ($provider_files | is-empty)) { + $commit_type = "feat" + $commit_scope = "providers" + $commit_description = "enhance provider capabilities" + } else { + $commit_type = "chore" + $commit_description = "update project files" + } + + # Build commit message + mut commit_message = if ($commit_scope | is-empty) { + $"($commit_type): ($commit_description)" + } else { + $"($commit_type)\(($commit_scope)\): ($commit_description)" + } + + # Add details section + mut details = [] + + if (not ($files_added | is-empty)) { + $details = ($details | append $"- Add: ($files_added | str join ', ')") + } + + if (not ($files_modified | is-empty)) { + $details = ($details | append $"- Update: ($files_modified | str join ', ')") + } + + if (not ($files_deleted | is-empty)) { + $details = ($details | append $"- Remove: ($files_deleted | str join ', ')") + } + + # Create full commit message + let full_message = if ($details | is-empty) { + $commit_message + } else { + $"($commit_message)\n\n($details | str join '\n')" + } + + # Write to file + $full_message | save $file + + print $"Commit message generated and saved to: ($file)" + print "\nGenerated message:" + print "==================" + print $full_message +} + +# Show current git changes that would be included in commit message +export def "show-commit-changes" [] -> table { + let status_output = (git status --porcelain | lines | where { $in | str length > 0 }) + + $status_output | each { |line| + let status_code = ($line | str substring 0..2) + let file_path = ($line | str substring 3..) + + let change_type = match $status_code { + " M" => "Modified (unstaged)" + "M " => "Modified (staged)" + "MM" => "Modified (both)" + "A " => "Added (staged)" + "??" => "Untracked" + " D" => "Deleted (unstaged)" + "D " => "Deleted (staged)" + _ => $status_code + } + + { + file: $file_path, + status: $change_type, + code: $status_code + } + } +} \ No newline at end of file diff --git a/core/versions b/core/versions index 55a9ed6..beb56d2 100644 --- a/core/versions +++ b/core/versions @@ -1,4 +1,4 @@ -NU_VERSION="0.105.2" +NU_VERSION="0.107.1" NU_SOURCE="https://github.com/nushell/nushell/releases" NU_TAGS="https://github.com/nushell/nushell/tags" NU_SITE="https://www.nushell.sh/" diff --git a/utils/commit-msg.nu b/utils/commit-msg.nu new file mode 100644 index 0000000..62bcaab --- /dev/null +++ b/utils/commit-msg.nu @@ -0,0 +1,198 @@ +# Git commit message generator for Claude Code +# Generates a commit message file based on current changes without creating a commit +# No signatures or automated text added + +# Generate commit message file based on staged and unstaged changes +export def "commit-msg" [ + --file (-f): string = "COMMIT_MSG.txt" # Output file for commit message + --staged (-s): bool = false # Only consider staged changes + --unstaged (-u): bool = false # Only consider unstaged changes + --show: bool = false # Show changes without generating message +] -> nothing { + + if $show { + show-git-changes + return + } + + # Determine what changes to analyze + let analyze_staged = if $staged or (not $unstaged) { true } else { false } + let analyze_unstaged = if $unstaged or (not $staged) { true } else { false } + + # Get git status + let git_status = (git status --porcelain | lines | where { $in | str length > 0 }) + + if ($git_status | is-empty) { + print "No changes to commit" + return + } + + # Analyze changes + mut files_modified = [] + mut files_added = [] + mut files_deleted = [] + + for line in $git_status { + let status_code = ($line | str substring 0..2) + let file_path = ($line | str substring 3..) + + # Parse git status codes + match $status_code { + " M" => { if $analyze_unstaged { $files_modified = ($files_modified | append $file_path) } } + "M " => { if $analyze_staged { $files_modified = ($files_modified | append $file_path) } } + "MM" => { $files_modified = ($files_modified | append $file_path) } + "A " => { if $analyze_staged { $files_added = ($files_added | append $file_path) } } + "??" => { if $analyze_unstaged { $files_added = ($files_added | append $file_path) } } + " D" => { if $analyze_unstaged { $files_deleted = ($files_deleted | append $file_path) } } + "D " => { if $analyze_staged { $files_deleted = ($files_deleted | append $file_path) } } + _ => {} + } + } + + # Analyze file types and changes + let config_files = ($files_modified | where { $in | str ends-with ".toml" or $in | str ends-with ".nu" or $in | str ends-with ".yaml" }) + let core_files = ($files_modified | where { $in | str contains "core/" }) + let provider_files = ($files_modified | where { $in | str contains "provider" }) + let migration_files = ($files_modified | where { $in | str contains "migration" }) + + # Generate commit message based on changes + mut commit_type = "chore" + mut commit_scope = "" + mut commit_description = "" + + # Determine commit type and scope based on project patterns + if (not ($files_added | is-empty)) { + $commit_type = "feat" + if ($files_added | any { $in | str contains "taskserv" }) { + $commit_scope = "taskserv" + $commit_description = "add new task service" + } else if ($files_added | any { $in | str contains "provider" }) { + $commit_scope = "provider" + $commit_description = "add new provider" + } else { + $commit_description = "add new functionality" + } + } else if (not ($files_deleted | is-empty)) { + $commit_type = "refactor" + $commit_description = "remove unused components" + } else if (not ($migration_files | is-empty)) { + $commit_type = "refactor" + $commit_scope = "config" + $commit_description = "continue config-driven architecture migration" + } else if (not ($config_files | is-empty)) { + if ($config_files | any { $in | str contains "config.defaults.toml" }) { + $commit_type = "feat" + $commit_scope = "config" + $commit_description = "enhance configuration system" + } else { + $commit_type = "config" + $commit_description = "update configuration settings" + } + } else if (not ($core_files | is-empty)) { + if ($core_files | any { $in | str contains "env.nu" }) { + $commit_type = "refactor" + $commit_scope = "env" + $commit_description = "migrate from ENV to config-driven approach" + } else { + $commit_type = "refactor" + $commit_scope = "core" + $commit_description = "improve core functionality" + } + } else if (not ($provider_files | is-empty)) { + $commit_type = "feat" + $commit_scope = "providers" + $commit_description = "enhance provider capabilities" + } else { + $commit_type = "chore" + $commit_description = "update project files" + } + + # Build commit message + let commit_message = if ($commit_scope | is-empty) { + $"($commit_type): ($commit_description)" + } else { + $"($commit_type)\(($commit_scope)\): ($commit_description)" + } + + # Add details section for complex changes + mut details = [] + + if ($files_modified | length) > 5 { + $details = ($details | append $"- Update ($files_modified | length) files across multiple modules") + } else if (not ($files_modified | is-empty)) { + $details = ($details | append $"- Update: ($files_modified | str join ', ')") + } + + if (not ($files_added | is-empty)) { + $details = ($details | append $"- Add: ($files_added | str join ', ')") + } + + if (not ($files_deleted | is-empty)) { + $details = ($details | append $"- Remove: ($files_deleted | str join ', ')") + } + + # Create full commit message + let full_message = if ($details | is-empty) { + $commit_message + } else { + $"($commit_message)\n\n($details | str join '\n')" + } + + # Write to file + $full_message | save $file + + print $"Commit message saved to: ($file)" + print "\nGenerated message:" + print "==================" + print $full_message +} + +# Show current git changes that would be included in commit message +export def "show-git-changes" [] -> table { + let status_output = (git status --porcelain | lines | where { $in | str length > 0 }) + + if ($status_output | is-empty) { + print "No changes found" + return [] + } + + $status_output | each { |line| + let status_code = ($line | str substring 0..2) + let file_path = ($line | str substring 3..) + + let change_type = match $status_code { + " M" => "Modified (unstaged)" + "M " => "Modified (staged)" + "MM" => "Modified (both)" + "A " => "Added (staged)" + "??" => "Untracked" + " D" => "Deleted (unstaged)" + "D " => "Deleted (staged)" + _ => $status_code + } + + { + file: $file_path, + status: $change_type, + code: $status_code + } + } +} + +# Quick commit message for common patterns +export def "quick-commit" [ + pattern: string # Pattern: config, fix, feat, refactor, docs, test +] -> nothing { + let message = match $pattern { + "config" => "config: update configuration settings" + "fix" => "fix: resolve issue in functionality" + "feat" => "feat: add new functionality" + "refactor" => "refactor: improve code structure" + "docs" => "docs: update documentation" + "test" => "test: add or update tests" + _ => $"chore: ($pattern)" + } + + $message | save "COMMIT_MSG.txt" + print $"Quick commit message saved: ($message)" +} \ No newline at end of file