From 47fac2223085f1eb1cf6c18036d2b0e6fefb0c08 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Thu, 14 May 2026 00:28:20 +0200 Subject: [PATCH] chore: auto-commit (799 archivos) - .claude/CLAUDE.md - .claude/commands/subagentes.md - .claude/rules/INDEX.md - .mcp.json - bash/functions/cybersecurity/analyze_dns.md - bash/functions/cybersecurity/audit_http_headers.md - bash/functions/cybersecurity/audit_ssh_config.md - bash/functions/cybersecurity/check_firewall.md - bash/functions/cybersecurity/detect_suspicious_users.md - bash/functions/cybersecurity/encrypt_file.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/CLAUDE.md | 145 ++++++-- .claude/commands/autonomous-task.md | 121 ++++++ .claude/commands/fn_claude.md | 191 ++++++++++ .claude/commands/subagentes.md | 17 + .claude/rules/INDEX.md | 3 + .claude/rules/capability_groups.md | 60 +++ .claude/rules/delegation.md | 42 +++ .claude/rules/registry_calls.md | 132 +++++++ .claude/scripts/hook_call_monitor.sh | 234 ++++++++++++ .claude/scripts/hook_capability_tag_gate.sh | 107 ++++++ .../scripts/hook_registry_first_reminder.sh | 72 ++++ .mcp.json | 7 +- bash/functions/cybersecurity/analyze_dns.md | 2 +- .../cybersecurity/audit_http_headers.md | 2 +- .../cybersecurity/audit_ssh_config.md | 2 +- .../functions/cybersecurity/check_firewall.md | 2 +- .../cybersecurity/detect_suspicious_users.md | 2 +- bash/functions/cybersecurity/encrypt_file.md | 2 +- .../cybersecurity/enumerate_subdomains.md | 2 +- .../cybersecurity/generate_password.md | 2 +- bash/functions/cybersecurity/geolocate_ip.md | 2 +- .../cybersecurity/inspect_ssl_cert.md | 2 +- .../cybersecurity/list_active_connections.md | 2 +- .../cybersecurity/verify_file_hash.md | 2 +- bash/functions/infra/analyze_disk_space.md | 2 +- bash/functions/infra/android_app_clear.md | 2 +- bash/functions/infra/android_app_info.md | 2 +- bash/functions/infra/android_app_kill.md | 2 +- bash/functions/infra/android_app_launch.md | 2 +- bash/functions/infra/android_app_uninstall.md | 2 +- bash/functions/infra/android_emu_battery.md | 2 +- bash/functions/infra/android_emu_geo_fix.md | 2 +- bash/functions/infra/android_emu_rotate.md | 2 +- .../functions/infra/android_input_keyevent.md | 2 +- bash/functions/infra/android_input_swipe.md | 2 +- bash/functions/infra/android_input_tap.md | 2 +- bash/functions/infra/android_input_text.md | 2 +- bash/functions/infra/android_pull.md | 2 +- bash/functions/infra/android_push.md | 2 +- bash/functions/infra/android_screen_record.md | 2 +- bash/functions/infra/android_screenshot.md | 2 +- bash/functions/infra/android_shell.md | 2 +- bash/functions/infra/append_diary_entry.md | 2 +- bash/functions/infra/build_cpp_linux.md | 2 +- bash/functions/infra/build_wasm_cpp_app.md | 2 +- bash/functions/infra/cuda_toolkit_check.md | 2 +- bash/functions/infra/detect_wsl.md | 2 +- bash/functions/infra/e2e_run_cpp_windows.md | 2 +- bash/functions/infra/frontend_doctor.md | 2 +- .../infra/git_hook_audit_app_drift.md | 2 +- bash/functions/infra/gitea_create_webhook.md | 2 +- bash/functions/infra/gitea_list_repos.md | 2 +- bash/functions/infra/gradle_clean.md | 2 +- bash/functions/infra/install_cpp_deps.md | 2 +- bash/functions/infra/install_mantine.md | 2 +- bash/functions/infra/install_nodejs.md | 2 +- bash/functions/infra/install_nordvpn.md | 2 +- bash/functions/infra/install_pnpm.md | 2 +- bash/functions/infra/install_python312.md | 2 +- bash/functions/infra/install_uv.md | 2 +- bash/functions/infra/install_volta.md | 2 +- bash/functions/infra/keepass_delete.md | 2 +- bash/functions/infra/keepass_generate.md | 2 +- bash/functions/infra/keepass_get.md | 2 +- bash/functions/infra/keepass_list.md | 2 +- bash/functions/infra/keepass_search.md | 2 +- bash/functions/infra/keepass_set.md | 2 +- bash/functions/infra/list_listening_ports.md | 2 +- bash/functions/infra/nordvpn_connect.md | 2 +- bash/functions/infra/nordvpn_disconnect.md | 2 +- bash/functions/infra/nordvpn_get_ip.md | 2 +- bash/functions/infra/nordvpn_list_cities.md | 2 +- .../functions/infra/nordvpn_list_countries.md | 2 +- bash/functions/infra/nordvpn_set_protocol.md | 2 +- bash/functions/infra/nordvpn_status.md | 2 +- bash/functions/infra/pass_delete.md | 2 +- bash/functions/infra/pass_generate.md | 2 +- bash/functions/infra/pass_list.md | 2 +- bash/functions/infra/pass_sync.md | 2 +- bash/functions/infra/port_kill.md | 2 +- .../infra/pre_commit_hook_install.md | 2 +- bash/functions/infra/systemd_local_restart.md | 2 +- .../infra/systemd_local_uninstall.md | 2 +- bash/functions/infra/tail_journal.md | 2 +- bash/functions/infra/tbd_branch_create.md | 2 +- bash/functions/infra/tbd_branch_finish.md | 2 +- bash/functions/infra/telemetry_prelude.md | 55 +++ bash/functions/infra/telemetry_prelude.sh | 124 +++++++ bash/functions/infra/wait_for_http.md | 2 +- bash/functions/infra/wait_for_port.md | 2 +- bash/functions/pipelines/compile_cpp_app.md | 2 +- bash/functions/pipelines/full_git_pull.md | 2 +- bash/functions/pipelines/full_git_push.md | 2 +- .../pipelines/generate_capability_doc.md | 111 ++++++ .../pipelines/generate_capability_doc.sh | 231 ++++++++++++ .../pipelines/install_systemd_service.md | 2 +- .../pipelines/propose_capability_groups.md | 85 +++++ .../pipelines/propose_capability_groups.sh | 346 ++++++++++++++++++ bash/functions/shell/bash_check_deps.md | 2 +- bash/functions/shell/bash_confirm.md | 2 +- bash/functions/shell/bash_handle_error.md | 2 +- bash/functions/shell/bash_safe_run.md | 2 +- bash/functions/shell/convert_text_case.md | 2 +- bash/functions/shell/git_clean_branches.md | 2 +- bash/functions/shell/git_log_visual.md | 2 +- bash/functions/shell/git_push_all_remotes.md | 2 +- bash/functions/shell/git_repo_status.md | 2 +- bash/functions/shell/run_steps.md | 2 +- cmd/fn/doctor.go | 72 ++++ cmd/fn/run.go | 54 ++- cpp/functions/core/app_menubar.md | 2 +- cpp/functions/core/bezier_editor.md | 2 +- cpp/functions/core/docking_layout.md | 2 +- cpp/functions/core/fps_overlay.md | 2 +- cpp/functions/core/icon_font.md | 2 +- cpp/functions/core/layout_storage.md | 2 +- cpp/functions/core/layout_storage_sqlite.md | 2 +- cpp/functions/core/memory_overlay.md | 2 +- cpp/functions/core/parallel_for.md | 2 +- cpp/functions/core/plot_theme.md | 2 +- cpp/functions/core/selectable_text.md | 2 +- cpp/functions/core/sidebar.md | 2 +- cpp/functions/core/slider.md | 2 +- cpp/functions/core/sql_workbench.md | 2 +- cpp/functions/core/tab_container.md | 2 +- cpp/functions/core/time_series_buffer.md | 2 +- cpp/functions/core/timeline.md | 2 +- cpp/functions/core/tracy_zone.md | 2 +- cpp/functions/datascience/beta_dist.md | 2 +- cpp/functions/datascience/drawdown.md | 2 +- .../datascience/mc_metropolis_hastings_gpu.md | 2 +- .../datascience/mc_random_walk_2d_gpu.md | 2 +- .../datascience/mc_session_sim_gpu.md | 2 +- .../datascience/metropolis_hastings.md | 2 +- cpp/functions/datascience/rhat_ess.md | 2 +- .../datascience/samples_to_grid_2d.md | 2 +- cpp/functions/datascience/stats_summary.md | 2 +- cpp/functions/gfx/gl_texture_load.md | 2 +- cpp/functions/gfx/gpu_check.md | 2 +- cpp/functions/gfx/gpu_histogram_1d.md | 2 +- cpp/functions/gfx/gpu_histogram_2d.md | 2 +- cpp/functions/gfx/gpu_reduce.md | 2 +- cpp/functions/infra/job_cache_sha256.md | 2 +- cpp/functions/viz/candlestick.md | 2 +- cpp/functions/viz/chord.md | 2 +- cpp/functions/viz/contour.md | 2 +- cpp/functions/viz/gauge.md | 2 +- cpp/functions/viz/histogram.md | 2 +- cpp/functions/viz/mesh_viewer.md | 2 +- cpp/functions/viz/sankey.md | 2 +- cpp/functions/viz/scatter_3d.md | 2 +- cpp/functions/viz/surface_plot_3d.md | 2 +- cpp/functions/viz/treemap.md | 2 +- cpp/functions/viz/voronoi.md | 2 +- dev/autonomous_protected_paths.json | 40 ++ ...onomous-agent-loop-self-iterating-tasks.md | 32 +- ...call-standardization-and-usage-tracking.md | 161 ++++++++ ...ude-md-delegation-and-capability-groups.md | 261 +++++++++++++ .../0062-deprecate-unused-core-functions.md | 17 +- docs/capabilities/INDEX.md | 43 +++ docs/capabilities/android.md | 54 +++ docs/capabilities/bigquery.md | 43 +++ docs/capabilities/docker.md | 55 +++ docs/capabilities/doctor.md | 48 +++ docs/capabilities/metabase.md | 123 +++++++ docs/capabilities/nlp.md | 50 +++ docs/capabilities/notebook.md | 58 +++ fn_operations/migrations/006_task_runs.sql | 22 ++ frontend/functions/core/api_client.md | 2 +- frontend/functions/core/chart_colors.md | 2 +- frontend/functions/core/format_compact.md | 2 +- frontend/functions/core/get_computed_color.md | 2 +- frontend/functions/core/get_series_color.md | 2 +- .../functions/core/use_debounced_search.md | 2 +- frontend/functions/core/use_fetch.md | 2 +- frontend/functions/core/use_form.md | 2 +- .../functions/core/use_infinite_scroll.md | 2 +- frontend/functions/core/use_mutation.md | 2 +- frontend/functions/core/use_sse.md | 2 +- frontend/functions/core/use_websocket.md | 2 +- frontend/functions/ui/accordion.md | 2 +- frontend/functions/ui/action_icon.md | 2 +- frontend/functions/ui/alert.md | 2 +- frontend/functions/ui/analytics_page.md | 2 +- frontend/functions/ui/area_chart.md | 2 +- frontend/functions/ui/auth_form.md | 2 +- frontend/functions/ui/autocomplete.md | 2 +- frontend/functions/ui/avatar.md | 2 +- frontend/functions/ui/badge.md | 2 +- frontend/functions/ui/bar_chart.md | 2 +- frontend/functions/ui/breadcrumb.md | 2 +- frontend/functions/ui/button.md | 2 +- frontend/functions/ui/card.md | 2 +- frontend/functions/ui/checkbox.md | 2 +- frontend/functions/ui/chip.md | 2 +- frontend/functions/ui/color_input.md | 2 +- frontend/functions/ui/color_picker_grid.md | 2 +- frontend/functions/ui/command.md | 2 +- frontend/functions/ui/crud_page.md | 2 +- frontend/functions/ui/dashboard_layout.md | 2 +- frontend/functions/ui/data_table.md | 2 +- frontend/functions/ui/date_picker_input.md | 2 +- frontend/functions/ui/detail_page.md | 2 +- frontend/functions/ui/dialog.md | 2 +- frontend/functions/ui/dropdown_menu.md | 2 +- frontend/functions/ui/dropzone.md | 2 +- frontend/functions/ui/empty_state.md | 2 +- frontend/functions/ui/error_page.md | 2 +- frontend/functions/ui/file_input.md | 2 +- frontend/functions/ui/form_field.md | 2 +- frontend/functions/ui/funnel_chart.md | 2 +- .../functions/ui/graph/graph_container.md | 2 +- frontend/functions/ui/heatmap_grid.md | 2 +- frontend/functions/ui/indicator.md | 2 +- frontend/functions/ui/input.md | 2 +- frontend/functions/ui/kpi_card.md | 2 +- frontend/functions/ui/label.md | 2 +- frontend/functions/ui/line_chart.md | 2 +- frontend/functions/ui/loading_overlay.md | 2 +- frontend/functions/ui/month_heatmap.md | 2 +- frontend/functions/ui/multi_select.md | 2 +- frontend/functions/ui/nav_link.md | 2 +- frontend/functions/ui/number_input.md | 2 +- frontend/functions/ui/page_header.md | 2 +- frontend/functions/ui/pagination.md | 2 +- frontend/functions/ui/password_input.md | 2 +- frontend/functions/ui/pie_chart.md | 2 +- frontend/functions/ui/pin_input.md | 2 +- frontend/functions/ui/popover.md | 2 +- frontend/functions/ui/progress_bar.md | 2 +- frontend/functions/ui/radio_group.md | 2 +- frontend/functions/ui/rating.md | 2 +- frontend/functions/ui/ring_progress.md | 2 +- frontend/functions/ui/search_bar.md | 2 +- frontend/functions/ui/segmented_control.md | 2 +- frontend/functions/ui/select.md | 2 +- frontend/functions/ui/settings_page.md | 2 +- frontend/functions/ui/sheet.md | 2 +- frontend/functions/ui/simple_select.md | 2 +- frontend/functions/ui/skeleton.md | 2 +- frontend/functions/ui/slider.md | 2 +- frontend/functions/ui/sparkline.md | 2 +- frontend/functions/ui/stepper.md | 2 +- frontend/functions/ui/sticker_picker.md | 2 +- frontend/functions/ui/switch_toggle.md | 2 +- frontend/functions/ui/tabs.md | 2 +- frontend/functions/ui/tags_input.md | 2 +- frontend/functions/ui/textarea.md | 2 +- frontend/functions/ui/timeline.md | 2 +- frontend/functions/ui/toast.md | 2 +- frontend/functions/ui/tooltip.md | 2 +- frontend/functions/ui/use_animated_canvas.md | 2 +- frontend/functions/ui/use_wails_mutation.md | 2 +- frontend/functions/ui/use_wails_query.md | 2 +- frontend/functions/ui/use_wails_stream.md | 2 +- functions/browser/cdp_click_text.md | 2 +- functions/browser/cdp_har_record.md | 2 +- functions/browser/cdp_list_tabs.md | 2 +- functions/browser/cdp_new_tab.md | 2 +- functions/browser/cdp_set_cookie.md | 2 +- functions/core/all_slice.md | 2 +- functions/core/any_slice.md | 2 +- functions/core/assert_contains_all.md | 2 +- functions/core/assert_json_equal.md | 2 +- functions/core/chunk.md | 2 +- functions/core/compare_versions.md | 2 +- functions/core/compose2.md | 2 +- functions/core/const_func.md | 2 +- functions/core/cron_match.md | 2 +- functions/core/curry2.md | 2 +- functions/core/detect_cycle.md | 2 +- functions/core/drop.md | 2 +- functions/core/filter_slice.md | 2 +- functions/core/find_index.md | 2 +- functions/core/flat_map_slice.md | 2 +- functions/core/flatten.md | 2 +- functions/core/flip.md | 2 +- functions/core/generate_id.md | 2 +- functions/core/golden_diff.md | 2 +- functions/core/group_by.md | 2 +- functions/core/html_to_markdown.md | 2 +- functions/core/identity.md | 2 +- functions/core/join_by_key.md | 2 +- functions/core/longest_common_prefix.md | 2 +- functions/core/map_concurrent.md | 2 +- functions/core/map_slice.md | 2 +- functions/core/memoize.md | 2 +- functions/core/parse_version.md | 2 +- functions/core/partial2.md | 2 +- functions/core/partition.md | 2 +- functions/core/pipe2.md | 2 +- functions/core/pipe3.md | 2 +- functions/core/pipeline.md | 2 +- functions/core/reduce.md | 2 +- functions/core/rel_or_full.md | 2 +- functions/core/rewrite_rule.md | 2 +- functions/core/split_command_and_arg.md | 2 +- functions/core/take.md | 2 +- functions/core/test_capture_logs.md | 2 +- functions/core/test_db_seed.md | 2 +- functions/core/test_db_setup.md | 2 +- functions/core/test_env_set.md | 2 +- functions/core/test_fixture_load.md | 2 +- functions/core/test_http_server.md | 2 +- functions/core/uncurry2.md | 2 +- functions/core/unique.md | 2 +- functions/core/validate_struct_fields.md | 2 +- functions/core/zip.md | 2 +- .../cybersecurity/detect_sql_injection.md | 2 +- functions/cybersecurity/entropy_shannon.md | 2 +- functions/cybersecurity/extract_iocs.md | 2 +- functions/cybersecurity/extract_urls.md | 2 +- functions/cybersecurity/fetch_http_headers.md | 2 +- functions/cybersecurity/hash_md5.md | 2 +- functions/cybersecurity/hash_sha256.md | 2 +- functions/cybersecurity/ip_in_range.md | 2 +- functions/cybersecurity/is_base64.md | 2 +- functions/cybersecurity/is_hex.md | 2 +- functions/cybersecurity/jaccard_similarity.md | 2 +- .../cybersecurity/levenshtein_distance.md | 2 +- functions/cybersecurity/lookup_whois.md | 2 +- functions/cybersecurity/normalize_url.md | 2 +- functions/cybersecurity/parse_ip_cidr.md | 2 +- functions/cybersecurity/resolve_dns.md | 2 +- functions/cybersecurity/scan_port_tcp.md | 2 +- functions/datascience/autocorrelation.md | 2 +- functions/datascience/clip.md | 2 +- functions/datascience/detect_outliers.md | 2 +- functions/datascience/diff_entities.md | 2 +- functions/datascience/fetch_data_frame.md | 2 +- functions/datascience/fft.md | 2 +- functions/datascience/group_by.md | 2 +- functions/datascience/histogram.md | 2 +- functions/datascience/impute.md | 2 +- functions/datascience/load_csv.md | 2 +- functions/datascience/load_parquet.md | 2 +- functions/datascience/lorenz_step.md | 2 +- functions/datascience/metrics_drift.md | 2 +- functions/datascience/min_max_scale.md | 2 +- functions/datascience/pivot.md | 2 +- functions/datascience/rolling_window.md | 2 +- functions/datascience/standardize.md | 2 +- functions/datascience/zip_slices.md | 2 +- functions/finance/annualized_volatility.md | 2 +- functions/finance/bollinger_bands.md | 2 +- functions/finance/ema.md | 2 +- functions/finance/fetch_ohlcv.md | 2 +- functions/finance/load_ohlcv_from_duckdb.md | 2 +- functions/finance/log_return.md | 2 +- functions/finance/max_drawdown.md | 2 +- functions/finance/normalize_ohlcv.md | 2 +- functions/finance/rsi.md | 2 +- functions/finance/sharpe_ratio.md | 2 +- functions/finance/sma.md | 2 +- functions/finance/stream_ticks.md | 2 +- functions/finance/tick_to_ohlcv.md | 2 +- functions/finance/vwap.md | 2 +- functions/finance/write_ohlcv_to_parquet.md | 2 +- functions/infra/artefact_doctor.md | 2 +- functions/infra/audit_capability_groups.go | 209 +++++++++++ functions/infra/audit_capability_groups.md | 79 ++++ functions/infra/audit_copied_code.go | 341 +++++++++++++++++ functions/infra/audit_copied_code.md | 74 ++++ functions/infra/audit_cpp_apps.md | 2 +- functions/infra/audit_ml_env.md | 2 +- functions/infra/cache_to_sqlite.md | 2 +- functions/infra/clickhouse_open.md | 2 +- functions/infra/config_dump.md | 2 +- functions/infra/config_from_env.md | 2 +- functions/infra/config_from_file.md | 2 +- functions/infra/config_merge.md | 2 +- functions/infra/config_validate.md | 2 +- functions/infra/crud_define_resource.md | 2 +- functions/infra/crud_generate_table_sql.md | 2 +- functions/infra/crud_register_routes.md | 2 +- functions/infra/db_close.md | 2 +- functions/infra/db_create_table.md | 2 +- functions/infra/db_exec.md | 2 +- functions/infra/db_insert_batch.md | 2 +- functions/infra/db_query.md | 2 +- functions/infra/deploy_app.md | 2 +- functions/infra/deploy_app_remote.md | 2 +- functions/infra/docker_compose_down.md | 2 +- functions/infra/docker_compose_up.md | 2 +- functions/infra/docker_remove_network.md | 2 +- functions/infra/docker_volume_create.md | 2 +- functions/infra/docker_volume_list.md | 2 +- functions/infra/docker_volume_remove.md | 2 +- functions/infra/dotenv_load.md | 2 +- functions/infra/duckdb_open.md | 2 +- functions/infra/e2e_run_checks.md | 2 +- functions/infra/email_build_html.md | 2 +- functions/infra/email_build_text.md | 2 +- functions/infra/email_template_render.md | 2 +- functions/infra/email_with_attachment.md | 2 +- functions/infra/env_require.md | 2 +- functions/infra/env_require_all.md | 2 +- functions/infra/file_delete.md | 2 +- functions/infra/file_serve.md | 2 +- .../generate_proposals_from_telemetry.go | 307 ++++++++++++++++ .../generate_proposals_from_telemetry.md | 62 ++++ functions/infra/go_build_binary.md | 2 +- functions/infra/http_download_file.md | 2 +- functions/infra/http_get_json.md | 2 +- functions/infra/http_post_json.md | 2 +- functions/infra/job_cleanup.md | 2 +- functions/infra/job_enqueue.md | 2 +- functions/infra/job_queue_create.md | 2 +- functions/infra/job_status.md | 2 +- functions/infra/job_worker_pool.md | 2 +- functions/infra/jwt_middleware.md | 2 +- functions/infra/log_debug.md | 2 +- functions/infra/log_error.md | 2 +- functions/infra/log_warn.md | 2 +- functions/infra/logger_middleware.md | 2 +- functions/infra/logger_new.md | 2 +- functions/infra/logger_with.md | 2 +- functions/infra/metabase_auth.md | 2 +- functions/infra/metabase_create_card.md | 2 +- functions/infra/metabase_create_dashboard.md | 2 +- functions/infra/metabase_create_user.md | 2 +- functions/infra/metabase_deactivate_user.md | 2 +- functions/infra/metabase_delete_card.md | 2 +- functions/infra/metabase_delete_dashboard.md | 2 +- functions/infra/metabase_execute_card.md | 2 +- functions/infra/metabase_execute_query.md | 2 +- functions/infra/metabase_get_card.md | 2 +- functions/infra/metabase_get_dashboard.md | 2 +- functions/infra/metabase_get_user.md | 2 +- functions/infra/metabase_list_cards.md | 2 +- functions/infra/metabase_list_dashboards.md | 2 +- functions/infra/metabase_list_users.md | 2 +- functions/infra/metabase_update_card.md | 2 +- functions/infra/metabase_update_dashboard.md | 2 +- functions/infra/metabase_update_user.md | 2 +- functions/infra/migration_create.md | 2 +- functions/infra/migration_down.md | 2 +- functions/infra/migration_status.md | 2 +- functions/infra/migration_validate.md | 2 +- functions/infra/nordvpn_container_run.md | 2 +- functions/infra/nordvpn_container_start.md | 2 +- functions/infra/nordvpn_container_stop.md | 2 +- functions/infra/notify_telegram.md | 2 +- functions/infra/oauth2_auth_url.md | 2 +- functions/infra/oauth2_exchange.md | 2 +- functions/infra/oauth2_refresh.md | 2 +- functions/infra/parse_nordvpn_status.md | 2 +- functions/infra/pc_locations_drift.md | 2 +- functions/infra/pdf_simple_report.md | 2 +- functions/infra/postgres_open.md | 2 +- functions/infra/proposal_from_failure.md | 2 +- functions/infra/rate_limit_middleware.md | 2 +- functions/infra/rate_limiter_cleanup.md | 2 +- functions/infra/rate_limiter_new.md | 2 +- functions/infra/rbac_middleware.md | 2 +- functions/infra/s3_download.md | 2 +- functions/infra/s3_presign_url.md | 2 +- functions/infra/s3_upload.md | 2 +- functions/infra/services_status.md | 2 +- functions/infra/setup_vps_app.md | 2 +- functions/infra/smtp_send.md | 2 +- .../sqlite_apply_versioned_migrations.md | 2 +- functions/infra/sse_handler.md | 2 +- functions/infra/sse_keepalive.md | 2 +- functions/infra/ssh_config_add_entry.md | 2 +- functions/infra/ssh_config_find.md | 2 +- functions/infra/ssh_config_read.md | 2 +- functions/infra/ssh_config_remove_entry.md | 2 +- functions/infra/ssh_config_write.md | 2 +- functions/infra/ssh_download.md | 2 +- functions/infra/ssh_tunnel_close.md | 2 +- functions/infra/ssh_tunnel_open.md | 2 +- functions/infra/stop_app.md | 2 +- functions/infra/systemd_status.md | 2 +- functions/infra/thumbnail_generate.md | 2 +- functions/infra/upload_handler.md | 2 +- functions/infra/vault_diff.md | 2 +- functions/infra/vault_search.md | 2 +- functions/infra/wails_emit_event.md | 2 +- functions/infra/wails_stream_data.md | 2 +- functions/infra/ws_broadcast.md | 2 +- functions/infra/ws_handler.md | 2 +- functions/infra/ws_hub.md | 2 +- functions/infra/ws_send.md | 2 +- functions/ml/genconfig_json_marshal.md | 2 +- functions/ml/sdcli_generate.md | 2 +- functions/ml/sdcli_resolve_binary.md | 2 +- functions/shell/extract_script_description.md | 2 +- functions/shell/run_cmd.md | 2 +- functions/shell/run_pipe.md | 2 +- functions/shell/run_shell.md | 2 +- functions/shell/run_shell_timeout.md | 2 +- functions/shell/which.md | 2 +- functions/tui/clear_screen.md | 2 +- functions/tui/confirm_prompt.md | 2 +- functions/tui/dark_theme.md | 2 +- functions/tui/default_theme.md | 2 +- functions/tui/draw_box.md | 2 +- functions/tui/draw_separator.md | 2 +- functions/tui/hide_cursor.md | 2 +- functions/tui/load_ascii_art.md | 2 +- functions/tui/new_confirm.md | 2 +- functions/tui/new_multi_list.md | 2 +- functions/tui/new_multi_progress.md | 2 +- functions/tui/new_progress.md | 2 +- functions/tui/new_progress_with_styles.md | 2 +- functions/tui/new_spinner_with_style.md | 2 +- functions/tui/new_spinner_with_timeout.md | 2 +- functions/tui/new_styles.md | 2 +- functions/tui/normalize_terminal_output.md | 2 +- functions/tui/print_error.md | 2 +- functions/tui/print_info.md | 2 +- functions/tui/print_muted.md | 2 +- functions/tui/print_success.md | 2 +- functions/tui/print_warning.md | 2 +- functions/tui/quit_msg.md | 2 +- functions/tui/read_dir_autocomplete.md | 2 +- functions/tui/run_model.md | 2 +- functions/tui/run_with_mouse.md | 2 +- functions/tui/show_cursor.md | 2 +- kotlin/functions/core/build_guide_prompt.md | 2 +- .../functions/core/match_pois_to_interests.md | 2 +- .../infra/nominatim_reverse_geocode.md | 2 +- kotlin/functions/infra/ollama_chat.md | 2 +- .../functions/infra/overpass_nearby_pois.md | 2 +- .../functions/infra/win_firewall_add_rule.md | 2 +- .../infra/win_firewall_remove_rule.md | 2 +- .../functions/infra/win_portproxy_add.md | 2 +- .../functions/infra/win_portproxy_remove.md | 2 +- python/functions/bigquery/bq_auth.md | 2 +- python/functions/bigquery/bq_cancel_job.md | 2 +- python/functions/bigquery/bq_copy_table.md | 2 +- .../functions/bigquery/bq_create_dataset.md | 2 +- .../functions/bigquery/bq_create_routine.md | 2 +- python/functions/bigquery/bq_create_table.md | 2 +- .../functions/bigquery/bq_delete_dataset.md | 2 +- .../functions/bigquery/bq_delete_routine.md | 2 +- python/functions/bigquery/bq_delete_table.md | 2 +- python/functions/bigquery/bq_export_to_gcs.md | 2 +- python/functions/bigquery/bq_get_dataset.md | 2 +- python/functions/bigquery/bq_get_job.md | 2 +- python/functions/bigquery/bq_get_table.md | 2 +- python/functions/bigquery/bq_insert_rows.md | 2 +- python/functions/bigquery/bq_list_datasets.md | 2 +- python/functions/bigquery/bq_list_jobs.md | 2 +- python/functions/bigquery/bq_list_routines.md | 2 +- python/functions/bigquery/bq_list_tables.md | 2 +- .../functions/bigquery/bq_load_from_file.md | 2 +- python/functions/bigquery/bq_load_from_gcs.md | 2 +- python/functions/bigquery/bq_preview_rows.md | 2 +- python/functions/bigquery/bq_query.md | 2 +- .../functions/bigquery/bq_update_dataset.md | 2 +- python/functions/bigquery/bq_update_table.md | 2 +- python/functions/core/all_of.md | 2 +- python/functions/core/any_of.md | 2 +- python/functions/core/build_guide_prompt.md | 2 +- .../functions/core/build_tree_from_headers.md | 2 +- python/functions/core/cache_decorator.md | 2 +- .../core/calculate_media_strategy.md | 2 +- .../functions/core/calculate_page_offset.md | 2 +- .../functions/core/call_batch_with_retry.md | 2 +- python/functions/core/chunk.md | 2 +- python/functions/core/circuit_breaker.md | 2 +- python/functions/core/clean_pdf_text.md | 2 +- python/functions/core/coerce_types.md | 2 +- python/functions/core/compose.md | 2 +- .../core/convert_github_to_raw_url.md | 2 +- python/functions/core/create_node_mapping.md | 2 +- .../functions/core/csv_to_parquet_duckdb.md | 2 +- python/functions/core/cursor_paginate.md | 2 +- python/functions/core/docx_to_markdown.md | 2 +- python/functions/core/drop.md | 2 +- python/functions/core/epub_to_markdown.md | 2 +- python/functions/core/excel_to_markdown.md | 2 +- python/functions/core/extract_frontmatter.md | 2 +- .../functions/core/extract_json_from_llm.md | 2 +- .../core/extract_markdown_headers.md | 2 +- python/functions/core/extract_pdf_text.md | 2 +- python/functions/core/fetch_and_parse_url.md | 2 +- python/functions/core/filter_list.md | 2 +- python/functions/core/find.md | 2 +- python/functions/core/find_headings.md | 2 +- python/functions/core/find_index.md | 2 +- python/functions/core/flat_map.md | 2 +- python/functions/core/flatten.md | 2 +- python/functions/core/flatten_tree.md | 2 +- python/functions/core/format_iso8601.md | 2 +- python/functions/core/format_simplified.md | 2 +- .../functions/core/format_tree_structure.md | 2 +- python/functions/core/from_csv.md | 2 +- python/functions/core/from_jsonl.md | 2 +- python/functions/core/generate_html_report.md | 2 +- .../functions/core/generate_pwa_manifest.md | 2 +- .../functions/core/generate_service_worker.md | 2 +- python/functions/core/get_leaf_nodes.md | 2 +- python/functions/core/get_pdf_page_tokens.md | 2 +- python/functions/core/get_text_stats.md | 2 +- python/functions/core/group_by.md | 2 +- .../functions/core/infer_provincia_from_cp.md | 2 +- python/functions/core/is_git_repo_url.md | 2 +- python/functions/core/join_by_key.md | 2 +- python/functions/core/list_to_tree.md | 2 +- .../functions/core/llm_acompletion_retry.md | 2 +- python/functions/core/llm_completion_retry.md | 2 +- python/functions/core/load_translations.md | 2 +- python/functions/core/map_list.md | 2 +- .../functions/core/match_pois_to_interests.md | 2 +- python/functions/core/next_cron_time.md | 2 +- python/functions/core/normalize_for_join.md | 2 +- python/functions/core/page_list_to_groups.md | 2 +- python/functions/core/parse_code_ast.md | 2 +- python/functions/core/parse_git_url.md | 2 +- python/functions/core/parse_iso_datetime.md | 2 +- python/functions/core/parse_llm_json.md | 2 +- python/functions/core/parse_page_range.md | 2 +- python/functions/core/parser_registry.md | 2 +- python/functions/core/partition.md | 2 +- python/functions/core/pdf_to_markdown.md | 2 +- python/functions/core/pipe.md | 2 +- python/functions/core/react_loop.md | 2 +- python/functions/core/reduce_list.md | 2 +- python/functions/core/remove_tree_fields.md | 2 +- python/functions/core/render_template.md | 2 +- python/functions/core/retry_async.md | 2 +- python/functions/core/retry_sync.md | 2 +- python/functions/core/retry_with_backoff.md | 2 +- .../core/retry_with_backoff_async.md | 2 +- .../functions/core/safe_read_csv_fallback.md | 2 +- python/functions/core/sanitize_for_path.md | 2 +- python/functions/core/slugify_ascii.md | 2 +- python/functions/core/smart_split_content.md | 2 +- python/functions/core/t.md | 2 +- python/functions/core/take.md | 2 +- python/functions/core/task_manager.md | 2 +- python/functions/core/to_csv.md | 2 +- python/functions/core/to_jsonl.md | 2 +- python/functions/core/to_pascal_case.md | 2 +- python/functions/core/tree_to_flat_list.md | 2 +- python/functions/core/unique.md | 2 +- python/functions/core/validate_git_ssh_uri.md | 2 +- python/functions/core/validate_json_schema.md | 2 +- python/functions/core/write_node_ids.md | 2 +- python/functions/core/zip_with.md | 2 +- .../cybersecurity/detect_sql_injection.md | 2 +- .../cybersecurity/entropy_shannon.md | 2 +- .../cybersecurity/envelope_decrypt.md | 2 +- .../functions/cybersecurity/extract_urls.md | 2 +- python/functions/cybersecurity/hash_md5.md | 2 +- python/functions/cybersecurity/hash_sha256.md | 2 +- python/functions/cybersecurity/is_base64.md | 2 +- python/functions/cybersecurity/is_hex.md | 2 +- .../cybersecurity/jaccard_similarity.md | 2 +- .../functions/cybersecurity/normalize_url.md | 2 +- .../datascience/aggregate_by_group.md | 2 +- .../datascience/alpha_shape_concave_hull.md | 2 +- .../functions/datascience/autocorrelation.md | 2 +- .../datascience/best_central_tendency.md | 2 +- python/functions/datascience/clip.md | 2 +- .../datascience/detect_distribution_type.md | 2 +- python/functions/datascience/detect_drift.md | 2 +- .../functions/datascience/detect_outliers.md | 2 +- python/functions/datascience/diff_entities.md | 2 +- .../functions/datascience/diff_relations.md | 2 +- .../functions/datascience/estimate_hawkes.md | 2 +- .../datascience/estimate_pareto_alpha.md | 2 +- .../datascience/extract_relations_mrebel.md | 2 +- .../datascience/extract_triples_spacy_es.md | 2 +- .../datascience/fuzzy_merge_adaptive.md | 2 +- python/functions/datascience/histogram.md | 2 +- python/functions/datascience/hotness_score.md | 2 +- python/functions/datascience/impute.md | 2 +- .../datascience/kde_density_levels.md | 2 +- python/functions/datascience/linspace.md | 2 +- python/functions/datascience/melt.md | 2 +- python/functions/datascience/merge_graphs.md | 2 +- python/functions/datascience/min_max_scale.md | 2 +- .../datascience/mrebel_base_load_model.md | 2 +- .../datascience/ops_to_rdf_triples.md | 2 +- python/functions/datascience/pearson.md | 2 +- python/functions/datascience/pivot.md | 2 +- .../functions/datascience/plot_heatmap_log.md | 2 +- python/functions/datascience/plot_kde_2d.md | 2 +- .../functions/datascience/rebel_load_model.md | 2 +- .../datascience/remove_words_from_column.md | 2 +- .../datascience/render_sigma_html.md | 2 +- .../functions/datascience/rolling_window.md | 2 +- python/functions/datascience/standardize.md | 2 +- .../datascience/translate_es_to_en.md | 2 +- .../functions/datascience/words_to_dataset.md | 2 +- .../functions/embedding/embedding_encode.md | 2 +- .../embedding/embedding_save_model.md | 2 +- .../embedding/embedding_search_sqlvec.md | 2 +- .../embedding/embedding_search_usearch.md | 2 +- .../embedding/embedding_store_sqlvec.md | 2 +- .../embedding/embedding_store_usearch.md | 2 +- .../finance/annualized_volatility.md | 2 +- python/functions/finance/bollinger_bands.md | 2 +- python/functions/finance/ema.md | 2 +- .../functions/finance/generate_taker_order.md | 2 +- python/functions/finance/hawkes_intensity.md | 2 +- python/functions/finance/log_return.md | 2 +- python/functions/finance/max_drawdown.md | 2 +- python/functions/finance/rsi.md | 2 +- python/functions/finance/sharpe_ratio.md | 2 +- python/functions/finance/vwap.md | 2 +- python/functions/geo/add_basemap_osm.md | 2 +- .../functions/geo/add_basemap_with_timeout.md | 2 +- python/functions/geo/distance_bucket.md | 2 +- python/functions/geo/extent_with_padding.md | 2 +- python/functions/geo/haversine_km.md | 2 +- python/functions/geo/load_boundary_gdf.md | 2 +- python/functions/geo/valhalla_isochrone.md | 2 +- python/functions/infra/add_header_logo.md | 2 +- .../infra/compress_pdf_ghostscript.md | 2 +- python/functions/infra/config_from_env.md | 2 +- python/functions/infra/dotenv_load.md | 2 +- python/functions/infra/email_build_html.md | 2 +- python/functions/infra/get_logger.md | 2 +- python/functions/infra/http_download_file.md | 2 +- python/functions/infra/http_get_json.md | 2 +- python/functions/infra/http_post_json.md | 2 +- .../infra/nominatim_reverse_geocode.md | 2 +- python/functions/infra/ollama_chat.md | 2 +- python/functions/infra/osm2pgsql_ingest.md | 2 +- .../functions/infra/overpass_nearby_pois.md | 2 +- .../functions/infra/pdf_add_header_footer.md | 2 +- python/functions/infra/pdf_add_image.md | 2 +- python/functions/infra/pdf_add_page.md | 2 +- python/functions/infra/pdf_add_table.md | 2 +- python/functions/infra/pdf_add_text.md | 2 +- python/functions/infra/pdf_create.md | 2 +- python/functions/infra/pdf_from_markdown.md | 2 +- python/functions/infra/pdf_merge.md | 2 +- python/functions/infra/pdf_save.md | 2 +- .../functions/infra/powertoys_config_path.md | 2 +- python/functions/infra/powertoys_restart.md | 2 +- .../functions/infra/powertoys_shortcut_add.md | 2 +- .../infra/powertoys_shortcut_list.md | 2 +- .../infra/powertoys_shortcut_remove.md | 2 +- .../infra/read_file_with_encoding.md | 2 +- python/functions/infra/registry_telemetry.md | 56 +++ python/functions/infra/registry_telemetry.py | 217 +++++++++++ .../infra/render_table_page_pdfpages.md | 2 +- python/functions/infra/safe_extract_zip.md | 2 +- python/functions/infra/scan_directory.md | 2 +- python/functions/infra/smtp_send.md | 2 +- .../functions/infra/vault_profile_dispatch.md | 2 +- .../metabase/metabase_add_dashboard_filter.md | 2 +- .../metabase/metabase_archive_document.md | 2 +- .../metabase/metabase_archive_snippet.md | 2 +- .../metabase/metabase_create_card_alert.md | 2 +- .../metabase_create_dashboard_subscription.md | 2 +- .../metabase_create_document_comment.md | 2 +- .../metabase/metabase_create_model.md | 2 +- .../metabase/metabase_create_snippet.md | 2 +- .../metabase/metabase_dashboard_append_row.md | 2 +- .../metabase/metabase_deactivate_user.md | 2 +- .../metabase/metabase_delete_card.md | 2 +- .../metabase/metabase_delete_document.md | 2 +- .../metabase/metabase_delete_notification.md | 2 +- .../metabase/metabase_execute_card.md | 2 +- .../metabase/metabase_export_card.md | 2 +- .../metabase/metabase_get_snippet.md | 2 +- .../functions/metabase/metabase_get_user.md | 2 +- .../metabase/metabase_list_notifications.md | 2 +- .../metabase/metabase_list_snippets.md | 2 +- .../metabase_mbql_from_source_card.md | 2 +- .../metabase_resolve_document_comment.md | 2 +- python/functions/metabase/metabase_setup.md | 2 +- .../metabase_smartscalar_anothercolumn_viz.md | 2 +- .../metabase_smartscalar_dimension_tag.md | 2 +- .../metabase_smartscalar_kpi_payload.md | 2 +- .../metabase/metabase_smartscalar_kpi_sql.md | 2 +- .../metabase/metabase_update_notification.md | 2 +- .../metabase/metabase_update_user.md | 2 +- .../metabase/prosemirror_card_embed.md | 2 +- python/functions/ml/diffusers_generate.md | 2 +- python/functions/ml/diffusers_load_lora.md | 2 +- .../functions/ml/diffusers_load_pipeline.md | 2 +- .../functions/ml/diffusers_set_scheduler.md | 2 +- python/functions/ml/diffusers_unload.md | 2 +- python/functions/ml/genconfig_load_json.md | 2 +- .../functions/ml/genconfig_to_sdcpp_args.md | 2 +- python/functions/ml/gpu_info.md | 2 +- python/functions/ml/hf_snapshot_download.md | 2 +- .../ml/image_compare_side_by_side.md | 2 +- python/functions/ml/image_grid.md | 2 +- python/functions/ml/image_save_png.md | 2 +- python/functions/ml/safetensors_inspect.md | 2 +- python/functions/ml/sdcpp_python_generate.md | 2 +- python/functions/ml/vram_budget.md | 2 +- python/functions/notebook/jupyter_discover.md | 2 +- python/functions/notebook/jupyter_exec.md | 2 +- python/functions/notebook/jupyter_kernel.md | 2 +- python/functions/notebook/jupyter_read.md | 2 +- python/functions/notebook/jupyter_write.md | 2 +- .../compute_centers_reachability_pipeline.md | 2 +- .../count_points_per_zone_pipeline.md | 2 +- .../pipelines/extract_graph_from_text.md | 2 +- .../pipelines/extraction_pipeline.md | 2 +- .../generate_isochrones_by_zone_pipeline.md | 2 +- .../setup_geo_stack_docker_pipeline.md | 2 +- .../functions/pipelines/tag_unused_pending.md | 52 +++ .../functions/pipelines/tag_unused_pending.py | 172 +++++++++ registry/store.go | 20 +- 805 files changed, 5515 insertions(+), 810 deletions(-) create mode 100644 .claude/commands/autonomous-task.md create mode 100644 .claude/commands/fn_claude.md create mode 100644 .claude/rules/capability_groups.md create mode 100644 .claude/rules/delegation.md create mode 100644 .claude/rules/registry_calls.md create mode 100755 .claude/scripts/hook_call_monitor.sh create mode 100755 .claude/scripts/hook_capability_tag_gate.sh create mode 100755 .claude/scripts/hook_registry_first_reminder.sh create mode 100644 bash/functions/infra/telemetry_prelude.md create mode 100644 bash/functions/infra/telemetry_prelude.sh create mode 100644 bash/functions/pipelines/generate_capability_doc.md create mode 100644 bash/functions/pipelines/generate_capability_doc.sh create mode 100644 bash/functions/pipelines/propose_capability_groups.md create mode 100644 bash/functions/pipelines/propose_capability_groups.sh create mode 100644 dev/autonomous_protected_paths.json create mode 100644 dev/issues/0086-claude-md-delegation-and-capability-groups.md rename dev/issues/{ => completed}/0062-deprecate-unused-core-functions.md (77%) create mode 100644 docs/capabilities/INDEX.md create mode 100644 docs/capabilities/android.md create mode 100644 docs/capabilities/bigquery.md create mode 100644 docs/capabilities/docker.md create mode 100644 docs/capabilities/doctor.md create mode 100644 docs/capabilities/metabase.md create mode 100644 docs/capabilities/nlp.md create mode 100644 docs/capabilities/notebook.md create mode 100644 fn_operations/migrations/006_task_runs.sql create mode 100644 functions/infra/audit_capability_groups.go create mode 100644 functions/infra/audit_capability_groups.md create mode 100644 functions/infra/audit_copied_code.go create mode 100644 functions/infra/audit_copied_code.md create mode 100644 functions/infra/generate_proposals_from_telemetry.go create mode 100644 functions/infra/generate_proposals_from_telemetry.md create mode 100644 python/functions/infra/registry_telemetry.md create mode 100644 python/functions/infra/registry_telemetry.py create mode 100644 python/functions/pipelines/tag_unused_pending.md create mode 100644 python/functions/pipelines/tag_unused_pending.py diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 5eb67bf1..9ed0d2a5 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -18,6 +18,33 @@ Registry personal de codigo reutilizable con busqueda FTS. Diseñado para compos --- +## Delegacion + Capability Groups (REGLA DURA — issue 0086) + +Claude **multiplica capacidades** delegando creacion de funciones a `fn-constructor` y reusandolas inmediatamente. NO escribir logica reutilizable inline. + +### Flujo obligatorio (mismo turno) + +1. **Detectar gap**. Si vas a escribir >=5 lineas de logica reutilizable inline -> STOP. +2. **Spawn `fn-constructor`** via `Agent(subagent_type="fn-constructor", ...)`. Sin preguntar al usuario. +3. **Paralelo**: si hay >1 funcion independiente -> **una sola llamada al Agent tool con N tool_use blocks paralelos** en mismo mensaje. NO serializar. +4. **Tag de grupo obligatorio** (`notebook`, `metabase`, `deploy`, etc.). Ver `docs/capabilities/INDEX.md`. +5. **`fn index`** + **importar + invocar en mismo turno**. No dejar funcion huerfana recien creada. +6. **Auto-verificar**: `fn doctor uses-functions` + `fn doctor unused` si tocas >=3 funciones nuevas. + +### Capability groups + +Cluster de >=3 funciones que comparten dominio operativo. Cada grupo tiene tag plano + pagina madre `docs/capabilities/.md` con: lista de funciones, ejemplo canonico end-to-end, fronteras. + +**Antes de buscar funciones sueltas en una tarea de dominio conocido:** lee `docs/capabilities/.md` para cargar el cluster entero en un solo read. Filtro MCP: `mcp__registry__fn_search query="" tag=""`. + +Reglas completas: `.claude/rules/delegation.md` + `.claude/rules/capability_groups.md`. + +### Telemetria CAPABILITY-GROWTH + +Cada turno el hook `UserPromptSubmit` inyecta `CAPABILITY-GROWTH: created_this_session=X used=Y orphan=Z`. Si `orphan>0` -> integra la funcion antes de cerrar turno o documenta por que. + +--- + ## Explorar el registry (OBLIGATORIO) **SIEMPRE** consulta registry.db antes de escribir codigo, crear funciones, o responder sobre el registry. No uses grep/glob sobre archivos .go/.md — la BD es la fuente de verdad. @@ -39,56 +66,67 @@ Registry personal de codigo reutilizable con busqueda FTS. Diseñado para compos Razones: menos tokens, output estructurado, FTS5 escapado bien (sin gotchas de `column:"valor"`), permisos pre-aprobados, no requiere `cd` ni paths absolutos a `registry.db`. -**Cuando si caer a `sqlite3` (Bash):** SOLO si el MCP no cubre el caso — JOINs custom entre tablas, agregaciones (`COUNT`/`GROUP BY`), introspeccion de schema (`.schema`, `PRAGMA table_info`), o columnas que el MCP no expone. En ese caso, los patrones FTS5 estan documentados abajo. +**La BD contiene el codigo y la documentacion completa** de cada funcion y tipo en los campos `code`, `documentation` y `notes`. Tambien indexados en FTS5 — buscas dentro del codigo directamente. Para leer codigo: `mcp__registry__fn_code `. -**La BD contiene el codigo y la documentacion completa** de cada funcion y tipo en los campos `code`, `documentation` y `notes`. Estos campos tambien estan indexados en FTS5, asi que puedes buscar dentro del codigo y la documentacion directamente. Para leer el codigo de una funcion: `mcp__registry__fn_code` (preferido) o `SELECT code FROM functions WHERE id = '...'` (fallback). +### Ejemplos MCP (usa estos, NO sqlite3) -**Busquedas FTS5 (cuando uses sqlite3 como fallback):** Usa SIEMPRE la tabla FTS5 para buscar tanto por `name` como por `description`. Esto encuentra coincidencias parciales y similares que una busqueda exacta perderia. Usa operadores FTS5: `OR` para ampliar, `*` para prefijos, `NEAR` para proximidad. +Cada llamada MCP se registra en `call_monitor` (issue 0085). Cada `sqlite3 registry.db "SELECT ..."` queda fuera del bucle reactivo y dispara el hook PreToolUse. -```bash -# Busqueda FTS5 por nombre Y descripcion (USAR SIEMPRE ESTE PATRON) -sqlite3 registry.db "SELECT id, kind, purity, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:slice OR description:slice') ORDER BY name;" +``` +# Busqueda basica por nombre/descripcion (FTS5 detras) +mcp__registry__fn_search query="slice" -# FTS5 con prefijo (encuentra slice, slicing, sliced...) -sqlite3 registry.db "SELECT id, kind, purity, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:slic* OR description:slic*') ORDER BY name;" +# Filtros: kind, purity, domain, lang +mcp__registry__fn_search query="filter" kind="function" purity="pure" domain="core" -# FTS5 en tipos -sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE id IN (SELECT id FROM types_fts WHERE types_fts MATCH 'name:result OR description:result') ORDER BY name;" +# Prefijo FTS5 — encuentra slice/slicing/sliced +mcp__registry__fn_search query="slic*" -# FTS5 por semantica de params (composabilidad) -sqlite3 registry.db "SELECT id, json_extract(params_schema, '$.output') FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'params_schema:retornos');" +# Buscar tipos +mcp__registry__fn_search query="result" entity="types" -# Por dominio -sqlite3 registry.db "SELECT id, purity, signature FROM functions WHERE domain = 'finance' ORDER BY name;" +# Apps +mcp__registry__fn_search query="kanban" entity="apps" -# Puras de un dominio -sqlite3 registry.db "SELECT id, signature FROM functions WHERE domain = 'core' AND purity = 'pure' ORDER BY name;" +# Listar dominios +mcp__registry__fn_list_domains -# Tipos por dominio -sqlite3 registry.db "SELECT id, algebraic, description FROM types WHERE domain = 'cybersecurity';" +# Ver una entrada concreta (functions, types, apps, analysis, proposals...) +mcp__registry__fn_show id="filter_slice_go_core" -# Dependencias -sqlite3 registry.db "SELECT id, uses_functions, uses_types FROM functions WHERE uses_functions != '[]';" +# Codigo fuente de una funcion/tipo +mcp__registry__fn_code id="filter_slice_go_core" -# Proposals pendientes -sqlite3 registry.db "SELECT id, kind, status, title FROM proposals WHERE status = 'pending';" +# Quien consume una funcion (consumidores indexados via uses_functions) +mcp__registry__fn_uses id="filter_slice_go_core" -# Schema completo -sqlite3 registry.db ".schema" +# Proposals (pending, approved, ...) +mcp__registry__fn_proposal action="list" status="pending" +mcp__registry__fn_proposal action="show" id="" + +# Diagnostico read-only del registry (artefacts/services/sync/uses-functions/unused/cpp-apps) +mcp__registry__fn_doctor subcommand="artefacts" +mcp__registry__fn_doctor subcommand="sync" ``` -**Regla:** Si necesitas saber si algo existe o hay algo similar, usa `mcp__registry__fn_search` (preferido) o consulta FTS5 con sqlite3 (fallback). No asumas que no existe sin consultar primero. +**Escapado FTS5 (gotcha cuando pasas query libre):** valores con `-`, `.`, `:`, espacios rompen el parser FTS5 si los expones como `column:valor`. El MCP escapa por defecto, pero si construyes una `query` con sintaxis FTS5 explicita, encierra el valor en comillas dobles: -**Escapado FTS5 (gotcha):** despues de `column:` el valor debe ser un solo token alfanumerico ASCII (underscores OK). Cualquier otro caracter (`-`, `.`, `:`, espacios) rompe el parser con `no such column: X` o `syntax error near "."`. Encierra el valor en comillas dobles dentro del MATCH: - -```bash -# MAL: description:single-page → "no such column: page" -# MAL: description:embed.FS → 'syntax error near "."' -# BIEN: -sqlite3 registry.db "SELECT id FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'description:\"single-page\" OR description:\"embed.FS\"');" +``` +# MAL: query="description:single-page" -> "no such column: page" +# BIEN +mcp__registry__fn_search query='description:"single-page" OR description:"embed.FS"' +mcp__registry__fn_search query='description:"react router"' ``` -Tokens multi-palabra tambien necesitan comillas: `description:"react router"`. +### Excepciones autorizadas para sqlite3 directo + +`sqlite3 registry.db` SOLO es legitimo si el MCP no expone la consulta: + +- Introspeccion de schema: `.schema`, `.tables`, `PRAGMA table_info(...)`, `PRAGMA index_list(...)`. +- Agregaciones: `COUNT(*)`, `GROUP BY`, `SUM(...)`, `AVG(...)`. +- JOINs custom entre tablas (ej. `functions JOIN unit_tests ON ...`) no expuestos por el MCP. + +Cualquier `SELECT ... FROM functions/types/apps/proposals WHERE ...` plano se hace via MCP. El hook PreToolUse avisa si ve `sqlite3 registry.db "SELECT ..."`. ### Schema rapido @@ -109,7 +147,7 @@ Tokens multi-palabra tambien necesitan comillas: `description:"react router"`. - `entity_type`: app, analysis, project, vault - `status`: active, missing, archived - Se puebla con `fn sync`, NO con `fn index` -- Consultas: `SELECT * FROM pc_locations WHERE pc_id = 'home-wsl'` +- Consultas: `mcp__registry__fn_doctor subcommand="sync"` (drift PC vs disco) o `sqlite3 registry.db "SELECT ... GROUP BY pc_id"` SOLO para agregaciones que el MCP no expone **FTS5 (columnas buscables):** - `functions_fts`: id, name, description, tags, signature, domain, example, notes, documentation, code, params_schema @@ -118,6 +156,43 @@ Tokens multi-palabra tambien necesitan comillas: `description:"react router"`. --- +## Como invocar funciones del registry (CANONICO) + +Tres patrones, uno por caso de uso. Toda invocacion del agente se loguea en `projects/fn_monitoring/apps/call_monitor/operations.db` para alimentar el bucle reactivo (issue 0085). + +| Caso de uso | Patron canonico | Cuando usar | +|---|---|---| +| **Inspeccionar** registro (buscar, leer codigo, ver dependencias, dominios) | `mcp__registry__fn_search` / `fn_show` / `fn_code` / `fn_uses` / `fn_list_domains` | SIEMPRE para descubrimiento. Reemplaza `sqlite3 registry.db "SELECT ..."` inline. | +| **Ejecutar** UNA funcion o pipeline con sus args | `mcp__registry__fn_run [args]` (preferido) o `./fn run [args]` (fallback CLI) | Cuando hay UN id conocido a lanzar. Despacho automatico por lenguaje. Salida estructurada. | +| **Componer** ad-hoc varias funciones con logica intermedia | Heredoc `python/.venv/bin/python3 - <<'PYEOF' ... PYEOF` IMPORTANDO funciones del registry | Solo cuando hay loops/conditionals/dispatch entre N funciones. Las funciones del registry **se importan**, no se reescriben. | + +Regla decisiva: antes de cada bloque de codigo, decide caso. Si dudas entre 2 y 3, casi siempre es 2 (un MCP run con args). Si el caso 3 se repite con el mismo shape >5 veces entre sesiones, **es candidato a pipeline** en `python/functions/pipelines/`. + +### Antipatrones prohibidos + +| Patron | Por que es malo | Sustituir por | +|---|---|---| +| `sqlite3 registry.db "SELECT ..."` para buscar funciones/tipos | Salta MCP, FTS5 gotchas, sin trazabilidad. Hook PreToolUse ya avisa. | `mcp__registry__fn_search` | +| `python -c "import metabase; print(dir(metabase))"` o `help(metabase)` para descubrir helpers | La fuente de verdad es el registry, no el `__init__.py` | `mcp__registry__fn_search "metabase"` + `mcp__registry__fn_show ` | +| Heredoc que reescribe logica que ya existe como funcion del registry | Reinvento + perdida de capitalizacion | Buscar primero; si falta, delegar a `fn-constructor` (no escribir inline) | +| `client._http.request(...)` directo cuando hay wrapper en el registry | Salta validacion del wrapper y telemetria | Usar wrapper; si la firma no cubre el caso, proponer extension via `fn proposal add` | +| Scripts en `temp/` para composiciones que se repiten | Codigo se pierde y no se monitoriza | Pipeline en `python/functions/pipelines/` o pipeline Bash en `bash/functions/pipelines/` | +| Imports `from import *` en heredoc | Imposible saber que funcion del registry se uso | Imports explicitos `from import , ` | + +Excepciones autorizadas para `sqlite3` directo (no requieren MCP): `.schema`, `.tables`, `PRAGMA table_info`, `COUNT(*) GROUP BY`, JOINs custom entre tablas que el MCP no expone. + +### Trazabilidad y bucle reactivo + +Hook `PostToolUse` en `.claude/settings.local.json` parsea cada comando Bash + cada `mcp__registry__*` y escribe en la `operations.db` del call_monitor. Datos consumidos por: + +1. **Tab "Claude usage" en `registry_dashboard`** — top funciones, latencias, error rate, huerfanas con `calls_90d=0`. +2. **Fase MEJORAR del bucle reactivo** — patrones inline repetidos generan proposals `new_function` con evidencia (session_ids + snippets). Funciones con error_rate alto y muchas llamadas suben en prioridad de bugfix. +3. **Auditoria de reglas** — assertions sobre `violation_count`, `mcp_ratio`, `heredoc_repetition`. Si fallan critical → proposal "actualizar CLAUDE.md / prompt del agente". + +Datos sensibles: solo se guarda `args_hash`, NUNCA valores concretos de argumentos. + +--- + ## Estructura ``` @@ -237,7 +312,7 @@ Entornos usados automaticamente: ## Añadir funciones -1. Consulta la BD para verificar que no existe algo similar +1. `mcp__registry__fn_search query=""` para verificar que no existe algo similar 2. Crea dos archivos segun el lenguaje: - Go: `functions/{domain}/{name}.go` + `.md` - Python: `python/functions/{domain}/{name}.py` + `.md` diff --git a/.claude/commands/autonomous-task.md b/.claude/commands/autonomous-task.md new file mode 100644 index 00000000..777fdb02 --- /dev/null +++ b/.claude/commands/autonomous-task.md @@ -0,0 +1,121 @@ +# /autonomous-task — Lanza fn-orquestador (Fase 6 del ciclo reactivo) + +Lanza el meta-orquestador autonomo que recorre el bucle CONSTRUIR → EJECUTAR → RECOPILAR → ANALIZAR → MEJORAR sobre un issue, sin intervencion humana, hasta convergencia / estancamiento / timeout / limite de iteraciones. + +Issue 0069. Pre-condiciones obligatorias (chequear ANTES de despachar): + +1. Migration `fn_operations/migrations/006_task_runs.sql` aplicada. +2. Subagentes `fn-constructor`, `fn-executor`, `fn-recopilador`, `fn-analizador`, `fn-mejorador`, `fn-orquestador` presentes en `.claude/agents/`. +3. `dev/autonomous_protected_paths.json` existe. +4. `master` local up-to-date con `origin/master`. +5. Branch `auto/` NO existe ya. +6. `gh auth status` OK (necesario para PR draft al converger). +7. Tipo de tarea soportado: `feature_app_simple`, `bugfix_with_repro`, `refactor_safe`, `add_e2e_check`. + +Si alguna pre-condicion falla → ABORT con razon. NO improvisar. + +--- + +## Argumento + +`$ARGUMENTS` — `` o `` + flags opcionales. + +``` +/autonomous-task 0070 +/autonomous-task 0070 --max-iterations 15 --max-minutes 90 +/autonomous-task 0070 --auto-apply-proposals safe +/autonomous-task 0070 --dry-run +/autonomous-task path/to/spec.yaml --branch auto/custom-name +``` + +Flags: +- `--max-iterations N` tope de iteraciones (default 10) +- `--max-minutes M` timeout total (default 60) +- `--auto-apply-proposals` `none|safe|aggressive` (default `safe`) +- `--branch NAME` rama TBD (default `auto/`) +- `--dry-run` simula, NO aplica + +--- + +## Comportamiento + +1. **Verificar pre-condiciones** con script bash (ver arriba). Si alguna falla, reportar y salir. +2. **Despachar a `fn-orquestador`** via Agent tool con `subagent_type=fn-orquestador`. Pasar: + - `issue_id` o `task_spec` + - flags resueltos + - paths protegidos (leidos de `dev/autonomous_protected_paths.json`) +3. **El subagente:** + - Crea worktree aislado `/tmp/fn_orq__/` desde `master`. + - Persiste estado en `task_runs` (operations.db del app target o repo root). + - Despacha por fases a los 5 subagentes especializados. + - Aplica proposals filtradas por `--auto-apply-proposals`. + - Termina con: `converged` (PR draft creado) | `stalled` | `timeout` | `iterations_exhausted` | `needs_human` | `aborted`. +4. **Reportar resultado al humano** con: + - `status`, `iterations / max`, `duration / max` + - `branch`, `worktree`, `PR draft url` si converged + - `proposals creadas / aplicadas` + - `last run_id` y status + - Resumen iter-por-iter del `progress_json` + +--- + +## Reglas duras (no negociables) + +- Sandbox de rama EN WORKTREE — nunca toca master ni el working tree del humano. +- No merge automatico — PR draft siempre. +- No `--no-verify`, no `--force`, no skip hooks. +- Paths protegidos via `dev/autonomous_protected_paths.json`. +- Watchdog: 2 iteraciones con mismo set de fails → `status=stalled`. +- Auditoria total en `task_runs.progress_json`. +- No self-modification: NO toca `.claude/agents/` ni `.claude/commands/`. + +--- + +## Integracion con call_monitor (issue 0085) + +El orquestador puede leer `projects/fn_monitoring/apps/call_monitor/operations.db` para: + +- Consultar `function_stats` antes de decidir que funciones usar/reusar. +- Filtrar proposals existentes via `mcp__registry__fn_proposal --status pending` para evitar duplicados. +- Loggear sus invocaciones via el hook PostToolUse (automatico). + +Tras converger, el `call_monitor propose` ejecutado por el humano (o futuro cron) absorbera las nuevas violations / copied_code / fails para alimentar la siguiente ronda. + +--- + +## Tipos NO soportados + +- Diseño arquitectura nuevo (humano decide). +- Decisiones UX subjetivas. +- Cambios BD productiva. +- Cualquier cosa que toque secrets/credenciales. +- Self-modification del propio orquestador. + +Si el issue contiene criterios no-verificables programaticamente, ABORT con `status=needs_human`. + +--- + +## Output canonico + +``` +=== /autonomous-task: 0070 === +status: converged +iterations: 7 / 10 +duration: 23 min / 60 +branch: auto/0070 +worktree: /tmp/fn_orq_0070_1731612345 +PR draft: https://github.com/.../pull/123 +proposals: 3 creadas, 2 auto-aplicadas +last run_id: e2e_run_abc123 (status: pass) + +Iter: + 1. construir → ok (2 funciones nuevas) + 2. ejecutar → ok + 3. analizar → fail (2/8 checks) + 4. mejorar → 3 proposals (2 auto-applicadas) + 5. construir → ok (re-build tras patches) + 6. analizar → pass + 7. recopilador → ok (operations.db integra) + +Siguiente: revisar PR draft + fn proposal list -s pending --target-id 0070 +``` diff --git a/.claude/commands/fn_claude.md b/.claude/commands/fn_claude.md new file mode 100644 index 00000000..ecd11121 --- /dev/null +++ b/.claude/commands/fn_claude.md @@ -0,0 +1,191 @@ +--- +description: "Auto-auditoria: verifica que la sesion registra uso de funciones, detecta gaps (patrones inline repetidos, wrappers saltados, heredocs sin function_id), lanza fn-constructor en paralelo para crear las funciones que faltan, y valida que Claude usara las nuevas en el siguiente turno" +--- + +# /fn_claude — auto-auditoria + auto-construccion del registry + +Comando meta: Claude se audita a si mismo. Verifica que su comportamiento en esta sesion (y las recientes) deja rastro en `call_monitor.operations.db`, detecta gaps reales del registry para el trabajo actual, lanza sub-agentes `fn-constructor` en paralelo para cerrar esos gaps, y verifica que la proxima vez usara las funciones nuevas. + +Issue 0085 fase autocompleta. Reemplaza el flujo manual de "veo un patron, decido si extraer, escribo proposal, espero humano, fn-mejorador genera, fn-orquestador opera". Con `/fn_claude` Claude hace todo eso solo, **autonomamente para si mismo**. + +--- + +## Comportamiento (ejecutalo en este orden) + +### 1. AUDIT — ¿estoy siendo registrado? + +```bash +ROOT="/home/lucas/fn_registry" +MON="$ROOT/projects/fn_monitoring/apps/call_monitor/operations.db" + +# Pre-condiciones +[ -f "$MON" ] || { echo "call_monitor.operations.db NO existe — issue 0085a no aplicado"; exit 1; } +[ "$FN_TELEMETRY" = "1" ] || echo "WARNING: FN_TELEMETRY != 1 — wrappers Python/Bash inactivos" + +# Metricas de la sesion actual + ultimas 24h +sqlite3 "$MON" <= CAST(strftime('%s','now','-1 day') AS INTEGER) +UNION ALL SELECT 'violations_24h', COUNT(*) FROM violations WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER) +UNION ALL SELECT 'tool_used_distribution_24h', NULL; +SELECT tool_used, COUNT(*) FROM calls WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER) GROUP BY tool_used ORDER BY 2 DESC; +SQL +``` + +Si `calls_session = 0` → algo esta mal (hook PostToolUse no fire o BD no escribible). Reporta y para. + +Si `mcp_*` / total < 0.4 → estas usando demasiado heredoc/sqlite directo. Reporta como warning. + +### 2. GAP — ¿que funciones faltan? + +Dos fuentes: + +#### 2a. Patrones repetidos en heredocs/Edit + +```sql +-- En call_monitor.operations.db +SELECT tool_used, COUNT(*) AS hits +FROM calls +WHERE function_id = '' + AND ts >= CAST(strftime('%s','now','-7 days') AS INTEGER) + AND tool_used IN ('heredoc_py', 'heredoc_bash', 'sqlite_direct') +GROUP BY tool_used; +``` + +Si `heredoc_py > 5` sin function_id → Claude esta componiendo logica que probablemente debe ser pipeline. Investigar el ultimo heredoc del transcript: si reescribe algo que ya es funcion del registry → violation candidate. Si no, es candidato a pipeline nuevo. + +#### 2b. Trabajo actual de la sesion — gap inferido del contexto + +Lee el ultimo prompt del usuario y los ultimos 10 turnos. Lista funciones que: + +- Has llamado inline (sed/awk/jq custom, transformaciones de datos, parsing). +- Has reinventado (HTTP client raw, SQLite open con flags, FS walks). +- Has compuesto >2 veces con el mismo shape. + +Para cada candidato: + +```bash +# Verifica si ya existe algo similar en el registry +mcp__registry__fn_search "" +``` + +Si NO existe match relevante → candidato a `fn-constructor`. +Si existe pero firma incompleta → candidato a `improve_function` (proposal, NO auto-construccion). + +### 3. PROPOSE — lista candidatos + +Genera tabla: + +``` +| Candidato | Razon | Lenguaje | Dominio | Evidencia (snippet) | +|---|---|---|---|---| +| | inline_repeated/wrapper_skip/new | go/py/bash | core/infra/... | | +``` + +Si lista vacia → "no gaps detected, sesion saludable" + reporta metricas. Para. + +### 4. CONSTRUCT — lanza fn-constructor en paralelo + +Para cada candidato, dispara un sub-agente `fn-constructor` con prompt autocontenido: + +``` +Agent(subagent_type="fn-constructor", prompt=...) +``` + +Prompts en PARALELO en un mismo mensaje (varios Agent calls). Pasar: +- nombre propuesto, lang, domain +- firma esperada (params + return) +- pureza +- descripcion + ejemplo de uso (heredoc real detectado) +- nota: "esta funcion la necesita Claude para auto-uso futuro" + +### 5. VALIDATE — ¿la proxima sesion la usara? + +Despues de que fn-constructor termine: + +```bash +./fn index 2>&1 | tail -2 +# Verifica que las nuevas funciones existen +for fn in ; do + mcp__registry__fn_show "$fn" >/dev/null && echo "OK: $fn" || echo "FAIL: $fn" +done +``` + +Tambien actualiza `call_monitor.copied_code` + `function_stats` corriendo: + +```bash +cd "$ROOT/projects/fn_monitoring/apps/call_monitor" && ./call_monitor copied-code && ./call_monitor propose +``` + +Reporta: +- N funciones nuevas creadas (con IDs) +- N proposals nuevas en `registry.db.proposals` +- Recomendacion al usuario: "proximo turno mencionar/usar `` para validar que el wrapper se invoca correctamente" + +### 6. SELF-TEST — telemetria del propio /fn_claude + +`/fn_claude` mismo debe quedar registrado. Tras ejecutar, query final: + +```bash +sqlite3 "$MON" "SELECT COUNT(*) FROM calls WHERE session_id = '${CLAUDE_SESSION_ID:-unknown}' AND ts >= " +``` + +Si la cuenta no aumento → el comando esta operando fuera de la telemetria (bug). Reportar. + +--- + +## Reglas duras + +1. **NO ejecutar fn-constructor para algo que ya existe.** Buscar primero via `mcp__registry__fn_search`. Si match relevante → NO crear duplicado. +2. **NO crear funciones especulativas.** Cada candidato debe tener evidencia real (snippet de heredoc o llamada inline detectada en esta sesion o en `call_monitor.calls` reciente). +3. **PARALELO**: si hay >1 candidato, lanza todos los `fn-constructor` en un solo mensaje con multiples `Agent` calls. NO secuencial. +4. **No autonomous merge**: las funciones nuevas viven en el branch local. NO push automatico. Humano revisa y push manual. +5. **Limites duros**: max 5 funciones nuevas por invocacion. Si detectas mas, prioriza por evidence weight (`occurrences * recency`) y reporta el resto como pending. +6. **Si la sesion no esta siendo registrada (`calls_session = 0`)**: ABORT antes de fase 2. No tiene sentido auto-construir sin telemetria. + +--- + +## Output canonico + +``` +=== /fn_claude — auto-auditoria === +session_id: +calls_session: N +calls_24h: M (mcp_ratio: 0.XX) +violations_24h: K +pending_proposals: P (existentes en registry.db) + +GAPS DETECTADOS: + 1. __ — razon — evidencia + 2. ... + +LANZADOS (en paralelo): + fn-constructor #1: → en progreso + fn-constructor #2: → en progreso + ... + +VALIDADAS tras ./fn index: + ✓ __ + ✓ __ + +PROPOSALS NUEVAS: + +PROXIMO TURNO: menciona `` para validar wrapper. +``` + +--- + +## Cuando usar + +- Al inicio de una sesion larga, para verificar telemetria activa. +- A media sesion, cuando notes que estas reescribiendo el mismo bloque. +- Antes de cerrar sesion, para capitalizar lo aprendido como funciones reutilizables. +- Tras `/autonomous-task` para validar que el orquestador no genero ruido (proposals/funciones huerfanas). + +--- + +## Cuando NO usar + +- En sesiones cortas (<5 turnos) — no hay datos suficientes. +- Si `call_monitor.operations.db` no esta inicializado (`call_monitor init` primero). +- Si el usuario quiere control manual del proceso de extraccion. Este comando es agresivo. diff --git a/.claude/commands/subagentes.md b/.claude/commands/subagentes.md index baf705bb..b637ac5a 100644 --- a/.claude/commands/subagentes.md +++ b/.claude/commands/subagentes.md @@ -77,4 +77,21 @@ Input: Tarea: Criterio exito: Limites: +Telemetria: tus tool calls quedan registradas en projects/fn_monitoring/apps/call_monitor/operations.db + via hook PostToolUse heredado de settings.local.json. Sigue patrones canonicos + (mcp__registry__fn_*, ./fn run, heredoc importando) — los antipatrones se loguean + como violations. ``` + +## Telemetria heredada (issue 0085 hardening 5) + +Los hooks de `.claude/settings.local.json` se heredan automaticamente por cada sub-agente que Claude Code lance via la tool `Agent`. Eso significa: + +- Cada Bash, Edit, Write, MultiEdit, `mcp__registry__*` del sub-agente dispara `hook_call_monitor.sh` exactamente igual que en la sesion principal. +- El `session_id` del JSON de input del hook viene del sub-agente, distinto al de la sesion padre. Util para auditar comportamiento por agente. +- Las violations detectadas (sqlite3 directo, heredoc reinventando, etc) cuentan tambien para sub-agentes — un `fn-constructor` que reescribe inline en lugar de delegar a otro `fn-constructor` queda registrado. +- `FN_TELEMETRY=1` esta en el `env` block de settings.local.json — los heredocs Python/Bash de sub-agentes ya tienen wrappers activos automaticamente. + +Implicacion: NO necesitas pasar flags `--telemetry` a sub-agentes. Solo asegurate de que el prompt sigue patrones canonicos. La regla `.claude/rules/registry_calls.md` se aplica igual. + +Si un sub-agente abre un proceso hijo que escapa al hook (ej. `nohup ... &`, daemons), ese subproceso queda fuera de la telemetria — documentalo en el prompt si es un caso valido. diff --git a/.claude/rules/INDEX.md b/.claude/rules/INDEX.md index da1c3bf2..2d6512fa 100644 --- a/.claude/rules/INDEX.md +++ b/.claude/rules/INDEX.md @@ -30,3 +30,6 @@ Reglas operativas del proyecto. Cada archivo es una regla independiente. | 24 | [feature_flags.md](feature_flags.md) | TBD: feature flags para mergear codigo incompleto sin romper master. Patrones por stack (Go/TS/Bash/Py), branch-by-abstraction, anti-patrones | | 25 | [db_migrations.md](db_migrations.md) | Migraciones SQLite obligatorias para cualquier cambio de schema. Aditivas, idempotentes, archivos numerados. Nunca borrar .db ni modificar migraciones existentes | | 26 | [e2e_validation.md](e2e_validation.md) | Contrato `e2e_checks` en `app.md` consumido por fn-analizador (fase 4 del bucle reactivo). Issue 0068 | +| 27 | [registry_calls.md](registry_calls.md) | Patrones canonicos para invocar funciones del registry (MCP inspect / MCP run / heredoc compose), antipatrones, excepciones, telemetria. Issue 0085 | +| 28 | [delegation.md](delegation.md) | Si vas a escribir logica reutilizable inline -> spawn fn-constructor inmediato + tag de grupo + usar en mismo turno. Issue 0086 | +| 29 | [capability_groups.md](capability_groups.md) | Tags planos + paginas madre `docs/capabilities/.md` para desbloquear clusters de funciones en un read. Issue 0086 | diff --git a/.claude/rules/capability_groups.md b/.claude/rules/capability_groups.md new file mode 100644 index 00000000..a560e100 --- /dev/null +++ b/.claude/rules/capability_groups.md @@ -0,0 +1,60 @@ +## Capability groups: tags + paginas madre en docs/capabilities/ + +Un **capability group** es un cluster de >=3 funciones del registry que comparten un dominio operativo (ej. `notebook`, `metabase`, `deploy`). Cada grupo tiene un **tag plano** (sin prefijo) y una **pagina madre** en `docs/capabilities/.md`. La pagina madre desbloquea el conjunto entero en un solo read. + +### Para que existen + +Sin grupos, Claude redescubre funciones via FTS5 una a una cada sesion ("¿como interactuo con Jupyter? ¿como subo deploy?"). Con grupos, Claude lee `docs/capabilities/.md` y carga las 5-10 funciones del cluster con su ejemplo canonico — menos turnos perdidos en discovery. + +### Convencion de tag + +- **Slug del grupo** = tag plano. Ej: `notebook`, `metabase`, `android-emu`. +- **No prefijos** (`cap:`, `group:`). Ya hay namespacing implicito porque convivirian con tags semanticos sueltos. +- **Una funcion puede llevar varios tags de grupo** si pertenece a dos clusters (raro pero valido). +- Filtro MCP: `mcp__registry__fn_search query="" tag="notebook"` lista el grupo. + +### Cuando crear grupo nuevo + +- **Minimo 3 funciones** afines. Con 2 no compensa pagina madre — quedan tags sueltos. +- **Dominio operativo claro**: el grupo debe ser describible en 1 frase ("operar Jupyter colaborativo", "deploy via SSH+systemd"). +- **Frontera neta** con grupos existentes. Si solapa con otro -> reorganizar, no duplicar. + +### Como crear grupo + +1. Anadir el tag al frontmatter `.md` de >=3 funciones afines. `fn index` lo registra. +2. Crear `docs/capabilities/.md` con plantilla: + - **Lista de funciones**: tabla `ID | firma corta | que hace`. + - **Ejemplo canonico**: 1-2 bloques de codigo end-to-end con los IDs reales. + - **Fronteras**: que NO cubre el grupo. + - **Prerequisitos** y **notas** si aplica. +3. Anadir fila al `docs/capabilities/INDEX.md`. +4. Correr `fn doctor capabilities` para auditar drift. + +### Auto-generacion + +`fn doctor capabilities --update` (TBD) reescribe la tabla de funciones de cada pagina madre preservando bloques curated (`Ejemplo canonico`, `Fronteras`, `Notas`). Las secciones curated nunca se sobrescriben. + +### Como Claude usa los grupos + +Cuando una tarea cae en un dominio conocido: + +1. `Read docs/capabilities/INDEX.md` para localizar grupo. +2. `Read docs/capabilities/.md` para cargar funciones + ejemplo. +3. Solo si el grupo no cubre lo necesario, `mcp__registry__fn_search` para funciones sueltas. +4. Si el grupo deberia cubrir pero falta funcion -> `fn-constructor` + tagear con el grupo en el frontmatter. + +### Auditoria + +```bash +fn doctor capabilities # lista grupos + drift +fn doctor capabilities --json # para agentes +``` + +Comprueba: +- Tag con N >=3 funciones pero sin pagina madre -> "tag huerfano". +- Pagina madre sin tag respaldo -> "grupo fantasma". +- Funcion con tag de grupo pero la pagina madre no la lista (autogen desfasada) -> "drift". + +### Relacion con dominios + +Los **dominios** del registry (`core`, `infra`, `finance`, `datascience`, `cybersecurity`, `shell`, `tui`, `pipelines`, `browser`) son taxonomia ortogonal — un grupo puede atravesar varios dominios (ej. `deploy` toca `infra` y `shell`). NO renombrar dominio a grupo ni viceversa. diff --git a/.claude/rules/delegation.md b/.claude/rules/delegation.md new file mode 100644 index 00000000..14c6f403 --- /dev/null +++ b/.claude/rules/delegation.md @@ -0,0 +1,42 @@ +## Delegacion: spawn fn-constructor en vez de escribir inline + +**REGLA DURA.** Si vas a escribir logica reutilizable inline en un artefacto (app, analysis, playground) o heredoc, STOP y delega a `fn-constructor`. La misma sesion debe crear + usar la funcion. No acumular huerfanas. + +### Cuando un patron es candidato a funcion + +- Aparece >=2 veces en esta sesion o en heredocs recientes. +- Firma generica (no depende de tipos internos del artefacto). +- 1 responsabilidad clara (CRUD, parse, transform, http call, formato fijo, etc.). +- No es one-liner idiomatico de stdlib (`time.Now().UTC().Format(...)` queda fuera). + +### Flujo obligatorio (mismo turno) + +1. **Detectar**. Si vas a escribir >=5 lineas de logica reutilizable inline -> STOP. +2. **Spawn `fn-constructor` inmediato** via `Agent(subagent_type="fn-constructor", ...)`: + - **Sin preguntar al usuario** (autorizado por defecto). + - Si hay >1 funcion independiente -> una sola llamada al Agent tool con **N tool_use blocks paralelos** en el mismo mensaje. NO serializar. +3. **Tagear con grupo de capacidad** al menos UN tag de grupo (`notebook`, `metabase`, `deploy`, etc.). Ver `capability_groups.md`. +4. **`fn index`** para registrar. +5. **Importar + invocar en el mismo turno** — no dejar funcion huerfana recien creada. +6. **Auto-verificar** con `fn doctor uses-functions` y `fn doctor unused` si tocas >=3 funciones nuevas. + +### Anti-patrones auditables + +| Anti-patron | Consecuencia | Sustituir por | +|---|---|---| +| Escribir helper inline en artefacto en vez de delegar | Reinvento por sesion | Spawn fn-constructor | +| Crear N funciones serialmente | Latencia x N | Multiples `Agent()` en mismo mensaje | +| Crear funcion y no usarla en el turno | Huerfana desde dia 1 (`calls_90d=0`) | Importar + invocar antes de cerrar turno | +| Crear funcion sin tag de grupo | Imposible descubrir en bloque proxima sesion | Anadir tag de grupo (capability group) | +| Reescribir en heredoc logica que ya existe | Capitalizacion perdida | `mcp__registry__fn_search` antes de escribir | + +### Excepciones + +- **Logica de dominio especifica del artefacto** (CRUD de tabla concreta, layout de UI, flujo unico de la app) -> queda en el artefacto. Solo lo reutilizable se delega. +- **Stub temporal con `not implemented`**: aceptable si la dependencia externa no esta disponible. Documentar en `.md` (ver `stubs.md`). + +### Telemetria + +Cada `code_writes` + `calls` se registra en `call_monitor/operations.db` (issue 0085). Vista `session_capability_growth` mide ratio creadas vs usadas por sesion. Hook `UserPromptSubmit` inyecta `CAPABILITY-GROWTH: created_this_session=X used=Y orphan=Z` en cada turno. + +Si `orphan>0` al cerrar la sesion -> revisar: o la funcion era especulativa (no debio crearse) o falta integrarla en el codigo del artefacto. diff --git a/.claude/rules/registry_calls.md b/.claude/rules/registry_calls.md new file mode 100644 index 00000000..01c6a096 --- /dev/null +++ b/.claude/rules/registry_calls.md @@ -0,0 +1,132 @@ +## Como invocar funciones del registry — patrones canonicos + +Toda invocacion del agente al registry sigue uno de **tres patrones**. Cualquier otro patron es antipatron auditable. Las invocaciones se loguean en `projects/fn_monitoring/apps/call_monitor/operations.db` (issue 0085) para alimentar el bucle reactivo. + +### Patrones canonicos + +| Caso | Patron | Cuando | +|---|---|---| +| **Inspeccionar** (buscar, leer codigo, ver dependencias, listar dominios, leer proposals) | `mcp__registry__fn_search` / `fn_show` / `fn_code` / `fn_uses` / `fn_list_domains` / `fn_proposal` | SIEMPRE para descubrimiento, lectura de codigo, exploracion. | +| **Ejecutar** UNA funcion/pipeline con sus args | `mcp__registry__fn_run [args]` (preferido) o `./fn run [args]` (fallback CLI) | ID conocido + args planos. Despacho automatico por lenguaje. | +| **Componer** ad-hoc multi-funcion con logica intermedia | Heredoc `python/.venv/bin/python3 - <<'PYEOF' ... PYEOF` IMPORTANDO funciones del registry | Solo si hay loops/conditionals/dispatch entre N funciones. Las funciones del registry **se importan**, no se reescriben. | + +### Antipatrones prohibidos (audit-targeted) + +| Patron | Razon | Sustituir por | +|---|---|---| +| `sqlite3 registry.db "SELECT ..."` para buscar funciones/tipos | Salta MCP, FTS5 gotchas, sin trazabilidad | `mcp__registry__fn_search` | +| `sqlite3 registry.db "SELECT ... FROM proposals"` | Mismo problema | `mcp__registry__fn_proposal` | +| `python -c "import metabase; dir(metabase)"` para descubrir helpers | Fuente de verdad = registry, no `__init__.py` | `mcp__registry__fn_search "metabase"` + `mcp__registry__fn_show ` | +| Heredoc que reescribe logica que ya existe como funcion del registry | Reinvento + perdida de capitalizacion | Buscar primero; si falta, delegar a `fn-constructor` (no escribir inline) | +| `client._http.request(...)` saltando wrapper del registry | Salta validacion del wrapper y telemetria | Usar wrapper; si firma incompleta, `fn proposal add --kind improve_function` | +| Scripts en `temp/` para composiciones que se repiten >2 veces | Codigo perdido + sin monitoreo | Pipeline en `python/functions/pipelines/` o `bash/functions/pipelines/` | +| `from import *` en heredoc | Imposible identificar funciones usadas | Imports explicitos `from import , ` | + +### Excepciones autorizadas para `sqlite3` directo + +Casos donde el MCP no aplica y `sqlite3 registry.db` es legitimo: + +- Introspeccion de schema: `.schema`, `.tables`, `PRAGMA table_info(...)`, `PRAGMA index_list(...)`. +- Agregaciones: `COUNT(*)`, `GROUP BY`, `SUM(...)`, `AVG(...)`. +- JOINs custom entre tablas que el MCP no expone (`functions JOIN unit_tests ON ...`). +- Columnas que el MCP no devuelve (rare; preferir proponer ampliacion del MCP). + +El hook `PreToolUse` (`.claude/scripts/hook_registry_mcp.sh`) ya deja pasar estas excepciones y solo avisa cuando ve `sqlite3 registry.db "SELECT ..."` plano. + +### Verificacion previa — `fn doctor` + +Antes de empezar trabajo no trivial sobre el registry, ejecutar `fn doctor` para confirmar que el ecosistema esta sano: + +- Artefactos OK (sin `git_not_initialized`, `venv_broken_path`, etc.). +- Services activos cuando se necesiten (`sqlite_api`, `registry_api`, `registry_mcp`). +- Sin drift `pc_locations` vs disco. +- Sin drift `uses_functions` vs imports reales. + +Si `fn doctor` reporta `service inactive` para `registry_mcp.service`, el MCP estara siendo invocado en modo stdio por Claude Code (normal); el systemd unit solo aplica al modo HTTP. Si el binario no responde, rebuild: `cd apps/registry_mcp && CGO_ENABLED=1 go build -tags fts5 -o registry_mcp .`. + +### Tools MCP disponibles + +| Tool | Lectura/escritura | Gating | +|---|---|---| +| `fn_search` | read | siempre on | +| `fn_show` | read | siempre on | +| `fn_code` | read | siempre on | +| `fn_uses` | read | siempre on | +| `fn_list_domains` | read | siempre on | +| `fn_proposal` | read | siempre on | +| `fn_doctor` | read | siempre on | +| `fn_run` | execute (mutating side-effects) | requiere `--enable-run` | +| `fn_create_function` | write | requiere `--enable-write` | + +### Heredoc Python — convenciones obligatorias + +Cuando el caso 3 (composicion) sea inevitable: + +1. **Imports explicitos** desde paquetes del registry. Nunca `import *`. +2. **No reescribir** la firma de una funcion del registry — importarla. +3. **Args via env vars o stdin JSON**, nunca interpolacion shell directa (inyeccion). +4. **Output a stdout JSON** cuando vaya a ser consumido por el siguiente paso. +5. **Si el heredoc supera ~30 lineas**, extraer a `python/functions/pipelines/`. El monitor avisara automaticamente cuando un patron similar se repita >5 veces. + +### Trazabilidad — bucle reactivo + +Cada evento alimenta a `call_monitor.db` (event-log append-only) y se rollupea en una vista `function_stats` con contadores por funcion del registry. Tablas event-log: + +| Tabla | Captura | +|---|---| +| `calls` | Cada invocacion (heredoc/mcp/fn_run): function_id, tool_used, duration_ms, success, error_class, args_hash | +| `code_writes` | Cada Edit/Write sobre archivo del registry: function_id, session_id, lines_added/removed | +| `test_runs` | Cada `go test`/`pytest` que toca codigo del registry: function_id, test_id, passed, duration_ms | +| `e2e_runs_fn` | Cada check `e2e_checks` de app que usa la funcion: function_id, app_id, check_id, passed | +| `violations` | Antipatron detectado: rule_id, session_id, command_snippet, severity | +| `patterns` | Heredocs clusterizados: pattern_hash, session_ids[], occurrences, representative_snippet | +| `sessions` | session_id, cwd, started_at, ended_at, health_score, mcp_ratio | + +Vista agregada `function_stats` por `function_id`: + +- **Uso:** `calls_total`, `calls_24h/7d/30d/90d`, `last_used_at` +- **Errores:** `errors_total`, `error_rate`, `last_error_class`, `last_error_ts` +- **Performance:** `mean_duration_ms`, `p95_duration_ms` +- **Codigo:** `writes_count`, `last_write_at` +- **Tests:** `tests_total`, `tests_failed`, `test_fail_rate`, `last_test_failed_at` +- **E2E:** `e2e_total`, `e2e_failed`, `e2e_fail_rate`, `consumer_apps_count` +- **Salud:** `violations_caused` + +Assertions derivadas → proposals automaticas: + +| Regla | Threshold | Proposal | +|---|---|---| +| Huerfana absoluta | `calls_90d=0 AND writes_count=0` | `deprecate_function` | +| Bug prioritario | `error_rate>0.1 AND calls_7d>5` | `improve_function` (bug) | +| Regresion performance | `p95_24h > 1.5 * p95_30d` | `improve_function` (perf) | +| Test flaky | `test_fail_rate>0.1 AND tests_total>10` | `improve_function` (flaky) | +| Wrapper saltado | `violations_caused>3` | `improve_function` (API gap) | +| Patron inline sin funcion | `patterns.occurrences>5 AND no match FTS` | `new_function` con snippet | +| Blast radius alto | `e2e_fail_rate>0 AND consumer_apps_count>=3` | `improve_function` (critical) | + +Datos sensibles: solo `args_hash`, NUNCA valores concretos. Snippets de error redactados via allowlist. + +### Capas de monitorizacion (issue 0085) + +Cobertura por capa, no todas activas a la vez: + +| # | Capa | Activacion | Cobertura | +|---|---|---|---| +| 1 | Hook PostToolUse Bash | siempre (settings.local.json) | mcp, fn_cli_run, edit_registry, violations | +| 2 | Wrapper Python `registry_telemetry` | `FN_TELEMETRY=1` env var | heredocs + notebooks Jupyter | +| 3 | Wrapper Bash `telemetry_prelude.sh` | `source` explicito o `FN_TELEMETRY=1` | heredoc bash + apps bash | +| 4 | Interceptor en `fn run` | siempre (binario Go) | duration/error real de invocacion CLI | +| 5 | `fn doctor copied-code` | comando manual / cron | drift estatico: codigo copiado en apps | +| 6 | `function_versions` + snapshot | poblado por `fn index` + edit-hook | historial de versiones | +| 7-8 | Build-tag Go / macro C++ | opt-in por app | runtime de app (futuro) | + +**Boundary:** monitorizamos al **agente** y a **invocaciones canonicas**. Runtime de apps Go/C++ compiladas queda fuera. Compensar con tests + `e2e_checks` (issue 0068). + +### Que NO se monitoriza + +- Funcion Go/C++ llamada internamente por app ya compilada. +- Funcion ejecutada por systemd timer / cron / Dagu sin pasar por `fn run`. +- Sub-agente (`Agent` tool) — sus tools no propagan a hook del padre. +- Service de produccion recibiendo HTTP. + +**Implicacion:** una funcion con `calls_90d=0` puede ser huerfana real O usada en runtime invisible. Antes de proponer `deprecate_function`, cruzar con `consumer_apps_count > 0` (e2e) o con `fn doctor uses-functions` (declaraciones estaticas). diff --git a/.claude/scripts/hook_call_monitor.sh b/.claude/scripts/hook_call_monitor.sh new file mode 100755 index 00000000..540e4111 --- /dev/null +++ b/.claude/scripts/hook_call_monitor.sh @@ -0,0 +1,234 @@ +#!/usr/bin/env bash +# PostToolUse hook: registra cada invocacion del agente en +# projects/fn_monitoring/apps/call_monitor/operations.db (issue 0085b). +# +# Identifica tool, extrae function_id cuando es posible, clasifica el patron +# (mcp_*, fn_cli_run, heredoc_py, sqlite_direct, edit_registry, ...) y +# detecta antipatrones para registrar violations. +# +# NUNCA bloquea la herramienta. Falla silenciosamente si la BD no esta lista. +# Solo guarda args_hash, jamas valores concretos. + +set -euo pipefail + +# ---- Resolve registry root (walks up from cwd looking for registry.db) ---- +resolve_root() { + local d="${PWD}" + while [ "$d" != "/" ]; do + if [ -f "$d/registry.db" ]; then + printf '%s' "$d" + return 0 + fi + d=$(dirname "$d") + done + return 1 +} + +ROOT=$(resolve_root) || exit 0 +DB="$ROOT/projects/fn_monitoring/apps/call_monitor/operations.db" + +# Si la BD aun no existe, el hook no hace nada (esperando init). +[ -f "$DB" ] || exit 0 + +# ---- Read stdin JSON ---- +INPUT=$(cat) +if [ -z "$INPUT" ]; then exit 0; fi + +# Required jq presence +command -v jq >/dev/null 2>&1 || exit 0 +command -v sqlite3 >/dev/null 2>&1 || exit 0 + +TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""') +SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""') +TS=$(date -u +%s) + +# Tool response success/error +SUCCESS=1 +ERROR_CLASS="" +ERROR_SNIPPET="" +RESP_IS_ERROR=$(printf '%s' "$INPUT" | jq -r 'if (.tool_response | type) == "object" then (.tool_response.is_error // false) else false end') +if [ "$RESP_IS_ERROR" = "true" ]; then + SUCCESS=0 + ERROR_SNIPPET=$(printf '%s' "$INPUT" | jq -r 'if (.tool_response | type) == "object" then (.tool_response.error // .tool_response.content // "") else "" end' | head -c 240 | tr '\n' ' ') +fi + +# args_hash: sha256 truncado del tool_input (sin valores) +ARGS_HASH=$(printf '%s' "$INPUT" | jq -c '.tool_input // {}' | sha256sum | cut -c1-16) + +# Helpers SQL +sql_escape() { printf '%s' "$1" | sed "s/'/''/g"; } + +insert_call() { + local fn_id="$1" tool_used="$2" duration_ms="${3:-0}" + local fn_esc tu_esc ec_esc es_esc sid_esc ah_esc + fn_esc=$(sql_escape "$fn_id") + tu_esc=$(sql_escape "$tool_used") + ec_esc=$(sql_escape "$ERROR_CLASS") + es_esc=$(sql_escape "$ERROR_SNIPPET") + sid_esc=$(sql_escape "$SESSION_ID") + ah_esc=$(sql_escape "$ARGS_HASH") + sqlite3 "$DB" "INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts) VALUES ('$sid_esc','$fn_esc','$tu_esc','$ah_esc',$duration_ms,$SUCCESS,'$ec_esc','$es_esc',$TS);" 2>/dev/null || true +} + +insert_code_write() { + local fn_id="$1" file_path="$2" added="${3:-0}" removed="${4:-0}" + local fn_esc fp_esc sid_esc + fn_esc=$(sql_escape "$fn_id") + fp_esc=$(sql_escape "$file_path") + sid_esc=$(sql_escape "$SESSION_ID") + sqlite3 "$DB" "INSERT INTO code_writes (session_id, function_id, file_path, lines_added, lines_removed, ts) VALUES ('$sid_esc','$fn_esc','$fp_esc',$added,$removed,$TS);" 2>/dev/null || true +} + +# Snapshot a function version row when an edit lands on a registry file. +# Uses sha256 of file bytes as content_hash (separate namespace from index source). +insert_edit_version() { + local fn_id="$1" abs_path="$2" + [ -f "$abs_path" ] || return 0 + command -v sha256sum >/dev/null 2>&1 || return 0 + local hash + hash=$(sha256sum "$abs_path" 2>/dev/null | awk '{print $1}') + [ -z "$hash" ] && return 0 + local fn_esc h_esc + fn_esc=$(sql_escape "$fn_id") + h_esc=$(sql_escape "$hash") + sqlite3 "$DB" "INSERT OR IGNORE INTO function_versions (function_id, content_hash, version, snapped_at, source, lines_added, lines_removed) VALUES ('$fn_esc','$h_esc','',$TS,'edit_hook',0,0);" 2>/dev/null || true +} + +insert_violation() { + local rule_id="$1" fn_id="$2" snippet="$3" severity="${4:-warning}" + local r_esc fn_esc sn_esc sev_esc sid_esc + r_esc=$(sql_escape "$rule_id") + fn_esc=$(sql_escape "$fn_id") + sn_esc=$(sql_escape "$(printf '%s' "$snippet" | head -c 240 | tr '\n' ' ')") + sev_esc=$(sql_escape "$severity") + sid_esc=$(sql_escape "$SESSION_ID") + sqlite3 "$DB" "INSERT INTO violations (session_id, rule_id, function_id, command_snippet, severity, ts) VALUES ('$sid_esc','$r_esc','$fn_esc','$sn_esc','$sev_esc',$TS);" 2>/dev/null || true +} + +# ---- Derive function_id from registry file path ---- +# Matches paths under functions//., python/functions//.py, +# bash/functions//.sh, frontend/functions//.ts(x) +derive_fn_id_from_path() { + local p="$1" + [ -z "$p" ] && return 1 + case "$p" in + functions/*/*.go|*/functions/*/*.go) + local dom name + dom=$(printf '%s' "$p" | sed -E 's|.*functions/([^/]+)/.*|\1|') + name=$(printf '%s' "$p" | sed -E 's|.*functions/[^/]+/([^/.]+)\..*|\1|') + [ -n "$dom" ] && [ -n "$name" ] && printf '%s_go_%s' "$name" "$dom" && return 0 ;; + python/functions/*/*.py) + local dom name + dom=$(printf '%s' "$p" | sed -E 's|python/functions/([^/]+)/.*|\1|') + name=$(printf '%s' "$p" | sed -E 's|python/functions/[^/]+/([^/.]+)\..*|\1|') + [ -n "$dom" ] && [ -n "$name" ] && printf '%s_py_%s' "$name" "$dom" && return 0 ;; + bash/functions/*/*.sh) + local dom name + dom=$(printf '%s' "$p" | sed -E 's|bash/functions/([^/]+)/.*|\1|') + name=$(printf '%s' "$p" | sed -E 's|bash/functions/[^/]+/([^/.]+)\..*|\1|') + [ -n "$dom" ] && [ -n "$name" ] && printf '%s_bash_%s' "$name" "$dom" && return 0 ;; + frontend/functions/*/*.ts|frontend/functions/*/*.tsx) + local dom name + dom=$(printf '%s' "$p" | sed -E 's|frontend/functions/([^/]+)/.*|\1|') + name=$(printf '%s' "$p" | sed -E 's|frontend/functions/[^/]+/([^/.]+)\..*|\1|') + [ -n "$dom" ] && [ -n "$name" ] && printf '%s_ts_%s' "$name" "$dom" && return 0 ;; + esac + return 1 +} + +# ---- Dispatch by tool ---- +case "$TOOL_NAME" in + mcp__registry__fn_search) + insert_call "" "mcp_fn_search" + ;; + mcp__registry__fn_show) + ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""') + insert_call "$ID" "mcp_fn_show" + ;; + mcp__registry__fn_code) + ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""') + insert_call "$ID" "mcp_fn_code" + ;; + mcp__registry__fn_uses) + ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""') + insert_call "$ID" "mcp_fn_uses" + ;; + mcp__registry__fn_run) + ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""') + insert_call "$ID" "mcp_fn_run" + ;; + mcp__registry__fn_list_domains) + insert_call "" "mcp_fn_list_domains" + ;; + mcp__registry__fn_proposal) + insert_call "" "mcp_fn_proposal" + ;; + mcp__registry__fn_doctor) + insert_call "" "mcp_fn_doctor" + ;; + mcp__registry__fn_create_function) + insert_call "" "mcp_fn_create_function" + ;; + + Edit|Write|MultiEdit) + FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // ""') + ABS_PATH="$FILE_PATH" + # Make path relative to root if absolute and inside root + case "$FILE_PATH" in + "$ROOT"/*) FILE_PATH="${FILE_PATH#$ROOT/}" ;; + /*) ABS_PATH="$FILE_PATH" ;; + *) ABS_PATH="$ROOT/$FILE_PATH" ;; + esac + FN_ID=$(derive_fn_id_from_path "$FILE_PATH" || true) + if [ -n "$FN_ID" ]; then + insert_code_write "$FN_ID" "$FILE_PATH" 0 0 + insert_call "$FN_ID" "edit_registry" + insert_edit_version "$FN_ID" "$ABS_PATH" + fi + ;; + + Bash) + CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // ""') + CMD_HEAD=$(printf '%s' "$CMD" | head -c 200 | tr '\n' ' ') + + # Classify + TOOL_USED="bash_other" + FN_ID="" + + if printf '%s' "$CMD" | grep -qE '(^|[[:space:]])\./fn[[:space:]]+run[[:space:]]+'; then + TOOL_USED="fn_cli_run" + FN_ID=$(printf '%s' "$CMD" | sed -nE 's/.*\.\/fn[[:space:]]+run[[:space:]]+([A-Za-z0-9_]+).*/\1/p' | head -n1) + elif printf '%s' "$CMD" | grep -qE 'python/\.venv/bin/python3[[:space:]]+-[[:space:]]+<<'; then + TOOL_USED="heredoc_py" + elif printf '%s' "$CMD" | grep -qE 'sqlite3[[:space:]][^|]*\bregistry\.db\b'; then + TOOL_USED="sqlite_direct" + fi + + insert_call "$FN_ID" "$TOOL_USED" + + # ---- Violation rules ---- + # 1. sqlite3 directo SELECT sobre registry.db (excepto schema/pragma/count/join) + if [ "$TOOL_USED" = "sqlite_direct" ]; then + if ! printf '%s' "$CMD" | grep -qiE '(\.schema|\.tables|PRAGMA[[:space:]]+(table_info|index_list)|COUNT\(|GROUP[[:space:]]+BY|JOIN[[:space:]])'; then + insert_violation "sqlite3_registry_select" "" "$CMD_HEAD" "warning" + fi + fi + + # 2. python -c "import X; dir(X)" + if printf '%s' "$CMD" | grep -qE 'python[3]?[[:space:]]+-c[[:space:]]+["'\''].*import.*(dir|help)\('; then + insert_violation "python_dir_inspect" "" "$CMD_HEAD" "info" + fi + + # 3. from import * (en heredoc python) + if [ "$TOOL_USED" = "heredoc_py" ]; then + if printf '%s' "$CMD" | grep -qE 'from[[:space:]]+[A-Za-z0-9_.]+[[:space:]]+import[[:space:]]+\*'; then + insert_violation "import_star_in_heredoc" "" "$CMD_HEAD" "warning" + fi + if printf '%s' "$CMD" | grep -qE 'client\._http\.request\('; then + insert_violation "client_http_request_direct" "" "$CMD_HEAD" "warning" + fi + fi + ;; +esac + +exit 0 diff --git a/.claude/scripts/hook_capability_tag_gate.sh b/.claude/scripts/hook_capability_tag_gate.sh new file mode 100755 index 00000000..08188735 --- /dev/null +++ b/.claude/scripts/hook_capability_tag_gate.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# PostToolUse hook: gate "tag de capability group obligatorio" tras crear/modificar +# funciones del registry. Issue 0086 paso 9/gate. +# +# Comportamiento: +# - Detecta .md de funciones (functions/, python/functions/, bash/functions/, +# frontend/functions/, cpp/functions/) modificados en los ultimos 60s. +# - Lee frontmatter `tags:` y verifica si al menos uno coincide con un capability +# group declarado en docs/capabilities/INDEX.md. +# - Si NO hay match -> emite additionalContext con la lista de funciones afectadas. +# - NUNCA bloquea. Solo warning visible. +# +# Salida JSON consumida por Claude Code: +# { "hookSpecificOutput": { "hookEventName": "PostToolUse", +# "additionalContext": "..." } } + +set -euo pipefail + +resolve_root() { + local d="${PWD}" + while [ "$d" != "/" ]; do + if [ -f "$d/registry.db" ]; then + printf '%s' "$d" + return 0 + fi + d=$(dirname "$d") + done + return 1 +} + +ROOT=$(resolve_root) || exit 0 +INDEX="$ROOT/docs/capabilities/INDEX.md" + +# Si no existe el INDEX aun, no hay grupos definidos -> nada que verificar. +[ -f "$INDEX" ] || exit 0 + +# Consume stdin (sin parsear — no necesitamos session_id para este gate) +cat >/dev/null + +# Solo correr si hay jq disponible +command -v jq >/dev/null 2>&1 || exit 0 + +# 1. Cargar lista de capability groups desde el INDEX. +# Formato esperado en INDEX.md: | [name](name.md) | N | descripcion | +CAP_GROUPS=$(grep -oE '\[[a-z][a-z0-9_-]*\]\([a-z][a-z0-9_-]*\.md\)' "$INDEX" \ + | sed -E 's/^\[([^]]+)\].*/\1/' \ + | sort -u) + +[ -z "$CAP_GROUPS" ] && exit 0 + +# 2. Encontrar .md de funciones modificados en ultimos 60s. +RECENT=$(find "$ROOT/functions" "$ROOT/python/functions" "$ROOT/bash/functions" \ + "$ROOT/frontend/functions" "$ROOT/cpp/functions" \ + -maxdepth 4 -type f -name '*.md' -mmin -1 2>/dev/null || true) + +[ -z "$RECENT" ] && exit 0 + +# 3. Para cada .md reciente: extraer tags del frontmatter, comparar con groups. +MISSING="" +while IFS= read -r mdfile; do + [ -z "$mdfile" ] && continue + # Extrae el bloque entre los dos `---` del inicio + front=$(awk '/^---$/{c++; next} c==1 {print} c>=2 {exit}' "$mdfile" 2>/dev/null || true) + [ -z "$front" ] && continue + + # tags: [a, b, c] o tags:\n - a\n - b + tags_inline=$( { printf '%s\n' "$front" | grep -E '^tags:[[:space:]]*\[' | head -1 \ + | sed -E 's/^tags:[[:space:]]*\[(.*)\].*$/\1/' \ + | tr ',' '\n' | sed -E 's/^[[:space:]"]+|[[:space:]"]+$//g'; } || true ) + + tags_block=$( { printf '%s\n' "$front" | awk ' + /^tags:[[:space:]]*$/ {intag=1; next} + intag && /^[[:space:]]*-[[:space:]]/ {sub(/^[[:space:]]*-[[:space:]]*/, ""); print; next} + intag && !/^[[:space:]]/ {intag=0} + ' | sed -E 's/^[[:space:]"]+|[[:space:]"]+$//g'; } || true ) + + tags=$( { printf '%s\n%s\n' "$tags_inline" "$tags_block" | grep -v '^$'; } || true ) + + matched=0 + while IFS= read -r g; do + [ -z "$g" ] && continue + if printf '%s\n' "$tags" | grep -qx "$g"; then + matched=1 + break + fi + done <<< "$CAP_GROUPS" + + if [ "$matched" -eq 0 ]; then + rel="${mdfile#$ROOT/}" + MISSING="${MISSING}${rel}\n" + fi +done <<< "$RECENT" + +# 4. Si hay funciones sin tag de grupo, emitir aviso. +if [ -n "$MISSING" ]; then + CAP_GROUPS_CSV=$(printf '%s' "$CAP_GROUPS" | tr '\n' ',' | sed 's/,$//') + WARN="CAPABILITY-GAP (issue 0086): funcion(es) recien tocada(s) sin tag de capability group: $(printf '%b' "$MISSING" | tr '\n' ' ')" + WARN+="| Grupos disponibles: ${CAP_GROUPS_CSV}. Anade al menos uno al frontmatter \`tags:\` y corre \`./fn index\`. Si la funcion no encaja en ningun grupo existente, considera crear grupo nuevo (>=3 funciones) o dejarla con tag plano (no de grupo)." + jq -n --arg ctx "$WARN" '{ + hookSpecificOutput: { + hookEventName: "PostToolUse", + additionalContext: $ctx + } + }' +fi + +exit 0 diff --git a/.claude/scripts/hook_registry_first_reminder.sh b/.claude/scripts/hook_registry_first_reminder.sh new file mode 100755 index 00000000..08b80816 --- /dev/null +++ b/.claude/scripts/hook_registry_first_reminder.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# UserPromptSubmit hook: recordatorio compacto de patrones canonicos del registry. +# Inyectado como additionalContext en cada turno del usuario. +# Issue 0085 (hardening 2). +# +# NUNCA bloquea. Solo printf de additionalContext. + +set -euo pipefail + +# Resolve registry root (walks up from cwd) +resolve_root() { + local d="${PWD}" + while [ "$d" != "/" ]; do + if [ -f "$d/registry.db" ]; then + printf '%s' "$d" + return 0 + fi + d=$(dirname "$d") + done + return 1 +} + +ROOT=$(resolve_root) || exit 0 + +# Read input, extract session_id (UserPromptSubmit payload includes it) +INPUT=$(cat) +SESSION_ID="" +if command -v jq >/dev/null 2>&1; then + SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null || true) +fi + +# Count current pending proposals + recent violations for situational awareness +PROPOSALS_PENDING="?" +VIOLATIONS_24H="?" +CALLS_24H="?" +CAP_CREATED=0 +CAP_USED=0 +CAP_ORPHAN=0 + +if command -v sqlite3 >/dev/null 2>&1; then + REG="$ROOT/registry.db" + MON="$ROOT/projects/fn_monitoring/apps/call_monitor/operations.db" + [ -f "$REG" ] && PROPOSALS_PENDING=$(sqlite3 "$REG" "SELECT COUNT(*) FROM proposals WHERE status='pending'" 2>/dev/null || echo "?") + if [ -f "$MON" ]; then + VIOLATIONS_24H=$(sqlite3 "$MON" "SELECT COUNT(*) FROM violations WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER)" 2>/dev/null || echo "?") + CALLS_24H=$(sqlite3 "$MON" "SELECT COUNT(*) FROM calls WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER)" 2>/dev/null || echo "?") + if [ -n "$SESSION_ID" ]; then + sid_esc=$(printf '%s' "$SESSION_ID" | sed "s/'/''/g") + CAP_CREATED=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc'" 2>/dev/null || echo 0) + CAP_USED=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc' AND calls_in_session>0" 2>/dev/null || echo 0) + CAP_ORPHAN=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc' AND calls_in_session=0" 2>/dev/null || echo 0) + fi + fi +fi + +REMINDER="REGISTRY-FIRST (issue 0085 telemetry active): " +REMINDER+="Inspect → mcp__registry__fn_search/show/code/uses/proposal. " +REMINDER+="Execute one fn → mcp__registry__fn_run or ./fn run. " +REMINDER+="Compose multi-fn → heredoc python IMPORTANDO del registry. " +REMINDER+="NUNCA sqlite3 registry.db directo (salvo schema/PRAGMA/COUNT/JOIN). " +REMINDER+="NUNCA reescribir inline logica que ya es funcion. " +REMINDER+="Si patron se repite >2x → propose nueva funcion via fn-constructor. " +REMINDER+="Estado: pending_proposals=${PROPOSALS_PENDING} violations_24h=${VIOLATIONS_24H} calls_24h=${CALLS_24H}. " +REMINDER+="CAPABILITY-GROWTH (issue 0086): created_this_session=${CAP_CREATED} used=${CAP_USED} orphan=${CAP_ORPHAN}. Si orphan>0 -> integra la funcion en el codigo o documenta por que se quedo huerfana. " +REMINDER+="Comando autocheck: /fn_claude." + +jq -n --arg ctx "$REMINDER" '{ + hookSpecificOutput: { + hookEventName: "UserPromptSubmit", + additionalContext: $ctx + } +}' diff --git a/.mcp.json b/.mcp.json index f84624b5..bfe2f251 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,11 +1,8 @@ { "mcpServers": { "registry": { - "command": "/home/egutierrez/fn_registry/apps/registry_mcp/registry_mcp", - "args": ["--enable-run", "--enable-write"], - "env": { - "FN_REGISTRY_ROOT": "/home/egutierrez/fn_registry" - } + "command": "./apps/registry_mcp/registry_mcp", + "args": ["--enable-run", "--enable-write"] } } } diff --git a/bash/functions/cybersecurity/analyze_dns.md b/bash/functions/cybersecurity/analyze_dns.md index 3f2e284b..b738cb0e 100644 --- a/bash/functions/cybersecurity/analyze_dns.md +++ b/bash/functions/cybersecurity/analyze_dns.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "analyze_dns(domain: string, mode: string) -> void" description: "Análisis DNS completo de un dominio: registros A/AAAA/MX/NS/TXT/CNAME/SOA, consulta whois y verificación contra listas negras DNSBL (spamhaus, spamcop, sorbs, barracuda)." -tags: [bash, cybersecurity, dns, network, whois, dnsbl, reconnaissance] +tags: [bash, cybersecurity, dns, network, whois, dnsbl, reconnaissance, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/audit_http_headers.md b/bash/functions/cybersecurity/audit_http_headers.md index 2c9d0189..62dc6e71 100644 --- a/bash/functions/cybersecurity/audit_http_headers.md +++ b/bash/functions/cybersecurity/audit_http_headers.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "audit_http_headers(url: string) -> void" description: "Audita las cabeceras HTTP de seguridad de una URL: verifica la presencia de HSTS (con validación de max-age mínimo de 6 meses), Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy y cabeceras CORS. También detecta cabeceras que exponen información del servidor." -tags: [bash, cybersecurity, web, http, headers, security, hsts, csp, hardening] +tags: [bash, cybersecurity, web, http, headers, security, hsts, csp, hardening, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/audit_ssh_config.md b/bash/functions/cybersecurity/audit_ssh_config.md index 94b51286..9bb262e3 100644 --- a/bash/functions/cybersecurity/audit_ssh_config.md +++ b/bash/functions/cybersecurity/audit_ssh_config.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "audit_ssh_config(config_path: string) -> void" description: "Audita la configuración de sshd_config evaluando parámetros de seguridad críticos (PermitRootLogin, PasswordAuthentication, Port, MaxAuthTries, X11Forwarding, AllowUsers). También revisa intentos de login fallidos en los logs y lista las claves autorizadas del usuario actual." -tags: [bash, cybersecurity, ssh, audit, security, hardening, linux] +tags: [bash, cybersecurity, ssh, audit, security, hardening, linux, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/check_firewall.md b/bash/functions/cybersecurity/check_firewall.md index 9d625e7f..16319cbc 100644 --- a/bash/functions/cybersecurity/check_firewall.md +++ b/bash/functions/cybersecurity/check_firewall.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "check_firewall() -> void" description: "Detecta el firewall activo del sistema (ufw, firewalld o iptables) y muestra su estado, reglas activas y puertos en escucha para cruzar con las reglas. Si no se detecta ningún firewall, emite una advertencia de exposición." -tags: [bash, cybersecurity, firewall, ufw, iptables, network, hardening, linux] +tags: [bash, cybersecurity, firewall, ufw, iptables, network, hardening, linux, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/detect_suspicious_users.md b/bash/functions/cybersecurity/detect_suspicious_users.md index 177f91b5..9a1a61e5 100644 --- a/bash/functions/cybersecurity/detect_suspicious_users.md +++ b/bash/functions/cybersecurity/detect_suspicious_users.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "detect_suspicious_users() -> void" description: "Revisa el sistema en busca de indicadores de compromiso en cuentas de usuario: UIDs 0 extras (además de root), usuarios con shell de login válida, homes en rutas inusuales, miembros de grupos privilegiados (sudo, docker, wheel, adm, etc.) y sesiones activas." -tags: [bash, cybersecurity, users, audit, linux, privilege-escalation, hardening] +tags: [bash, cybersecurity, users, audit, linux, privilege-escalation, hardening, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/encrypt_file.md b/bash/functions/cybersecurity/encrypt_file.md index 9fe0e8e7..bff1305b 100644 --- a/bash/functions/cybersecurity/encrypt_file.md +++ b/bash/functions/cybersecurity/encrypt_file.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "encrypt_file(mode: string, file: string) -> void" description: "Cifra o descifra un archivo usando AES-256-CBC con PBKDF2 (310.000 iteraciones) via openssl. La contraseña se lee de la variable de entorno ENCRYPT_PASSWORD o se solicita interactivamente. El archivo cifrado se guarda con extensión .enc; al descifrar se recupera el nombre original." -tags: [bash, cybersecurity, encryption, aes256, openssl, crypto, pbkdf2] +tags: [bash, cybersecurity, encryption, aes256, openssl, crypto, pbkdf2, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/enumerate_subdomains.md b/bash/functions/cybersecurity/enumerate_subdomains.md index 53b10d12..6da9a390 100644 --- a/bash/functions/cybersecurity/enumerate_subdomains.md +++ b/bash/functions/cybersecurity/enumerate_subdomains.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "enumerate_subdomains(domain: string, output_file: string) -> void" description: "Enumera subdominios de un dominio objetivo usando un diccionario integrado de ~100 subdominios comunes (www, mail, api, dev, admin, vpn, etc.). Detecta tanto registros A (IP directa) como CNAME. Muestra progreso cada 20 subdominios y opcionalmente guarda los resultados en un archivo." -tags: [bash, cybersecurity, dns, subdomain, enumeration, reconnaissance, osint] +tags: [bash, cybersecurity, dns, subdomain, enumeration, reconnaissance, osint, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/generate_password.md b/bash/functions/cybersecurity/generate_password.md index 3bd6bec0..a06611e1 100644 --- a/bash/functions/cybersecurity/generate_password.md +++ b/bash/functions/cybersecurity/generate_password.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "generate_password(mode: string, length: int, count: int) -> void" description: "Genera contraseñas seguras en cuatro modos: full (alfanumérico + símbolos, excluye caracteres ambiguos), alpha (solo alfanumérico), passphrase (palabras aleatorias unidas con guión) y pin (numérico). Calcula y muestra la entropía en bits para cada modo." -tags: [bash, cybersecurity, password, generator, entropy, security, urandom] +tags: [bash, cybersecurity, password, generator, entropy, security, urandom, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/geolocate_ip.md b/bash/functions/cybersecurity/geolocate_ip.md index 7ceb3f44..ac301a1e 100644 --- a/bash/functions/cybersecurity/geolocate_ip.md +++ b/bash/functions/cybersecurity/geolocate_ip.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "geolocate_ip(target: string) -> void" description: "Geolocaliza una dirección IP o dominio usando la API pública de ip-api.com. Muestra país, región, ciudad, coordenadas, ISP, ASN y detecta VPN, Proxy o infraestructura de hosting." -tags: [bash, cybersecurity, network, geoip, ip, osint, reconnaissance] +tags: [bash, cybersecurity, network, geoip, ip, osint, reconnaissance, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/inspect_ssl_cert.md b/bash/functions/cybersecurity/inspect_ssl_cert.md index 800011ff..fce3cc79 100644 --- a/bash/functions/cybersecurity/inspect_ssl_cert.md +++ b/bash/functions/cybersecurity/inspect_ssl_cert.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "inspect_ssl_cert(host: string) -> void" description: "Inspecciona el certificado SSL/TLS de un host: muestra sujeto, emisor, fechas de validez, días hasta expiración, SANs (Subject Alternative Names), cadena de confianza completa y detecta soporte de versiones inseguras TLS 1.0/1.1." -tags: [bash, cybersecurity, ssl, tls, certificate, web, openssl, security] +tags: [bash, cybersecurity, ssl, tls, certificate, web, openssl, security, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/list_active_connections.md b/bash/functions/cybersecurity/list_active_connections.md index 24420984..a6a36758 100644 --- a/bash/functions/cybersecurity/list_active_connections.md +++ b/bash/functions/cybersecurity/list_active_connections.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "list_active_connections(mode: string) -> void" description: "Muestra conexiones de red activas del sistema usando ss: puertos en escucha, conexiones establecidas y detección de conexiones hacia IPs externas (excluye RFC1918, loopback y link-local)." -tags: [bash, cybersecurity, network, connections, monitoring, ss, ports] +tags: [bash, cybersecurity, network, connections, monitoring, ss, ports, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/cybersecurity/verify_file_hash.md b/bash/functions/cybersecurity/verify_file_hash.md index 3c893813..5a229240 100644 --- a/bash/functions/cybersecurity/verify_file_hash.md +++ b/bash/functions/cybersecurity/verify_file_hash.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "verify_file_hash(file: string, algorithm: string, expected_hash: string) -> void" description: "Calcula el hash criptográfico de un archivo con el algoritmo especificado (md5, sha1, sha256, sha512) y opcionalmente lo compara con un hash esperado para verificar integridad. Retorna exit code 1 si los hashes no coinciden." -tags: [bash, cybersecurity, hash, integrity, checksum, md5, sha256, sha512] +tags: [bash, cybersecurity, hash, integrity, checksum, md5, sha256, sha512, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/analyze_disk_space.md b/bash/functions/infra/analyze_disk_space.md index 53e76dd1..ceee1830 100644 --- a/bash/functions/infra/analyze_disk_space.md +++ b/bash/functions/infra/analyze_disk_space.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "analyze_disk_space([target_dir: string], [mode: string]) -> void" description: "Analiza el uso de espacio en disco. Modos: partitions (df con filtros), top-dirs (du top 10), top-files (find top 20), inodes (df -i), all (todos). Emite advertencias si el uso supera el 90%." -tags: [bash, disk, space, analysis, filesystem] +tags: [bash, disk, space, analysis, filesystem, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_app_clear.md b/bash/functions/infra/android_app_clear.md index 958b08d4..db64fd41 100644 --- a/bash/functions/infra/android_app_clear.md +++ b/bash/functions/infra/android_app_clear.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_app_clear([--serial ], package: string) -> void" description: "Wipe app data + cache via pm clear. App keeps installed but factory-state. Multi-emulator via --serial." -tags: [android, adb, app, clear, reset] +tags: [android, adb, app, clear, reset, pendiente-usar] params: - name: "--serial " desc: "Optional target device/emulator serial. Auto-detected if omitted." diff --git a/bash/functions/infra/android_app_info.md b/bash/functions/infra/android_app_info.md index e1a4322f..5717fd5c 100644 --- a/bash/functions/infra/android_app_info.md +++ b/bash/functions/infra/android_app_info.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_app_info([--serial ], package, [--json]) -> stdout" description: "Inspect installed app: version, target SDK, activities via dumpsys package." -tags: [android, adb, app, info, dumpsys] +tags: [android, adb, app, info, dumpsys, pendiente-usar] params: - name: "--serial " desc: "Optional ADB serial to target a specific device/emulator. Auto-detected if omitted." diff --git a/bash/functions/infra/android_app_kill.md b/bash/functions/infra/android_app_kill.md index f15b2adb..c38338e2 100644 --- a/bash/functions/infra/android_app_kill.md +++ b/bash/functions/infra/android_app_kill.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_app_kill([--serial ], package: string) -> void" description: "Force-stop running app via am force-stop. Multi-emulator via --serial." -tags: [android, adb, app, kill, force-stop] +tags: [android, adb, app, kill, force-stop, pendiente-usar] params: - name: "--serial " desc: "Optional target device/emulator serial. Auto-detected if omitted." diff --git a/bash/functions/infra/android_app_launch.md b/bash/functions/infra/android_app_launch.md index 0a5097b8..de7982a4 100644 --- a/bash/functions/infra/android_app_launch.md +++ b/bash/functions/infra/android_app_launch.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_app_launch([--serial ], package: string, [activity: string]) -> void" description: "Launch app activity via am start. Multi-emulator via --serial." -tags: [android, adb, app, launch, activity] +tags: [android, adb, app, launch, activity, pendiente-usar] params: - name: "--serial " desc: "Optional target serial. Default: first device" diff --git a/bash/functions/infra/android_app_uninstall.md b/bash/functions/infra/android_app_uninstall.md index 9e903064..6442764c 100644 --- a/bash/functions/infra/android_app_uninstall.md +++ b/bash/functions/infra/android_app_uninstall.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_app_uninstall([--serial ] package [--keep-data]) -> void" description: "Uninstall app via adb uninstall. Optionally keep data with --keep-data." -tags: [android, adb, app, uninstall] +tags: [android, adb, app, uninstall, pendiente-usar] params: - name: "--serial " desc: "Optional target device/emulator serial. Auto-detects first connected device if omitted." diff --git a/bash/functions/infra/android_emu_battery.md b/bash/functions/infra/android_emu_battery.md index 1f42a588..02561882 100644 --- a/bash/functions/infra/android_emu_battery.md +++ b/bash/functions/infra/android_emu_battery.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_emu_battery([--serial ], level: int, [--charging ]) -> void" description: "Simulate battery state on emulator (level + charging). Emulator-only." -tags: [android, emulator, battery, power] +tags: [android, emulator, battery, power, pendiente-usar] params: - name: "--serial " desc: "Optional emulator serial (e.g. emulator-5554). Auto-detected if omitted." diff --git a/bash/functions/infra/android_emu_geo_fix.md b/bash/functions/infra/android_emu_geo_fix.md index e89a24be..971595a1 100644 --- a/bash/functions/infra/android_emu_geo_fix.md +++ b/bash/functions/infra/android_emu_geo_fix.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_emu_geo_fix([--serial ], longitude: string, latitude: string, [altitude: string]) -> void" description: "Fake GPS location on Android emulator via emu geo fix. Emulator-only (not physical devices)." -tags: [android, emulator, geo, gps, location] +tags: [android, emulator, geo, gps, location, pendiente-usar] params: - name: "--serial " desc: "Optional emulator serial. Auto-detected if omitted." diff --git a/bash/functions/infra/android_emu_rotate.md b/bash/functions/infra/android_emu_rotate.md index 9162705e..8ebcfd3a 100644 --- a/bash/functions/infra/android_emu_rotate.md +++ b/bash/functions/infra/android_emu_rotate.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_emu_rotate([--serial ] [portrait|landscape|0|90|180|270])" description: "Rotate emulator screen. Empty=toggle, or fixed orientation. Locks autorotate." -tags: [android, emulator, rotation, orientation] +tags: [android, emulator, rotation, orientation, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_input_keyevent.md b/bash/functions/infra/android_input_keyevent.md index c6b43514..347b1470 100644 --- a/bash/functions/infra/android_input_keyevent.md +++ b/bash/functions/infra/android_input_keyevent.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_input_keyevent([--serial ] key: string)" description: "Send key event via adb shell input keyevent. Accepts aliases (BACK, HOME, POWER, ENTER, MENU, RECENT_APPS, VOLUME_UP, VOLUME_DOWN), raw numeric codes, or explicit KEYCODE_* names." -tags: [android, adb, input, keyevent, ui-test] +tags: [android, adb, input, keyevent, ui-test, pendiente-usar] params: - name: "--serial " desc: "Optional target device/emulator serial. If omitted, adb_pick_serial resolves the single connected device." diff --git a/bash/functions/infra/android_input_swipe.md b/bash/functions/infra/android_input_swipe.md index 8839088c..c481d683 100644 --- a/bash/functions/infra/android_input_swipe.md +++ b/bash/functions/infra/android_input_swipe.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_input_swipe([--serial ], x1: int, y1: int, x2: int, y2: int, [duration_ms: int])" description: "Send swipe gesture between two points with duration." -tags: [android, adb, input, swipe, gesture, ui-test] +tags: [android, adb, input, swipe, gesture, ui-test, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_input_tap.md b/bash/functions/infra/android_input_tap.md index 026f4010..abaeb005 100644 --- a/bash/functions/infra/android_input_tap.md +++ b/bash/functions/infra/android_input_tap.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_input_tap([--serial ], x: int, y: int) -> void" description: "Send tap gesture at screen coordinates via adb shell input tap." -tags: [android, adb, input, tap, ui-test, gesture] +tags: [android, adb, input, tap, ui-test, gesture, pendiente-usar] params: - name: "--serial " desc: "Optional target device serial. Auto-detected if omitted." diff --git a/bash/functions/infra/android_input_text.md b/bash/functions/infra/android_input_text.md index 0b91be43..c7f1cf69 100644 --- a/bash/functions/infra/android_input_text.md +++ b/bash/functions/infra/android_input_text.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_input_text([--serial ], text: string) -> void" description: "Type text in focused field via adb shell input text. Spaces handled." -tags: [android, adb, input, text, ui-test] +tags: [android, adb, input, text, ui-test, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_pull.md b/bash/functions/infra/android_pull.md index 28cb7fba..56937d37 100644 --- a/bash/functions/infra/android_pull.md +++ b/bash/functions/infra/android_pull.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_pull [--serial ] remote_path local_path" description: "Pull file/dir from Android device to WSL via adb pull." -tags: [android, adb, pull, file, transfer] +tags: [android, adb, pull, file, transfer, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_push.md b/bash/functions/infra/android_push.md index f1963b8c..adcf3a72 100644 --- a/bash/functions/infra/android_push.md +++ b/bash/functions/infra/android_push.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_push([--serial ], local_path: string, remote_path: string) -> void" description: "Push file/dir from WSL to Android device via adb push." -tags: [android, adb, push, file, transfer] +tags: [android, adb, push, file, transfer, pendiente-usar] params: - name: "--serial " desc: "Optional target device/emulator serial. Auto-detected if omitted." diff --git a/bash/functions/infra/android_screen_record.md b/bash/functions/infra/android_screen_record.md index 9a4ba234..f1f06f7f 100644 --- a/bash/functions/infra/android_screen_record.md +++ b/bash/functions/infra/android_screen_record.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_screen_record([--serial ] [--duration ] [--bit-rate ] [--size ] output_path: string) -> void" description: "Record screen video via adb screenrecord, pulls to local path." -tags: [android, adb, screen, record, video] +tags: [android, adb, screen, record, video, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_screenshot.md b/bash/functions/infra/android_screenshot.md index e6376dcf..29103778 100644 --- a/bash/functions/infra/android_screenshot.md +++ b/bash/functions/infra/android_screenshot.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_screenshot([--serial ], output_path: string) -> void" description: "Capture screen as PNG via adb exec-out screencap -p." -tags: [android, adb, screenshot, screen, capture] +tags: [android, adb, screenshot, screen, capture, pendiente-usar] uses_functions: [adb_wsl_bash_infra] uses_types: [] returns: [] diff --git a/bash/functions/infra/android_shell.md b/bash/functions/infra/android_shell.md index 827122ce..c87df273 100644 --- a/bash/functions/infra/android_shell.md +++ b/bash/functions/infra/android_shell.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "android_shell([--serial ], cmd ...args)" description: "Execute arbitrary shell command on Android device. Multi-emulator via --serial." -tags: [android, adb, shell, exec] +tags: [android, adb, shell, exec, pendiente-usar] params: - name: "--serial " desc: "Optional target device serial. Omit to auto-pick (single device) or use ADB_SERIAL env." diff --git a/bash/functions/infra/append_diary_entry.md b/bash/functions/infra/append_diary_entry.md index 5a9f3473..e4ef8da9 100644 --- a/bash/functions/infra/append_diary_entry.md +++ b/bash/functions/infra/append_diary_entry.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "append_diary_entry(titulo: string, cuerpo: string) -> string" description: "Añade una entrada al diario del dia en ${DIARY_DIR:-docs/diary}/YYYY-MM-DD.md. Crea el archivo con cabecera si no existe. Nunca reescribe contenido previo. Si cuerpo es vacio escribe solo el header de la seccion." -tags: [diary, markdown, append, idempotent, infra] +tags: [diary, markdown, append, idempotent, infra, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/build_cpp_linux.md b/bash/functions/infra/build_cpp_linux.md index 4685129e..5a15e96a 100644 --- a/bash/functions/infra/build_cpp_linux.md +++ b/bash/functions/infra/build_cpp_linux.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "build_cpp_linux(target?: string) -> void" description: "Compila las funciones y apps C++ del registry para Linux nativo usando cmake" -tags: [cpp, build, cmake, linux, imgui] +tags: [cpp, build, cmake, linux, imgui, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/build_wasm_cpp_app.md b/bash/functions/infra/build_wasm_cpp_app.md index 34a19612..6102c669 100644 --- a/bash/functions/infra/build_wasm_cpp_app.md +++ b/bash/functions/infra/build_wasm_cpp_app.md @@ -7,7 +7,7 @@ version: "0.1.0" purity: impure signature: "build_wasm_cpp_app(app_name: string, [--no-budget-check]) -> void" description: "Compila una app C++ del registry (cpp/apps/) a WASM via emscripten. Sale build/wasm//.{html,js,wasm,wasm.gz}. Falla si gzip > 2 MB." -tags: [wasm, emscripten, cpp, build, gamedev] +tags: [wasm, emscripten, cpp, build, gamedev, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/cuda_toolkit_check.md b/bash/functions/infra/cuda_toolkit_check.md index 46d57331..b2218905 100644 --- a/bash/functions/infra/cuda_toolkit_check.md +++ b/bash/functions/infra/cuda_toolkit_check.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "cuda_toolkit_check() -> void" description: "Detecta componentes CUDA instalados en el sistema y emite pares key=value a stdout: nvcc (version o missing), nvidia_smi (present/missing), driver_version, cuda_libs (path o missing) y overall (ok|partial|missing). Exit code 0 siempre — funcion informativa, no fatal." -tags: [cuda, nvidia, gpu, hardware, probe, infra, toolkit] +tags: [cuda, nvidia, gpu, hardware, probe, infra, toolkit, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/detect_wsl.md b/bash/functions/infra/detect_wsl.md index f4d9b075..3022659d 100644 --- a/bash/functions/infra/detect_wsl.md +++ b/bash/functions/infra/detect_wsl.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "detect_wsl([--check]) -> void" description: "Detecta si el sistema es WSL (Windows Subsystem for Linux). Con --check retorna solo exit code (0=WSL, 1=no WSL) sin output. Sin argumentos imprime versión WSL, usuario Windows, distribución, hostname, unidades montadas y ruta Windows del directorio actual." -tags: [bash, wsl, windows, detect, integration] +tags: [bash, wsl, windows, detect, integration, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/e2e_run_cpp_windows.md b/bash/functions/infra/e2e_run_cpp_windows.md index dfa5ac59..77e7df16 100644 --- a/bash/functions/infra/e2e_run_cpp_windows.md +++ b/bash/functions/infra/e2e_run_cpp_windows.md @@ -3,7 +3,7 @@ name: e2e_run_cpp_windows lang: bash domain: infra description: "Cross-compila una app C++ del registry para Windows con mingw-w64, deploy al Desktop\\apps de Windows (matando instancia previa con taskkill.exe), lanza el .exe nativamente desde WSL y devuelve stdout + exit code. Pensado para tests headless tipo altsnap_jitter_test." -tags: [windows, e2e, cross-compile, test, mingw] +tags: [windows, e2e, cross-compile, test, mingw, pendiente-usar] purity: impure kind: function signature: "e2e_run_cpp_windows(target string, --no-build, --no-deploy) int" diff --git a/bash/functions/infra/frontend_doctor.md b/bash/functions/infra/frontend_doctor.md index 4b980a76..07341ebc 100644 --- a/bash/functions/infra/frontend_doctor.md +++ b/bash/functions/infra/frontend_doctor.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "frontend_doctor(project_dir: string) -> diagnostics_stdout" description: "Diagnostica la salud de un proyecto frontend Mantine. Verifica Node, React, Mantine, PostCSS, TypeScript, vite.config y detecta residuos de shadcn/@base-ui. Imprime tabla de checks con exit code 0/1." -tags: [frontend, mantine, doctor, diagnostics, health, validation] +tags: [frontend, mantine, doctor, diagnostics, health, validation, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/git_hook_audit_app_drift.md b/bash/functions/infra/git_hook_audit_app_drift.md index 9b41d42f..fcee4932 100644 --- a/bash/functions/infra/git_hook_audit_app_drift.md +++ b/bash/functions/infra/git_hook_audit_app_drift.md @@ -7,7 +7,7 @@ version: 1.0.0 purity: impure signature: "git_hook_audit_app_drift " description: "Pre-commit guard: bloquea commit si los archivos staged tocan una app cuyo app.md tiene drift de uses_functions. Permite ediciones a app.md (correcciones)." -tags: [git, hook, precommit, registry-first, audit] +tags: [git, hook, precommit, registry-first, audit, pendiente-usar] uses_functions: - audit_uses_functions_go_infra uses_types: [] diff --git a/bash/functions/infra/gitea_create_webhook.md b/bash/functions/infra/gitea_create_webhook.md index f519e42e..5aa5328a 100644 --- a/bash/functions/infra/gitea_create_webhook.md +++ b/bash/functions/infra/gitea_create_webhook.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "gitea_create_webhook(owner: string, repo: string, target_url: string, secret?: string) -> json" description: "Crea un webhook de push en un repositorio Gitea. El webhook notifica a target_url en cada push." -tags: [gitea, webhook, push, deploy, ci, infra] +tags: [gitea, webhook, push, deploy, ci, infra, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/gitea_list_repos.md b/bash/functions/infra/gitea_list_repos.md index ac3e9f08..afadc168 100644 --- a/bash/functions/infra/gitea_list_repos.md +++ b/bash/functions/infra/gitea_list_repos.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "gitea_list_repos(owner: string) -> string" description: "Lista repositorios de un owner en Gitea. Intenta listar como org primero; si falla, lista como usuario. Imprime una línea por repo en formato namehtml_urldescription." -tags: [gitea, git, repo, list, org, user, api, infra] +tags: [gitea, git, repo, list, org, user, api, infra, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/gradle_clean.md b/bash/functions/infra/gradle_clean.md index a6b0823d..124040f0 100644 --- a/bash/functions/infra/gradle_clean.md +++ b/bash/functions/infra/gradle_clean.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "gradle_clean(project_dir: string) -> int" description: "Limpia build artifacts de un proyecto Android (gradle clean + rm .gradle + rm build)." -tags: [android, gradle, clean, build] +tags: [android, gradle, clean, build, pendiente-usar] params: - name: project_dir desc: "Raiz del proyecto Gradle" diff --git a/bash/functions/infra/install_cpp_deps.md b/bash/functions/infra/install_cpp_deps.md index 2f48e230..291e1758 100644 --- a/bash/functions/infra/install_cpp_deps.md +++ b/bash/functions/infra/install_cpp_deps.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_cpp_deps() -> void" description: "Verifica e instala las dependencias de sistema necesarias para compilar C++ con ImGui (cmake, g++, glfw, mesa)" -tags: [cpp, dependencies, setup, cmake, imgui] +tags: [cpp, dependencies, setup, cmake, imgui, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_mantine.md b/bash/functions/infra/install_mantine.md index d28e8622..b2de0750 100644 --- a/bash/functions/infra/install_mantine.md +++ b/bash/functions/infra/install_mantine.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_mantine(project_dir: string) -> void" description: "Instala Mantine UI con todas sus dependencias (@mantine/core, hooks, charts, notifications, form) y PostCSS en un proyecto frontend. Detecta package manager por lockfile. Genera postcss.config.cjs si no existe. Idempotente." -tags: [mantine, frontend, install, react, ui, postcss] +tags: [mantine, frontend, install, react, ui, postcss, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_nodejs.md b/bash/functions/infra/install_nodejs.md index f4bdbd47..8b0e808e 100644 --- a/bash/functions/infra/install_nodejs.md +++ b/bash/functions/infra/install_nodejs.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_nodejs([version: string]) -> void" description: "Instala Node.js en Linux usando nvm. Instala nvm v0.39.7 si no está presente. Instala la versión de Node indicada, la activa con 'nvm use' y la configura como default. Idempotente si nvm ya está instalado." -tags: [bash, install, nodejs, nvm] +tags: [bash, install, nodejs, nvm, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_nordvpn.md b/bash/functions/infra/install_nordvpn.md index 6cd40ee5..393d0020 100644 --- a/bash/functions/infra/install_nordvpn.md +++ b/bash/functions/infra/install_nordvpn.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_nordvpn() -> void" description: "Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2). Configura repositorio oficial, instala paquete y habilita servicio nordvpnd. Idempotente." -tags: [vpn, nordvpn, install, infra, wsl2] +tags: [vpn, nordvpn, install, infra, wsl2, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_pnpm.md b/bash/functions/infra/install_pnpm.md index 4d8387c8..de274491 100644 --- a/bash/functions/infra/install_pnpm.md +++ b/bash/functions/infra/install_pnpm.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_pnpm() -> void" description: "Instala pnpm globalmente usando npm (npm install -g pnpm). Verifica que npm esté disponible. Idempotente: si pnpm ya está instalado, informa y termina sin hacer nada." -tags: [bash, install, pnpm, node] +tags: [bash, install, pnpm, node, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_python312.md b/bash/functions/infra/install_python312.md index 6ee4fcff..382a34a1 100644 --- a/bash/functions/infra/install_python312.md +++ b/bash/functions/infra/install_python312.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_python312() -> void" description: "Instala Python 3.12 detectando la distribución Linux automáticamente. Ubuntu/Debian/Mint usan deadsnakes PPA; Fedora/RHEL usan dnf; Arch/Manjaro usan pacman. Instala también python3.12-venv, python3.12-dev y verifica pip. Idempotente." -tags: [bash, install, python, python312] +tags: [bash, install, python, python312, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_uv.md b/bash/functions/infra/install_uv.md index 49888252..8639dae8 100644 --- a/bash/functions/infra/install_uv.md +++ b/bash/functions/infra/install_uv.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_uv() -> void" description: "Instala uv, el gestor de paquetes Python ultra-rápido escrito en Rust, usando el instalador oficial de astral.sh. Configura PATH en ~/.bashrc y ~/.zshrc. Idempotente: si uv ya está instalado, informa y termina." -tags: [bash, install, uv, python] +tags: [bash, install, uv, python, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/install_volta.md b/bash/functions/infra/install_volta.md index 8fd5a610..b8a175de 100644 --- a/bash/functions/infra/install_volta.md +++ b/bash/functions/infra/install_volta.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "install_volta() -> void" description: "Instala Volta, el gestor de versiones de Node.js, usando el instalador oficial de get.volta.sh. Configura VOLTA_HOME y PATH en ~/.bashrc y ~/.zshrc. Idempotente: si Volta ya está instalado, informa y termina." -tags: [bash, install, volta, node] +tags: [bash, install, volta, node, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/keepass_delete.md b/bash/functions/infra/keepass_delete.md index 64505b15..2b10bb87 100644 --- a/bash/functions/infra/keepass_delete.md +++ b/bash/functions/infra/keepass_delete.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_delete(entry: string)" description: "Elimina una entry del KeePassXC database via keepassxc-cli rm. La entry pasa a la papelera dentro del .kdbx (no se borra fisicamente)." -tags: [keepass, keepassxc, secret, credential, delete] +tags: [keepass, keepassxc, secret, credential, delete, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/keepass_generate.md b/bash/functions/infra/keepass_generate.md index 76d48fd8..21b4acfb 100644 --- a/bash/functions/infra/keepass_generate.md +++ b/bash/functions/infra/keepass_generate.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_generate(entry: string, length?: int, username?: string, url?: string) -> string" description: "Genera un password aleatorio (lower+upper+digits+special), lo almacena en una entry nueva y lo imprime a stdout. Length default 24." -tags: [keepass, keepassxc, secret, credential, generate, random] +tags: [keepass, keepassxc, secret, credential, generate, random, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/keepass_get.md b/bash/functions/infra/keepass_get.md index bf86c119..b7561037 100644 --- a/bash/functions/infra/keepass_get.md +++ b/bash/functions/infra/keepass_get.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_get(entry: string, attr?: string) -> string" description: "Lee un atributo (Password por defecto) de una entry del KeePassXC database via keepassxc-cli. Resuelve master password desde pass (meta/keepassxc-master) o env KEEPASS_PASSWORD." -tags: [keepass, keepassxc, secret, credential, get] +tags: [keepass, keepassxc, secret, credential, get, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/keepass_list.md b/bash/functions/infra/keepass_list.md index 875fc9fc..06054f07 100644 --- a/bash/functions/infra/keepass_list.md +++ b/bash/functions/infra/keepass_list.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_list(prefix?: string) -> json" description: "Lista paths de entries del KeePassXC database como array JSON. Filtra opcionalmente por prefijo de grupo. Internamente usa keepass_dump y proyecta solo los paths." -tags: [keepass, keepassxc, list] +tags: [keepass, keepassxc, list, pendiente-usar] uses_functions: - keepass_dump_bash_infra uses_types: [] diff --git a/bash/functions/infra/keepass_search.md b/bash/functions/infra/keepass_search.md index c8bed72f..6cadf560 100644 --- a/bash/functions/infra/keepass_search.md +++ b/bash/functions/infra/keepass_search.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_search(term: string) -> json" description: "Busca entries en el KeePassXC database por substring. Devuelve array JSON de paths que matchean (title/username/url/notes)." -tags: [keepass, keepassxc, search, query] +tags: [keepass, keepassxc, search, query, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/keepass_set.md b/bash/functions/infra/keepass_set.md index d259876c..5f8f2457 100644 --- a/bash/functions/infra/keepass_set.md +++ b/bash/functions/infra/keepass_set.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "keepass_set(entry: string, password: string, username?: string, url?: string)" description: "Crea o sobreescribe una entry en el KeePassXC database. Auto-detecta si existe (edit) o no (add). Soporta username y url opcionales." -tags: [keepass, keepassxc, secret, credential, set, write] +tags: [keepass, keepassxc, secret, credential, set, write, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/list_listening_ports.md b/bash/functions/infra/list_listening_ports.md index 5c5affc5..8c897084 100644 --- a/bash/functions/infra/list_listening_ports.md +++ b/bash/functions/infra/list_listening_ports.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "list_listening_ports([mode: string]) -> void" description: "Lista puertos activos del sistema usando ss (preferido) o netstat como fallback. Modos: all (LISTEN), tcp, udp, established (conexiones activas), stats (resumen + interfaces). Imprime salida tabulada a stdout." -tags: [bash, ports, network, listening, monitoring] +tags: [bash, ports, network, listening, monitoring, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_connect.md b/bash/functions/infra/nordvpn_connect.md index 2a2a0bc0..7e408604 100644 --- a/bash/functions/infra/nordvpn_connect.md +++ b/bash/functions/infra/nordvpn_connect.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_connect(country?: string, city?: string) -> json" description: "Conecta a NordVPN por pais, ciudad o servidor especifico. Sin argumentos conecta al mejor servidor disponible. Devuelve JSON con resultado." -tags: [vpn, nordvpn, connect, infra, network] +tags: [vpn, nordvpn, connect, infra, network, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_disconnect.md b/bash/functions/infra/nordvpn_disconnect.md index cd9b1a1d..6cd4f3cb 100644 --- a/bash/functions/infra/nordvpn_disconnect.md +++ b/bash/functions/infra/nordvpn_disconnect.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_disconnect() -> json" description: "Desconecta de NordVPN. Idempotente — si no hay conexion activa retorna ok. Devuelve JSON con resultado." -tags: [vpn, nordvpn, disconnect, infra, network] +tags: [vpn, nordvpn, disconnect, infra, network, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_get_ip.md b/bash/functions/infra/nordvpn_get_ip.md index 6ca29068..b66f8302 100644 --- a/bash/functions/infra/nordvpn_get_ip.md +++ b/bash/functions/infra/nordvpn_get_ip.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_get_ip() -> json" description: "Obtiene IP publica actual con fallback entre multiples servicios. Indica si la conexion VPN esta activa y el servidor usado." -tags: [vpn, nordvpn, ip, infra, network, verification] +tags: [vpn, nordvpn, ip, infra, network, verification, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_list_cities.md b/bash/functions/infra/nordvpn_list_cities.md index 27898faa..7a03714a 100644 --- a/bash/functions/infra/nordvpn_list_cities.md +++ b/bash/functions/infra/nordvpn_list_cities.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_list_cities(country: string) -> json" description: "Lista ciudades disponibles de un pais en NordVPN como array JSON ordenado." -tags: [vpn, nordvpn, cities, infra, network] +tags: [vpn, nordvpn, cities, infra, network, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_list_countries.md b/bash/functions/infra/nordvpn_list_countries.md index c8574b05..4fec664f 100644 --- a/bash/functions/infra/nordvpn_list_countries.md +++ b/bash/functions/infra/nordvpn_list_countries.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_list_countries() -> json" description: "Lista paises disponibles en NordVPN como array JSON ordenado alfabeticamente." -tags: [vpn, nordvpn, countries, infra, network] +tags: [vpn, nordvpn, countries, infra, network, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_set_protocol.md b/bash/functions/infra/nordvpn_set_protocol.md index 226bd0e2..e436443d 100644 --- a/bash/functions/infra/nordvpn_set_protocol.md +++ b/bash/functions/infra/nordvpn_set_protocol.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_set_protocol(protocol: string) -> json" description: "Cambia el protocolo de NordVPN entre NordLynx (WireGuard) y OpenVPN. NordLynx recomendado por velocidad." -tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra] +tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/nordvpn_status.md b/bash/functions/infra/nordvpn_status.md index 6b9ac05f..6fe78ef7 100644 --- a/bash/functions/infra/nordvpn_status.md +++ b/bash/functions/infra/nordvpn_status.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "nordvpn_status() -> json" description: "Obtiene estado actual de NordVPN como JSON estructurado. Incluye servidor, IP, pais, protocolo y estado de conexion." -tags: [vpn, nordvpn, status, infra, network] +tags: [vpn, nordvpn, status, infra, network, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/pass_delete.md b/bash/functions/infra/pass_delete.md index 5da9ec96..9b674a2a 100644 --- a/bash/functions/infra/pass_delete.md +++ b/bash/functions/infra/pass_delete.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "pass_delete(entry: string) -> void" description: "Elimina un secreto del password store (pass)." -tags: [pass, secret, credential, delete] +tags: [pass, secret, credential, delete, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/pass_generate.md b/bash/functions/infra/pass_generate.md index 4657ed81..1463cd12 100644 --- a/bash/functions/infra/pass_generate.md +++ b/bash/functions/infra/pass_generate.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "pass_generate(entry: string, [length: int]) -> string" description: "Genera un password aleatorio, lo almacena en el password store e imprime el valor generado." -tags: [pass, secret, credential, generate, random] +tags: [pass, secret, credential, generate, random, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/pass_list.md b/bash/functions/infra/pass_list.md index 3ba49e75..5c10db07 100644 --- a/bash/functions/infra/pass_list.md +++ b/bash/functions/infra/pass_list.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "pass_list([prefix: string]) -> json" description: "Lista entradas del password store como JSON array. Filtra opcionalmente por prefijo." -tags: [pass, secret, credential, list] +tags: [pass, secret, credential, list, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/pass_sync.md b/bash/functions/infra/pass_sync.md index 414ed9c1..052acd0d 100644 --- a/bash/functions/infra/pass_sync.md +++ b/bash/functions/infra/pass_sync.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "pass_sync() -> json" description: "Sincroniza el password store con el repositorio git remoto (pull + push)." -tags: [pass, secret, sync, git] +tags: [pass, secret, sync, git, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/port_kill.md b/bash/functions/infra/port_kill.md index ddbe6107..a0b77aad 100644 --- a/bash/functions/infra/port_kill.md +++ b/bash/functions/infra/port_kill.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "port_kill(port: int, signal: string) -> void" description: "Mata los procesos que escuchan en un puerto TCP dado. Idempotente: si no hay proceso en el puerto retorna exit 0. Hace un segundo intento con SIGKILL si el primer intento con signal no libera el puerto." -tags: ["port", "kill", "process", "tcp"] +tags: [port, kill, process, tcp, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/pre_commit_hook_install.md b/bash/functions/infra/pre_commit_hook_install.md index e594b8ab..4a7ae607 100644 --- a/bash/functions/infra/pre_commit_hook_install.md +++ b/bash/functions/infra/pre_commit_hook_install.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "pre_commit_hook_install(repo_dir: string, [--force]) -> void" description: "Instala un hook pre-commit en .git/hooks/pre-commit de un repo dado. El hook invoca scan_secrets_in_dirty para abortar el commit si detecta secrets en archivos staged. Idempotente: si el hook ya esta instalado (marca fn_registry-pre-commit-v1) no lo sobreescribe a menos que se pase --force." -tags: ["git", "hook", "precommit", "secrets"] +tags: [git, hook, precommit, secrets, pendiente-usar] uses_functions: ["scan_secrets_in_dirty_bash_cybersecurity"] uses_types: [] returns: [] diff --git a/bash/functions/infra/systemd_local_restart.md b/bash/functions/infra/systemd_local_restart.md index 208cdbd1..c6ca18bc 100644 --- a/bash/functions/infra/systemd_local_restart.md +++ b/bash/functions/infra/systemd_local_restart.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "systemd_local_restart(name: string) -> json" description: "Reinicia un servicio systemd local con systemctl restart. Útil tras actualizar el binario o cambiar el unit. Requiere sudo." -tags: [systemd, service, local, infra, restart] +tags: [systemd, service, local, infra, restart, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/systemd_local_uninstall.md b/bash/functions/infra/systemd_local_uninstall.md index 321efefb..0804a173 100644 --- a/bash/functions/infra/systemd_local_uninstall.md +++ b/bash/functions/infra/systemd_local_uninstall.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "systemd_local_uninstall(name: string) -> json" description: "Detiene, deshabilita y elimina el unit file de un servicio systemd local. Idempotente: no falla si el servicio ya está parado o el unit no existe. Requiere sudo." -tags: [systemd, service, local, infra, uninstall, cleanup] +tags: [systemd, service, local, infra, uninstall, cleanup, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/tail_journal.md b/bash/functions/infra/tail_journal.md index 00e7f9b9..f6642887 100644 --- a/bash/functions/infra/tail_journal.md +++ b/bash/functions/infra/tail_journal.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "tail_journal(unit: string, lines: int=100, follow: bool=false, since: string=\"\", priority: string=\"info\") -> void" description: "Wrapper sobre journalctl con formato consistente. Tail logs de una unidad systemd con coloreado, filtro por prioridad y seguimiento opcional." -tags: ["journal", "systemd", "logs"] +tags: [journal, systemd, logs, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/tbd_branch_create.md b/bash/functions/infra/tbd_branch_create.md index a04da345..10f35829 100644 --- a/bash/functions/infra/tbd_branch_create.md +++ b/bash/functions/infra/tbd_branch_create.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "tbd_branch_create(mode: string, ...args: string) -> void" description: "Crea una rama TBD (trunk-based development) desde master/main actualizado. Soporta modos 'issue ' y 'quick '. Autodetecta la rama base (master/main), verifica working tree limpio, hace pull --rebase y crea la rama. Valida formato de numero de issue (4 digitos) y slug (kebab-case ASCII)." -tags: [git, tbd, branch, trunk-based-development, workflow] +tags: [git, tbd, branch, trunk-based-development, workflow, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/tbd_branch_finish.md b/bash/functions/infra/tbd_branch_finish.md index 37a0d1bb..2c4e0090 100644 --- a/bash/functions/infra/tbd_branch_finish.md +++ b/bash/functions/infra/tbd_branch_finish.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "tbd_branch_finish([merge_title: string]) -> void" description: "Integra una rama TBD (issue/* o quick/*) a master/main con merge --no-ff, publica el merge al remote y elimina la rama local. Autodetecta la rama base (master/main), verifica working tree limpio y construye el titulo del merge commit. NO ejecuta tests — esa responsabilidad es del caller. Exit 2 si hay conflicto de merge (deja al usuario resolver)." -tags: [git, tbd, merge, trunk-based-development, workflow] +tags: [git, tbd, merge, trunk-based-development, workflow, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/telemetry_prelude.md b/bash/functions/infra/telemetry_prelude.md new file mode 100644 index 00000000..420f567a --- /dev/null +++ b/bash/functions/infra/telemetry_prelude.md @@ -0,0 +1,55 @@ +--- +name: telemetry_prelude +lang: bash +domain: infra +version: 0.1.0 +purity: impure +kind: function +description: "Prelude bash que envuelve cada funcion del registry definida en el shell con un wrapper que mide duration y registra cada llamada en call_monitor.operations.db. Activable con FN_TELEMETRY=1. Issue 0085c." +tags: [telemetry, monitoring, registry, bash-wrapper, pendiente-usar] +signature: "source telemetry_prelude.sh" +error_type: "error_go_core" +returns_optional: false +params: + - name: FN_TELEMETRY (env) + desc: "Si vale '1', el prelude auto-envuelve cada funcion bash conocida del registry. Si no, return 0 inmediato sin hacer nada." + - name: FN_REGISTRY_ROOT (env) + desc: "Override de la raiz. Si no se setea, se descubre walking up desde cwd buscando registry.db." + - name: CLAUDE_SESSION_ID (env) + desc: "ID de sesion Claude Code persistido en cada fila de calls." +output: "Sin output. Side effect: cada funcion del registry sourceada queda reemplazada por un wrapper con telemetria. Idempotente." +uses_functions: [] +uses_types: [] +imports: + - sqlite3 (CLI) + - date (coreutils) + - find (coreutils) +example: | + # Auto-wrap + export FN_TELEMETRY=1 + source bash/functions/infra/android_screenshot.sh # define android_screenshot() + source bash/functions/infra/telemetry_prelude.sh # envuelve android_screenshot + android_screenshot /tmp/out.png # registrado en calls como android_screenshot_bash_infra +file_path: "bash/functions/infra/telemetry_prelude.sh" +tested: false +notes: | + Mecanismo: `declare -f ` extrae el cuerpo de la funcion. Se renombra + a `_fn_t_orig_` via eval. La funcion original queda reemplazada por + un wrapper que mide `date +%s%3N` antes/despues, ejecuta `_fn_t_orig_`, + captura exit code, y llama `_fn_t_log function_id duration_ms success error_class`. + + function_id heuristic: `{name}_bash_{domain}` donde `name`=basename del .sh + y `domain`=basename del directorio padre. Coincide con convencion del registry. + + Fail-safe: si la BD no existe, sqlite3 falta, o INSERT falla, el wrapper + ignora silenciosamente y retorna el exit code del original. NUNCA aborta + ni modifica el comportamiento de la funcion envuelta. + + Idempotente: marca cada wrapper con `_FN_T_WRAPPED_=1` y no + re-envuelve. Sourcear el prelude N veces es seguro. + + Limitacion: el wrapper requiere que las funciones del registry ya esten + sourceadas antes de cargarse este prelude. Si la app sourcea una funcion + DESPUES del prelude, esa funcion NO queda envuelta automaticamente — hay + que llamar `_fn_t_autowrap` manualmente o usar `_fn_t_wrap `. +--- diff --git a/bash/functions/infra/telemetry_prelude.sh b/bash/functions/infra/telemetry_prelude.sh new file mode 100644 index 00000000..41be60c3 --- /dev/null +++ b/bash/functions/infra/telemetry_prelude.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# telemetry_prelude — Wrapper de telemetria para funciones bash del registry. +# Issue 0085c-bash. +# +# Uso 1 (manual): source bash/functions//.sh primero, despues source +# este archivo, y todas las funciones bash del registry +# quedaran envueltas con logging a call_monitor.operations.db. +# +# Uso 2 (auto): exportar FN_TELEMETRY=1 antes de sourcear; este prelude +# detecta cada funcion definida que coincida con una funcion +# del registry (bash/functions//.sh) y la +# redefine como wrapper. +# +# Reglas: +# - Idempotente: marca cada wrapper con flag _FN_T_WRAPPED_=1; no +# re-envuelve si ya esta envuelta. +# - No-op silencioso si BD no existe o INSERT falla. NUNCA aborta el caller. +# - Solo guarda function_id, duration_ms, success, error_class. NUNCA args. + +if [ "${FN_TELEMETRY:-0}" != "1" ]; then + return 0 2>/dev/null || exit 0 +fi + +# ---- Resolve registry root ---- +_fn_t_root() { + if [ -n "${FN_REGISTRY_ROOT:-}" ] && [ -f "$FN_REGISTRY_ROOT/registry.db" ]; then + printf '%s' "$FN_REGISTRY_ROOT" + return 0 + fi + local d="${PWD}" + while [ "$d" != "/" ]; do + if [ -f "$d/registry.db" ]; then + printf '%s' "$d" + return 0 + fi + d=$(dirname "$d") + done + return 1 +} + +# ---- Resolve operations.db ---- +_FN_T_DB="" +_fn_t_resolve_db() { + if [ -n "$_FN_T_DB" ] && [ -f "$_FN_T_DB" ]; then return 0; fi + local root + root=$(_fn_t_root) || return 1 + _FN_T_DB="$root/projects/fn_monitoring/apps/call_monitor/operations.db" + if [ ! -f "$_FN_T_DB" ]; then + _FN_T_DB="" + return 1 + fi + return 0 +} + +# ---- Log call ---- +_fn_t_log() { + local fn_id="$1" duration_ms="$2" success="$3" error_class="${4:-}" + _fn_t_resolve_db || return 0 + command -v sqlite3 >/dev/null 2>&1 || return 0 + local sid="${CLAUDE_SESSION_ID:-}" + local ts + ts=$(date -u +%s) + sid="${sid//\'/\'\'}" + fn_id="${fn_id//\'/\'\'}" + error_class="${error_class//\'/\'\'}" + sqlite3 "$_FN_T_DB" "INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts) VALUES ('$sid','$fn_id','bash_wrapper','',$duration_ms,$success,'$error_class','',$ts);" 2>/dev/null || true +} + +# ---- Wrap a single function by name ---- +_fn_t_wrap() { + local orig="$1" fn_id="$2" + + # already wrapped? + local guard + guard="_FN_T_WRAPPED_${orig}" + if [ "${!guard:-0}" = "1" ]; then return 0; fi + + # must exist as a function + declare -F "$orig" >/dev/null 2>&1 || return 1 + + # capture original body, rename to _fn_t_orig_ + local body + body=$(declare -f "$orig") || return 1 + # body starts with " ()"; prepend prefix to rename + local renamed="_fn_t_orig_${body}" + eval "$renamed" + + # define wrapper with the original name + eval " +${orig}() { + local _t0_ms _t1_ms _rc _dur + _t0_ms=\$(date +%s%3N) + _fn_t_orig_${orig} \"\$@\" + _rc=\$? + _t1_ms=\$(date +%s%3N) + _dur=\$((_t1_ms - _t0_ms)) + if [ \$_rc -eq 0 ]; then + _fn_t_log \"${fn_id}\" \$_dur 1 \"\" + else + _fn_t_log \"${fn_id}\" \$_dur 0 \"exit_\$_rc\" + fi + return \$_rc +} +" + eval "$guard=1" +} + +# ---- Auto-wrap: walk bash/functions and wrap every function currently defined ---- +_fn_t_autowrap() { + local root + root=$(_fn_t_root) || return 1 + local f domain name fn_id + while IFS= read -r f; do + domain=$(basename "$(dirname "$f")") + name=$(basename "$f" .sh) + fn_id="${name}_bash_${domain}" + if declare -F "$name" >/dev/null 2>&1; then + _fn_t_wrap "$name" "$fn_id" + fi + done < <(find "$root/bash/functions" -type f -name '*.sh' 2>/dev/null) +} + +# ---- Run autowrap on source ---- +_fn_t_autowrap diff --git a/bash/functions/infra/wait_for_http.md b/bash/functions/infra/wait_for_http.md index 0114b779..fccd9f71 100644 --- a/bash/functions/infra/wait_for_http.md +++ b/bash/functions/infra/wait_for_http.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "wait_for_http [timeout_seconds] [interval_seconds]" description: "Hace polling a una URL HTTP/HTTPS hasta recibir respuesta 2xx o agotar el timeout. Util en deploys, post-restart de servicios y smoke tests." -tags: [http, wait, poll, health, deploy, smoke-test] +tags: [http, wait, poll, health, deploy, smoke-test, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/infra/wait_for_port.md b/bash/functions/infra/wait_for_port.md index dce32b13..629f5926 100644 --- a/bash/functions/infra/wait_for_port.md +++ b/bash/functions/infra/wait_for_port.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "wait_for_port(host: string, port: int, timeout_seconds: int, interval_seconds: int) -> int" description: "Hace polling TCP a host:puerto hasta que acepte conexiones o agote el timeout. Util para esperar a que un servicio (DB, API, container) este listo antes de ejecutar pasos siguientes." -tags: [tcp, wait, poll, port] +tags: [tcp, wait, poll, port, pendiente-usar] uses_functions: [] uses_types: [] returns: [] diff --git a/bash/functions/pipelines/compile_cpp_app.md b/bash/functions/pipelines/compile_cpp_app.md index c128555d..f381f3da 100644 --- a/bash/functions/pipelines/compile_cpp_app.md +++ b/bash/functions/pipelines/compile_cpp_app.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "compile_cpp_app(app_name?: string) -> void" description: "Pipeline que resuelve la app C++ desde el nombre o CWD, la cross-compila para Windows con mingw-w64, y despliega el .exe al escritorio de Windows. Composicion de resolve_cpp_app_dir + build_cpp_windows + deploy_cpp_exe_to_windows." -tags: [cpp, compile, windows, mingw, cross-compile, deploy, pipeline] +tags: [cpp, compile, windows, mingw, cross-compile, deploy, pipeline, pendiente-usar] uses_functions: - resolve_cpp_app_dir_bash_infra - build_cpp_windows_bash_infra diff --git a/bash/functions/pipelines/full_git_pull.md b/bash/functions/pipelines/full_git_pull.md index eff6d55c..cf142bbd 100644 --- a/bash/functions/pipelines/full_git_pull.md +++ b/bash/functions/pipelines/full_git_pull.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "full_git_pull() -> stdout: tabla resumen" description: "Pull automatico de fn_registry + todos los sub-repos locales + submodules + fn sync. Descubre repos locales, stashea dirty trees antes de pullear, hace pull --ff-only, actualiza submodulos del repo principal, pulla ~/.password-store, regenera registry.db con fn index y ejecuta fn sync." -tags: [git, pull, sync, registry, pipeline] +tags: [git, pull, sync, registry, pipeline, pendiente-usar] uses_functions: - discover_git_repos_bash_infra - git_pull_with_stash_bash_infra diff --git a/bash/functions/pipelines/full_git_push.md b/bash/functions/pipelines/full_git_push.md index 5d33d070..2cdbecca 100644 --- a/bash/functions/pipelines/full_git_push.md +++ b/bash/functions/pipelines/full_git_push.md @@ -7,7 +7,7 @@ version: "1.0.0" purity: impure signature: "full_git_push(commit_message?: string) -> stdout: tabla resumen" description: "Push automatico de fn_registry + todos los sub-repos + fn sync. Descubre repos, escanea secrets (aborta si detecta), auto-inicializa apps/analyses sin .git via ensure_repo_synced, auto-commitea dirty trees, pushea solo repos adelantados, pushea ~/.password-store sin commitear, y ejecuta fn sync." -tags: [git, push, sync, registry, pipeline] +tags: [git, push, sync, registry, pipeline, pendiente-usar] uses_functions: - discover_git_repos_bash_infra - scan_secrets_in_dirty_bash_cybersecurity diff --git a/bash/functions/pipelines/generate_capability_doc.md b/bash/functions/pipelines/generate_capability_doc.md new file mode 100644 index 00000000..6fad4554 --- /dev/null +++ b/bash/functions/pipelines/generate_capability_doc.md @@ -0,0 +1,111 @@ +--- +name: generate_capability_doc +kind: pipeline +lang: bash +domain: pipelines +version: "1.0.0" +purity: impure +signature: "generate_capability_doc(group: string, --registry: string?, --out: string?) -> void" +description: "Regenera la tabla de funciones de una pagina capability en docs/capabilities/.md consultando registry.db. Preserva bloques curated (Ejemplo canonico, Fronteras, Prerequisitos, Notas). Si el archivo no existe lo crea con plantilla minima." +tags: ["capability-groups", "docs", "doctor", "generator", "pipeline"] +uses_functions: + - audit_capability_groups_go_infra +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +params: + - name: group + desc: "slug del capability group (ej. notebook, metabase). Coincide con el tag canonico en frontmatter de funciones." + - name: --registry + desc: "path opcional a registry.db o a su directorio padre (default: walk-up desde cwd hasta encontrar registry.db)." + - name: --out + desc: "path opcional del archivo de salida (default: /docs/capabilities/.md)." +output: "path del archivo actualizado o creado + count de funciones a stdout. Exit 0 ok, exit 1 si error." +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/pipelines/generate_capability_doc.sh" +--- + +## Ejemplo + +```bash +# Regenerar tabla de notebook (ya existe, preserva Ejemplo canonico / Fronteras) +./bash/functions/pipelines/generate_capability_doc.sh notebook +# → /home/lucas/fn_registry/docs/capabilities/notebook.md updated (5 functions) + +# Crear pagina nueva para un grupo sin pagina todavia +./bash/functions/pipelines/generate_capability_doc.sh metabase +# → /home/lucas/fn_registry/docs/capabilities/metabase.md created (12 functions) + +# Especificar registry y destino custom +./bash/functions/pipelines/generate_capability_doc.sh android \ + --registry /ruta/alternativa/registry.db \ + --out /tmp/android_cap.md +# → /tmp/android_cap.md created (8 functions) + +# Grupo sin funciones todavia (avisa pero no falla) +./bash/functions/pipelines/generate_capability_doc.sh nuevo_grupo +# WARN: El grupo 'nuevo_grupo' no tiene funciones con ese tag en registry.db. +# → /home/lucas/fn_registry/docs/capabilities/nuevo_grupo.md created (0 functions) +``` + +## Comportamiento detallado + +### Resolucion de root + +Walk-up desde `cwd` buscando `registry.db`. Si se pasa `--registry`: +- Si es un archivo: toma el directorio padre. +- Si es un directorio: lo usa directamente. + +### Tabla generada (SQL) + +```sql +SELECT f.id, f.signature, f.description +FROM functions f, json_each(f.tags) j +WHERE j.value = '' +ORDER BY f.id; +``` + +JOIN custom via `json_each` — usa `sqlite3` directo (excepcion autorizada para JOINs no expuestos por MCP). + +### Formato de tabla + +```markdown +| ID | Firma | Que hace | +|---|---|---| +| `` | `` | | +``` + +Los `|` dentro de signatures y descriptions se escapan como `\|`. + +### Preservacion de bloques curated + +Cuando el archivo ya existe: +- El bloque entre `## Funciones` y la siguiente `## ` se reemplaza. +- Todo lo anterior y posterior (Ejemplo canonico, Fronteras, Prerequisitos, Notas, etc.) se mantiene intacto. +- Implementado con `awk`: copia hasta `## Funciones`, imprime nueva tabla, salta contenido viejo hasta encontrar `^## `, reanuda copia. + +### Archivo nuevo + +Si `docs/capabilities/.md` no existe, se crea con plantilla minima: +- Titulo `# Capability: `. +- Placeholder de descripcion (editable a mano). +- Tabla generada. +- Secciones vacias: `## Ejemplo canonico`, `## Fronteras`. + +## Codigos de salida + +| Codigo | Significado | +|--------|-------------| +| 0 | Exito | +| 1 | Error: grupo no especificado, registry.db no encontrado, fallo SQL, awk vacio | + +## Notas + +- `uses_functions: []` — depende de `sqlite3` y `awk` del sistema, no de funciones del registry. +- El tag del grupo debe ser plano (ej. `notebook`, `metabase`), no `tag:notebook`. +- Enganchado en `docs/capabilities/INDEX.md` como el mecanismo de auto-generacion referenciado bajo `fn doctor capabilities --update`. +- Seguro para re-ejecucion: idempotente si el registry no cambia. diff --git a/bash/functions/pipelines/generate_capability_doc.sh b/bash/functions/pipelines/generate_capability_doc.sh new file mode 100644 index 00000000..b2b6355b --- /dev/null +++ b/bash/functions/pipelines/generate_capability_doc.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +# generate_capability_doc — regenera la tabla "Funciones" de una pagina capability +# Preserva bloques curated (Ejemplo canonico, Fronteras, Prerequisitos, Notas, etc.) +# Usage: generate_capability_doc [--registry ] [--out ] +set -euo pipefail + +# -------------------------------------------------------------------------- +# Helpers +# -------------------------------------------------------------------------- +die() { echo "ERROR: $*" >&2; exit 1; } +warn() { echo "WARN: $*" >&2; } + +# -------------------------------------------------------------------------- +# Parse args +# -------------------------------------------------------------------------- +GROUP="" +REGISTRY_PATH="" +OUT_PATH="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --registry) + shift + REGISTRY_PATH="${1:-}" + [[ -z "$REGISTRY_PATH" ]] && die "--registry requiere un valor" + shift + ;; + --out) + shift + OUT_PATH="${1:-}" + [[ -z "$OUT_PATH" ]] && die "--out requiere un valor" + shift + ;; + -*) + die "Opcion desconocida: $1" + ;; + *) + [[ -n "$GROUP" ]] && die "Solo se acepta un . Ya se especifico: '$GROUP'" + GROUP="$1" + shift + ;; + esac +done + +[[ -z "$GROUP" ]] && die "Uso: generate_capability_doc [--registry ] [--out ]" + +# -------------------------------------------------------------------------- +# Resolver registry root +# -------------------------------------------------------------------------- +find_registry_root() { + local dir + dir="$(pwd)" + while [[ "$dir" != "/" ]]; do + if [[ -f "$dir/registry.db" ]]; then + echo "$dir" + return 0 + fi + dir="$(dirname "$dir")" + done + return 1 +} + +if [[ -n "$REGISTRY_PATH" ]]; then + # Si se pasa un path que termina en registry.db, tomar el directorio + if [[ -f "$REGISTRY_PATH" ]]; then + REGISTRY_ROOT="$(dirname "$(realpath "$REGISTRY_PATH")")" + elif [[ -d "$REGISTRY_PATH" ]]; then + REGISTRY_ROOT="$(realpath "$REGISTRY_PATH")" + else + die "registry no encontrado en: $REGISTRY_PATH" + fi +else + REGISTRY_ROOT="$(find_registry_root)" || die "No se encontro registry.db. Ejecutar desde dentro del registry o pasar --registry." +fi + +DB="$REGISTRY_ROOT/registry.db" +[[ -f "$DB" ]] || die "registry.db no encontrado en: $DB" + +# -------------------------------------------------------------------------- +# Resolver output path +# -------------------------------------------------------------------------- +if [[ -z "$OUT_PATH" ]]; then + OUT_PATH="$REGISTRY_ROOT/docs/capabilities/${GROUP}.md" +fi + +# -------------------------------------------------------------------------- +# Consultar funciones del grupo +# -------------------------------------------------------------------------- +# JOIN custom entre functions y json_each(tags) — excepcion autorizada para sqlite3 directo. +# Usamos U+001F (ASCII Unit Separator) como separador de campos para evitar conflictos +# con el caracter | que aparece en signatures Python (ej. "list[int] | None"). +SEP=$'\x1f' +ROWS="$(sqlite3 -separator "$SEP" "$DB" \ + "SELECT f.id, f.signature, f.description + FROM functions f, json_each(f.tags) j + WHERE j.value = '${GROUP}' + ORDER BY f.id;" 2>/dev/null)" || die "Error al consultar registry.db" + +# Escapar | en un valor para que no rompa la tabla Markdown +escape_pipe() { + printf '%s' "$1" | sed 's/|/\\|/g' +} + +# Construir tabla Markdown +TABLE_HEADER="| ID | Firma | Que hace | +|---|---|---|" + +TABLE_ROWS="" +FUNC_COUNT=0 + +if [[ -n "$ROWS" ]]; then + while IFS="$SEP" read -r id signature description; do + # Limpiar espacios extra + id="${id// /}" + signature="$(printf '%s' "$signature" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + description="$(printf '%s' "$description" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + # Escapar pipes internos en firma y descripcion + sig_esc="$(escape_pipe "$signature")" + desc_esc="$(escape_pipe "$description")" + TABLE_ROWS="${TABLE_ROWS}| \`${id}\` | \`${sig_esc}\` | ${desc_esc} | +" + FUNC_COUNT=$((FUNC_COUNT + 1)) + done <<< "$ROWS" +fi + +if [[ $FUNC_COUNT -eq 0 ]]; then + warn "El grupo '${GROUP}' no tiene funciones con ese tag en registry.db." + TABLE_CONTENT="_No hay funciones con tag ${GROUP}._" +else + TABLE_CONTENT="${TABLE_HEADER} +${TABLE_ROWS}" +fi + +# -------------------------------------------------------------------------- +# Crear o actualizar el archivo +# -------------------------------------------------------------------------- +mkdir -p "$(dirname "$OUT_PATH")" + +if [[ ! -f "$OUT_PATH" ]]; then + # --- Archivo nuevo: plantilla minima --- + cat > "$OUT_PATH" <