agentpulse

📁 dang-hai/agentpulse 📅 11 days ago
1
总安装量
1
周安装量
#49198
全站排名
安装命令
npx skills add https://github.com/dang-hai/agentpulse --skill agentpulse

Agent 安装分布

codex 1
claude-code 1

Skill 文档

AgentPulse Component Exposure

Help users write effective useExpose hooks to make React components controllable by AI agents.

Core Principle: Expose Workflows, Not Just State

AI agents should interact with UI the same way humans do. This means:

  1. Visibility first – A human can’t fill a form that’s hidden. Neither should an AI. Expose actions to make elements visible (openModal, expandSection, showForm) before the elements inside them.

  2. Workflow-oriented – Structure exposures around user workflows, not implementation details:

    • Create project → Configure settings → Add team members
    • Open form → Fill fields → Submit
    • Expand section → Make changes → Collapse
  3. Sequential visibility – If a UI requires steps (wizard, nested panels, accordions), expose the navigation actions so the AI follows the same path a human would.

Example: Form in Modal

// ❌ Bad: Exposes form fields but no way to show the modal
useExpose('contact-form', {
  name, setName,
  email, setEmail,
  submit,
});

// ✅ Good: Exposes the workflow - open modal, fill fields, submit
useExpose('contact-form', {
  isOpen,
  openForm,      // AI must call this first to show the modal
  closeForm,
  name, setName,
  email, setEmail,
  submit,
}, {
  description: 'Contact form. Call openForm() first to show modal, then fill fields with setName/setEmail, then submit().',
});

Example: Collapsible Section

// ✅ Good: Expose expand/collapse so AI can make content visible
useExpose('advanced-settings', {
  isExpanded,
  expand,        // AI calls this to reveal settings
  collapse,
  // Settings only accessible after expand()
  debugMode, setDebugMode,
  logLevel, setLogLevel,
}, {
  description: 'Advanced settings panel. Call expand() to reveal options, then modify settings, collapse() when done.',
});

Example: Multi-Step Workflow

// ✅ Good: Expose the full workflow for creating a project
useExpose('project-wizard', {
  // Navigation state
  currentStep,     // 'details' | 'team' | 'settings' | 'review'

  // Step transitions (AI follows same flow as human)
  goToDetails,
  goToTeam,
  goToSettings,
  goToReview,

  // Per-step actions
  setProjectName,
  addTeamMember,
  removeTeamMember,
  toggleSetting,

  // Final action
  createProject,
}, {
  description: 'Project creation wizard. Navigate: goToDetails() → goToTeam() → goToSettings() → goToReview(). Fill each step before advancing. Call createProject() on review step.',
});

Quick Reference

import { useExpose } from 'agentpulse';

useExpose('component-id', {
  // State (read-only) - AI can read
  value,
  items,
  isLoading,

  // Setters (read-write) - AI can modify (auto-detected by setXxx naming)
  setValue,
  setFilter,

  // Actions (callable) - AI can call
  submit,
  refresh,
  clear,
}, {
  description: 'What it is. How to use it. What to expect.',
  tags: ['category'],
});

What to Expose

Expose components users interact with. Always include visibility controls when elements can be hidden.

Component Type Visibility Controls Content/Actions Example ID
Forms in modals isOpen, openForm, closeForm fields, setters, submit, errors contact-form
Collapsible sections isExpanded, expand, collapse settings, setters advanced-settings
Tabs/Panels activeTab, setActiveTab per-tab content settings-tabs
Wizards currentStep, goToStep, next, prev per-step fields, submit signup-wizard
Drawers/Sidebars isOpen, open, close content inside filter-drawer
Dropdowns isOpen, open, close options, select user-menu
Lists (usually visible) items, add, remove, toggle todo-list
Inline inputs (usually visible) value, setValue, clear search-input

Don’t expose: Layout components, pure displays with no interaction, internal implementation details.

Key rule: If a human must click to reveal something before interacting with it, the AI must do the same.

Writing Good Descriptions

Descriptions tell the AI how to use your component. They’re critical for workflow ordering.

Formula: [What it is]. [Visibility step if needed]. [How to use it]. [What to expect/check].

Bad:

  • "User form" – What can AI do?
  • "Handles input" – Too vague
  • "Set email and password" – Missing visibility step if form is in modal

Good (with visibility steps):

  • "Contact form in modal. Call openForm() first, then fill name/email/message, then submit(). Check errors after."
  • "Settings panel. Call expand() to reveal options, modify settings, collapse() when done."
  • "Project wizard. Navigate goToDetails() → goToTeam() → goToSettings(). Fill each step, then createProject()."

Good (always visible):

  • "Search box. Use setQuery(text), then search(). Check loading, read results when done."
  • "Todo list. Use add(text) to create, toggle(id) to complete, remove(id) to delete."

Common Patterns

Form in Modal (visibility-first)

useExpose('contact-form', {
  // Visibility - AI must call openForm() first
  isOpen,
  openForm,
  closeForm,

  // Fields - only interact after form is visible
  name, setName,
  email, setEmail,
  message, setMessage,

  // Validation
  errors,
  isValid,

  // Actions
  submit,
  reset,
}, {
  description: 'Contact form in modal. Call openForm() first, fill name/email/message, check isValid, then submit(). closeForm() to dismiss.',
});

Collapsible Settings

useExpose('advanced-options', {
  // Visibility - AI must expand first
  isExpanded,
  expand,
  collapse,

  // Settings - only interact when expanded
  debugMode, setDebugMode,
  verboseLogging, setVerboseLogging,
  cacheEnabled, setCacheEnabled,
}, {
  description: 'Advanced options (collapsed by default). Call expand() to reveal, modify settings, collapse() when done.',
});

Inline Input (always visible)

useExpose('search-input', {
  value,
  setValue,
  clear: () => setValue(''),
  submit: () => onSearch(value),
}, {
  description: 'Search input. Use setValue(text), then submit() to search. clear() resets.',
});

List with Add Form

useExpose('todo-list', {
  // Add form visibility
  showAddForm,
  hideAddForm,
  isAddFormVisible,

  // Add form fields
  newItemText, setNewItemText,
  addItem: () => { addTodo(newItemText); hideAddForm(); },

  // List operations (always visible)
  items,
  count: items.length,
  toggle: (id) => toggleItem(id),
  remove: (id) => removeItem(id),
}, {
  description: 'Todo list. To add: showAddForm(), setNewItemText(text), addItem(). List: toggle(id) to check, remove(id) to delete.',
});

Tabbed Interface

useExpose('settings-tabs', {
  // Tab navigation
  tabs: ['profile', 'security', 'notifications'],
  activeTab,
  setActiveTab,

  // Profile tab fields (visible when activeTab === 'profile')
  displayName, setDisplayName,
  bio, setBio,

  // Security tab fields (visible when activeTab === 'security')
  changePassword,
  enable2FA,

  // Notifications tab fields
  emailNotifs, setEmailNotifs,

  // Save applies to current tab
  save,
}, {
  description: 'Settings tabs: profile, security, notifications. Use setActiveTab(name) to switch, modify fields for that tab, then save().',
});

Confirmation Dialog

useExpose('confirm-dialog', {
  isOpen,
  title,
  message,
  open: (title, message) => showDialog(title, message),
  confirm: () => { onConfirm(); close(); },
  cancel: () => { onCancel(); close(); },
}, {
  description: 'Confirmation dialog. Use open(title, message) to show. Respond with confirm() or cancel().',
});

Binding Types

AgentPulse auto-detects types:

Pattern Type AI Access
value (primitive/object) Value Read
setValue (function named set*) Setter Read + Write
submit (other function) Action Call
{ get, set } Accessor Read + Write

Tags for Organization

useExpose('login-form', bindings, { tags: ['auth', 'form'] });
useExpose('profile-form', bindings, { tags: ['auth', 'form'] });

AI can filter: discover({ tag: 'auth' })

Scroll Bindings

For scrollable containers, use createScrollBindings to add scroll control:

import { useExpose, createScrollBindings } from 'agentpulse';

function MessageList({ messages }) {
  const listRef = useRef<HTMLUListElement>(null);

  useExpose('message-list', {
    messages,
    count: messages.length,
    ...createScrollBindings(listRef),
  }, {
    description: 'Message list. Read messages array. Scroll: scrollToTop(), scrollToBottom(), scrollBy(delta).',
  });

  return <ul ref={listRef}>{/* ... */}</ul>;
}

Exposed bindings:

  • scrollTop (read/write) – Current scroll position
  • scrollHeight (read-only) – Total scrollable height
  • clientHeight (read-only) – Visible height
  • scrollToTop() – Scroll to top
  • scrollToBottom() – Scroll to bottom
  • scrollTo(position) – Scroll to specific position
  • scrollBy(delta) – Scroll by relative amount

Options:

createScrollBindings(ref, { behavior: 'smooth' }); // default
createScrollBindings(ref, { behavior: 'auto' });   // instant

Multiple Instances

For components that render multiple times (list items), use useExposeId:

import { useExpose, useExposeId } from 'agentpulse';

function TodoItem({ item }) {
  const exposeId = useExposeId('todo-item');

  useExpose(exposeId, {
    text: item.text,
    completed: item.completed,
    toggle: () => toggleItem(item.id),
  });

  return <li>{/* ... */}</li>;
}

Generates IDs like todo-item:r1a2b3, unique per component instance.

Alternative: Use item ID directly:

useExpose(`todo-item:${item.id}`, { ... });

Non-React Usage

For exposing state outside React components (services, modules), use expose:

import { expose } from 'agentpulse';

// In a service
const unregister = expose('api-client', {
  isConnected: { get: () => client.connected, set: () => {} },
  reconnect: () => client.reconnect(),
}, {
  description: 'API client. Check isConnected, call reconnect() if needed.',
});

// Cleanup when done
unregister();

Connection Status

Check AgentPulse connection status with useAgentPulse:

import { useAgentPulse } from 'agentpulse';

function StatusIndicator() {
  const { isConnected } = useAgentPulse();

  return (
    <div className={isConnected ? 'connected' : 'disconnected'}>
      {isConnected ? 'AI Connected' : 'Offline'}
    </div>
  );
}

Error Handling

Handle registration errors with onRegistrationError:

useExpose('critical-form', bindings, {
  description: '...',
  onRegistrationError: (error) => {
    console.error('Failed to register with server:', error);
    // Component still works locally, just not remotely controllable
  },
});

Visual Overlay Integration

For visual animations (cursor, typing effects), add data-agentpulse-id attributes to elements:

useExpose('contact-form', {
  setName: (v) => setName(v),
  setEmail: (v) => setEmail(v),
  submitForm: () => handleSubmit(),
});

// Add data attributes for visual targeting
<input data-agentpulse-id="contact-form-name" />
<input data-agentpulse-id="contact-form-email" />
<button data-agentpulse-id="contact-form-submitform">Submit</button>

Format: data-agentpulse-id="componentId-normalizedKey"

Where normalizedKey = binding key with set prefix removed, lowercased.

See the visual-overlay skill for full targeting documentation.

Process for Exposing a Component

  1. Identify the workflow – What task does the user accomplish? (create project, submit form, configure settings)
  2. Map the visibility steps – What must be clicked/opened before interacting? (modal, accordion, tab, drawer)
  3. List user actions in order – First open/expand, then fill/modify, then submit/save, then close/collapse
  4. Map to bindings – Visibility controls first, then state, then actions
  5. Write description with workflow – Include the sequence: “Call X first, then Y, then Z”
  6. Add useExpose – Import hook, add call inside component
  7. Test the workflow – Verify AI can complete the full task, not just individual actions

More Patterns

See references/EXPOSE_PATTERNS.md for:

  • Multi-step forms/wizards
  • Filtered/sorted/paginated lists
  • Navigation patterns
  • State management integration (Redux/Zustand)
  • Testing exposed components