# AGENTS.md — add-to-cart-direct-checkout-for-woocommerce

WordPress.org WooCommerce plugin (free version). No composer/npm/build/test/lint/CI. Verify manually in WP + WooCommerce.

## Entrypoints
- `pi-dcw.php` — bootstrap. Guards: deactivates self if Pro active (`pi_dcw_pro_check()`), aborts with admin notice if WooCommerce inactive. Declares HPOS compat (`custom_order_tables`). Bump version in 3 places together: plugin header `Version:`, `PI_DCW_VERSION`, `README.txt` Stable tag.
- `includes/class-pi-dcw.php` (`Pi_Dcw::run()`) — loader-pattern core; only registers enqueue + i18n hooks.
- `includes/includes.php` — real feature manifest. Every admin/public feature class is required here; add new files here.
- `admin/class-pi-dcw-menu.php` — settings page `WooCommerce > Direct Checkout` (`admin.php?page=pi-dcw`). Tabs wired via actions `pi-dcw_tab` / `pi-dcw_tab_content` / `pi-dcw_promotion`.

## Settings convention (do not invent a new one)
- Each tab is a class in `admin/class-pi-dcw-*.php` that self-instantiates on `add_action('init', ...)`, defines `$setting_key` + `$settings` array, calls `register_setting` per field, renders via `new pisol_class_form_dcw($setting, $key)` (`includes/pisol.class.form.php`). Note: the constructor takes only `($setting)` — the second arg passed at call sites is ignored.
- Field types map to methods in that form class (`switch`, `select`, `text`, `color`, `setting_category`, etc.). Reuse them. Note: tab classes currently call bare `register_setting($key, $field)` with no `sanitize_callback` — `pisol_class_form_dcw::register_setting()/getValidationFunction()` exists but is unused, so add sanitization explicitly if needed.
- Options are plain `get_option('pi_dcw_*')` with defaults inline at read time (see `public/class-pi-dcw-public.php::__construct`). Keep option names stable.

## Public behavior (`public/`, `admin/`)
- Redirect logic: `Pi_Dcw_Public::redirect_to_selected_page()` on `woocommerce_add_to_cart_redirect`. Shared URL builder `Pi_Dcw_Public::global_redirect_url()` (page permalink / custom URL / checkout fallback) — reused by both the PHP redirect and the JS-localized `pisol_dcw_setting.redirect_url`. AJAX add-to-cart handled separately via `woocommerce_get_script_data` (`cart_redirect_after_add`); early-return on `DOING_AJAX` — keep all paths in sync.
- Woo Blocks support: `enqueue_scripts()` localizes `redirect_url` (empty string when global redirect off) and `pi-dcw-public.js::blockAddToCart()` listens for `wc-blocks_added_to_cart` → `window.location = pisol_dcw_setting.redirect_url`. Only fires when global redirect is on; guard for missing/empty `pisol_dcw_setting` before extending.
- Buy Now button styling (`public/css/pi-dcw-public.css`): centered flex pill, not full-width — `a.pisol_buy_now_button` (archives, `max-width:200px`, `margin:10px auto`) vs `.pisol_single_buy_now` (single product, `max-width:300px`). Shared look: `min-height:46px`, `radius:8px`, hover lift + shadow, `:active`, `:focus-visible` outline, `:disabled`/`[disabled]` muted. Colors still injected via `enqueue_styles()` inline CSS; width override via `pisol_dcw_button_size` option targets `.pisol_buy_now_button.pisol_single_buy_now`.
- Pro hooks to preserve: filters `pi_disable_redirect_for_this_product`, `pi_dcw_product_overwrite_global`, `pi_dcw_product_redirect_to_page`.
- Disable-cart uses `template_redirect` → checkout (empty cart → shop). Single-page checkout injects `[woocommerce_cart]` before checkout form (`woocommerce_before_checkout_form`); old `the_content` path only if `pi_dcw_use_old_way` set. Requires `[woocommerce_checkout]` shortcode — does NOT work with Checkout Block (documented in README FAQ).
- Buy Now / Quick View live in `public/class-pi-dcw-buy-now.php` (`Pi_WooCommerce_Quick_Buy_Auto_Add`, `pi_quick_checkout` param) and `public/quickview/`. Buy Now on archives is simple-products-only.

## Gotchas
- Every PHP file starts with `if ( ! defined( 'ABSPATH' ) ) exit;` (or `WPINC`); keep it. Escape output (`esc_html/url/attr`, `wp_kses_post`); form class already whitelists tags.
- Text domain `pi-dcw`, template `languages/pi-dcw.pot`, `wpml-config.xml` — wrap all user strings in `__()`/`esc_html_e()`.
- Shared vendored helpers (`pisol.class.form.php`, `review.php`, `Pro_Warning.php`) are copied across Pi plugins — edit locally only, don't "fix" globally.
- No uninstall cleanup (`uninstall.php` is a stub; `PISOL_DCW_DELETE_SETTING` is defined but unused) — don't assume options are removed.
