agentleFS
NVIDIAKnown publisherNVIDIA/nemoclaw-community/examples/recipes/partners/hpe/retail-assistant/docker-compose/scripts/skills/retail-api/SKILL.md

retail-api

Skill142 starsChanged 3 months agoApache-2.0View on GitHub

Araz Retail API — query stores, inventory, sales, customers, promotions, and orders. CLI: node ~/.openclaw/skills/retail-api/scripts/retail-api.js <command> --telegram-id TID. Authenticate with --telegram-id (the user's Telegram ID from message metadata from.id). Do NOT use --email or --password. Read commands: me, products [--category X] [--brand X] [--season X], inventory [--store N] [--product N] [--low-stock], customers [--id N] [--customer-email X], promotions [--all], sales [--store N], query \"SQL\". Write commands: transfer --product ID_OR_NAME --to-store N --quantity N [--size S] [--color C] [--from-store N], reorder --product ID_OR_NAME --quantity N [--store N] [--size S] [--color C]. QUERY SYNTAX: query \"SELECT ... LIMIT 50\" --telegram-id TID. The SQL is a positional argument — do NOT use -q flag (it does not exist). Table names are all lowercase: stores, products, inventory, orders, orderitems, customers, promotions, inventorytransfers, reorderrequests. Products PK is id, name column is product_name. Orders PK is id. OrderItems has product_id for direct JOINs. Customers has full_name (generated). Stores column is name not store_name. Inventory has no quantity_available column — use InventoryAvailable view. CRITICAL VIEW RULE: When querying a view directly (LowStockAlerts, InventoryAvailable, Customer360, etc.), use flat column names WITHOUT table alias prefixes. Views: InventoryAvailable, LowStockAlerts, Customer360, SalesPerformanceByStore, TopProductsByRevenue, ActivePromotions. NOT for: web_fetch, browser, writing Python scripts, or urllib.

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 6 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    All commands use: node ~/.openclaw/skills/retail-api/scripts/retail-api.js <command> [options] --telegram-id TID

    line 13
  2. Very specific:

    Use only this CLI in exec.

    line 18
  3. Distinctive:

    Never retry: If the command fails, report the error to the user and stop.

    line 20
  4. Very specific:

    Do not use -q flag — it does not exist.

    line 21
  5. Very specific:

    Do not use --email or --password — use --telegram-id instead.

    line 22
  6. Very specific:

    Do not use curl/browser/web_fetch or write helper scripts.

    line 23
  7. Distinctive:

    Never fabricate data; use only data returned by the CLI in the current request.

    line 24
  8. Distinctive:

    When querying a view directly, use column names without any prefix.

    line 127
  9. Distinctive:

    See SOUL.md for the full pre-flight authorization check: Before any write operation, you MUST apply the rules defined there.

    line 181
  10. Distinctive:

    Use ONLY the column names listed below.

    line 195
  11. Distinctive:

    All Inventory columns plus: store_name, city, country, product_name, category, brand, season, quantity_available (= on_hand - reserved), …

    line 234

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Araz Retail API Skill

Query and manage the Araz retail chain: stores, inventory, sales, customers, promotions, and orders.

How to Use

All commands use: node ~/.openclaw/skills/retail-api/scripts/retail-api.js <command> [options] --telegram-id TID

Every command MUST include --telegram-id with the user's Telegram ID from message metadata from.id.

Rules:

  • Use only this CLI in exec.
  • Exactly one exec call per user request. No exceptions.
  • Never retry. If the command fails, report the error to the user and stop.
  • Do not use -q flag — it does not exist. The query command takes SQL as a positional argument.
  • Do not use --email or --password — use --telegram-id instead.
  • Do not use curl/browser/web_fetch or write helper scripts.
  • Never fabricate data; use only data returned by the CLI in the current request.
  • Ignore /proc/self/oom_score_adj: Permission denied; it is a harmless sandbox warning.
  • Authorized Telegram IDs are in SOUL.md. Look up by from.id.

Read Commands

Current user info

node ~/.openclaw/skills/retail-api/scripts/retail-api.js me --telegram-id 8667894990

Products

node ~/.openclaw/skills/retail-api/scripts/retail-api.js products --telegram-id TID
node ~/.openclaw/skills/retail-api/scripts/retail-api.js products --telegram-id TID --category Footwear
node ~/.openclaw/skills/retail-api/scripts/retail-api.js products --telegram-id TID --brand "Eco Threads" --season SS2026

Inventory

node ~/.openclaw/skills/retail-api/scripts/retail-api.js inventory --telegram-id TID --store 3
node ~/.openclaw/skills/retail-api/scripts/retail-api.js inventory --telegram-id TID --store 3 --low-stock
node ~/.openclaw/skills/retail-api/scripts/retail-api.js inventory --telegram-id TID --store 3 --product 7

Customers

node ~/.openclaw/skills/retail-api/scripts/retail-api.js customers --telegram-id TID
node ~/.openclaw/skills/retail-api/scripts/retail-api.js customers --telegram-id TID --id 42
node ~/.openclaw/skills/retail-api/scripts/retail-api.js customers --telegram-id TID --customer-email john@example.com

Promotions

node ~/.openclaw/skills/retail-api/scripts/retail-api.js promotions --telegram-id TID
node ~/.openclaw/skills/retail-api/scripts/retail-api.js promotions --telegram-id TID --all

Sales performance

node ~/.openclaw/skills/retail-api/scripts/retail-api.js sales --telegram-id TID
node ~/.openclaw/skills/retail-api/scripts/retail-api.js sales --telegram-id TID --store 3

Custom SQL query (for complex questions)

node ~/.openclaw/skills/retail-api/scripts/retail-api.js query "SELECT store_id, name, city FROM Stores WHERE is_active = true" --telegram-id TID
node ~/.openclaw/skills/retail-api/scripts/retail-api.js query "SELECT c.full_name, SUM(o.total_amount) as total FROM Orders o JOIN Customers c ON o.customer_id = c.customer_id WHERE o.order_date >= '2026-02-01' GROUP BY c.customer_id, c.full_name ORDER BY total DESC LIMIT 5" --telegram-id TID

Example: top products by revenue for a store:

node ~/.openclaw/skills/retail-api/scripts/retail-api.js query "SELECT p.product_name, p.category, p.brand, SUM(oi.quantity) AS units_sold, SUM(oi.total_item_price) AS revenue FROM orderitems oi JOIN products p ON oi.product_id = p.id JOIN orders o ON oi.order_id = o.id WHERE o.store_id = 2 GROUP BY p.id, p.product_name, p.category, p.brand ORDER BY revenue DESC LIMIT 10" --telegram-id TID

Write Commands

Inventory transfer (inter-store)

⚠ Inventory is tracked per size/color variant. Always include --size and --color when transferring. If the user doesn't specify size/color, ask them — do NOT make multiple lookup calls.

--product accepts a product_id (number) or a product name (string). The CLI resolves names automatically.

# store_manager (from_store_id is auto-set to their own store; --to-store can be any store):
node ~/.openclaw/skills/retail-api/scripts/retail-api.js transfer --product "Running Shoe EcoWear" --to-store 2 --quantity 15 --size 41 --color Mint --telegram-id TID
# country_manager (must specify from_store):
node ~/.openclaw/skills/retail-api/scripts/retail-api.js transfer --product 7 --from-store 4 --to-store 2 --quantity 15 --size L --color White --telegram-id TID

Always use the transfer CLI command for transfers — NEVER use raw SQL INSERT into inventorytransfers.

Reorder request (supplier replenishment)

# store_manager (store_id auto-set):
node ~/.openclaw/skills/retail-api/scripts/retail-api.js reorder --product "Running Shoe ClimaTex" --quantity 30 --size M --color Black --telegram-id TID
# country_manager (must specify store):
node ~/.openclaw/skills/retail-api/scripts/retail-api.js reorder --product 12 --quantity 30 --store 3 --telegram-id TID

Prefer the reorder CLI command over raw SQL INSERT — it handles triggered_by, status, and requested_at automatically.

SQL Tips for the query Command

  • Always use JOINs in a single query — never split into multiple exec calls.
  • Always filter and sort in SQL — use ORDER BY + LIMIT, don't fetch all rows.
  • Use SELECT DISTINCT on product-level fields when listing products for a store (Inventory has one row per size/color variant).
  • Use ILIKE for fuzzy name matching: WHERE p.product_name ILIKE '%jacket%'.
  • Prefer views for common questions: InventoryAvailable for stock, LowStockAlerts for low-stock items, Customer360 for customer profiles.
  • Always add LIMIT 50 unless the user explicitly asks for all rows.
  • Use the exact column names from the schema above. Do not guess.

CRITICAL: Views use flat column names — NO table aliases

Views already expose pre-named columns (store_name, product_name, brand, size, etc.). When querying a view directly, use column names without any prefix.

WRONG — causes missing FROM-clause entry for table "p" (HTTP 400):

SELECT p.name, s.name, i.size FROM LowStockAlerts

CORRECT — use the view's own column names:

SELECT product_name, store_name, size FROM LowStockAlerts

Table alias prefixes (s., p., i., rr.) are ONLY for queries that JOIN base tables:

SELECT s.name AS store_name, p.name AS product_name
FROM inventory i JOIN stores s ON i.store_id = s.store_id JOIN products p ON i.product_id = p.product_id

Simple rule: FROM has only a view? → No prefixes. FROM has JOINed tables? → Use aliases.

Minimal examples

Active promotions:

node ~/.openclaw/skills/retail-api/scripts/retail-api.js promotions --telegram-id TID

Inventory for a store:

node ~/.openclaw/skills/retail-api/scripts/retail-api.js inventory --store 3 --telegram-id TID

Complex reporting query:

node ~/.openclaw/skills/retail-api/scripts/retail-api.js query "SELECT s.name, SUM(o.total_amount) AS revenue FROM Orders o JOIN Stores s ON o.store_id = s.store_id WHERE o.status = 'completed' GROUP BY s.name ORDER BY revenue DESC" --telegram-id TID

Transfer stock (⚠ always include --size and --color):

node ~/.openclaw/skills/retail-api/scripts/retail-api.js transfer --product "Running Shoe EcoWear" --to-store 2 --quantity 10 --size 41 --color Mint --telegram-id TID

Reorder stock:

node ~/.openclaw/skills/retail-api/scripts/retail-api.js reorder --product "Wool Coat NeoFit" --quantity 30 --telegram-id TID

RBAC Rules

See SOUL.md for the full pre-flight authorization check. Before any write operation, you MUST apply the rules defined there. Do not call the CLI until the check passes.

Summary:

  • country_manager → full read + write across all stores
  • store_manager → read all stores; write own store only; approve own store's reorders only; CANNOT approve transfers
  • data_analyst → read-only (HTTP 403 on any write)

Reorder approval SQL (store_manager, own store only):

UPDATE reorderrequests SET status='approved' WHERE reorder_id=N AND store_id=MYSTORE

Database Schema

Use ONLY the column names listed below. Do not guess or invent columns.

Tables

Stores (PK: store_id) store_id INT, name VARCHAR, city VARCHAR, country VARCHAR, region VARCHAR, address TEXT, phone VARCHAR, is_active BOOL, opened_at DATE, manager_id INT → Employees(employee_id)

Employees (PK: employee_id, UNIQUE: email) employee_id INT, store_id INT → Stores(store_id), first_name VARCHAR, last_name VARCHAR, email VARCHAR, role VARCHAR, country VARCHAR, is_active BOOL, hired_at DATE

Customers (PK: customer_id, UNIQUE: email) customer_id INT, first_name VARCHAR, last_name VARCHAR, full_name VARCHAR (generated: first_name || ' ' || last_name), email VARCHAR, phone VARCHAR, preferred_store_id INT → Stores(store_id), date_of_birth DATE, gender VARCHAR, registration_date TIMESTAMP

Products (PK: id) id INT, product_name VARCHAR, description TEXT, category VARCHAR, subcategory VARCHAR, brand VARCHAR, gender_target VARCHAR, sustainability_score NUMERIC, season VARCHAR, price NUMERIC, is_active BOOL

Inventory (PK: inventory_id, UNIQUE: store_id+product_id+size+color) inventory_id INT, store_id INT → Stores(store_id), product_id INT → Products(id), size VARCHAR, color VARCHAR, quantity_on_hand INT, quantity_reserved INT, reorder_level INT, reorder_quantity INT, last_restocked_at TIMESTAMP

Note: there is NO quantity_available column in Inventory. Use the InventoryAvailable view or compute quantity_on_hand - quantity_reserved.

Orders (PK: id) id INT, customer_id INT → Customers(customer_id), store_id INT → Stores(store_id), employee_id INT → Employees(employee_id), order_channel VARCHAR, status VARCHAR, subtotal NUMERIC, discount_amount NUMERIC, total_amount NUMERIC, order_date TIMESTAMP

OrderItems (PK: order_item_id) order_item_id INT, order_id INT → Orders(id), inventory_id INT → Inventory(inventory_id), product_id INT → Products(id), promotion_id INT → Promotions(promotion_id), quantity INT, unit_price NUMERIC, discount_amount NUMERIC, total_item_price NUMERIC

Promotions (PK: promotion_id) promotion_id INT, name VARCHAR, description TEXT, discount_type VARCHAR, discount_value NUMERIC, buy_x_quantity INT, get_y_quantity INT, min_purchase_amount NUMERIC, applicable_to VARCHAR, applicable_value VARCHAR, store_id INT → Stores(store_id), start_date DATE, end_date DATE, is_active BOOL, created_by INT → Employees(employee_id)

InventoryTransfers (PK: transfer_id) transfer_id INT, from_store_id INT → Stores(store_id), to_store_id INT → Stores(store_id), product_id INT → Products(id), size VARCHAR, color VARCHAR, quantity INT, requested_by INT → Employees(employee_id), approved_by INT → Employees(employee_id), status VARCHAR, notes TEXT, requested_at TIMESTAMP, completed_at TIMESTAMP

ReorderRequests (PK: reorder_id) reorder_id INT, store_id INT → Stores(store_id), product_id INT → Products(id), size VARCHAR, color VARCHAR, requested_quantity INT, triggered_by VARCHAR(20) NOT NULL — CHECK: must be 'manual' or 'automatic' (no other values accepted — NOT a Telegram ID or integer), requested_by INT → Employees(employee_id), status VARCHAR NOT NULL DEFAULT 'pending' — CHECK: one of 'pending', 'approved', 'ordered', 'received', 'cancelled', supplier_reference VARCHAR, expected_delivery DATE, requested_at TIMESTAMP, received_at TIMESTAMP

Views (pre-computed, prefer these for common queries)

InventoryAvailable — stock levels with computed availability All Inventory columns plus: store_name, city, country, product_name, category, brand, season, quantity_available (= on_hand - reserved), needs_reorder BOOL, selling_price

LowStockAlerts — items at or below reorder threshold store_id, store_name, product_id, product_name, category, brand, size, color, quantity_on_hand, quantity_reserved, quantity_available, reorder_level, suggested_reorder_qty, needs_reorder

SalesPerformanceByStore — monthly revenue per store country, region, store_id, store_name, sales_month, total_orders, unique_customers, revenue, total_discounts, avg_order_value

TopProductsByRevenue — top-selling products country, product_id, product_name, category, brand, season, units_sold, revenue

Customer360 — customer profile with purchase summary customer_id, full_name, email, phone, gender, date_of_birth, registration_date, preferred_store, total_orders, lifetime_value, last_order_date, avg_order_value

ActivePromotions — currently active promotions (filtered by date + is_active) promotion_id, name, description, discount_type, discount_value, buy_x_quantity, get_y_quantity, min_purchase_amount, applicable_to, applicable_value, applicable_store (VARCHAR — store NAME, e.g., 'NemoClaw Barcelona', 'All Stores'; NOT a store_id), start_date, end_date ⚠ applicable_store is a store name string, not an integer. To get promotions for a store: WHERE applicable_store = 'NemoClaw Barcelona' OR applicable_store = 'All Stores'

Tips to avoid common mistakes

  • Stores has name, NOT store_name. Use s.name with alias: s.name AS store_name.
  • Inventory has NO quantity_available. Use the InventoryAvailable view or quantity_on_hand - quantity_reserved.
  • NEVER prefix columns with table aliases when querying a view. Views are flat — SELECT product_name FROM LowStockAlerts, NOT SELECT p.product_name FROM LowStockAlerts.
  • Orders date column is order_date, NOT purchase_date, date, or order_time.
  • ReorderRequests triggered_by must be exactly 'manual' or 'automatic'. It is NOT a Telegram ID, user ID, or integer.
  • Use transfer / reorder CLI commands for write operations — do NOT use raw SQL INSERT.
  • ActivePromotions view: applicable_store is a store NAME string (e.g., 'NemoClaw Barcelona', 'All Stores'), NOT a store_id integer.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.