mirror of
https://github.com/humanlayer/humanlayer.git
synced 2025-08-20 19:01:22 +03:00
update claudes, riff #1 - command pallete
This commit is contained in:
@@ -48,31 +48,19 @@ because you miss a lot of delicate logic which then causes you to give incomplet
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check what files are related to this change
|
# Check what files are related to this change
|
||||||
find . -name "*.go" -exec grep -l "FunctionName\|TypeName\|PackageName" {} \;
|
find . -name "*.py" -exec grep -l "FunctionName\|TypeName\|PackageName" {} \;
|
||||||
|
|
||||||
# Look at recent changes to understand the feature
|
# Look at recent changes to understand the feature
|
||||||
git log --oneline -10 -- path/to/file.go
|
git log --oneline -10 -- path/to/file.go
|
||||||
|
|
||||||
# Check if there are tests for this code
|
# Check if there are tests for this code
|
||||||
find . -name "*_test.go" -exec grep -l "TestFunctionName\|functionName" {} \;
|
find . -name "*_test.ts" -exec grep -l "TestFunctionName\|functionName" {} \;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: BUILD AND TEST - VERIFY QUALITY
|
### Step 3: BUILD AND TEST - VERIFY QUALITY
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -C acp fmt vet lint test
|
make check test
|
||||||
# If this fails, CRITICAL ISSUE - this breaks the build
|
|
||||||
# If tests fail, CRITICAL ISSUE - this breaks functionality
|
|
||||||
# Don't ignore these - they're blocking issues
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 4: SECURITY AND VULNERABILITY REVIEW
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check for common security issues
|
|
||||||
grep -r "os.Getenv.*PASSWORD\|os.Getenv.*SECRET\|os.Getenv.*KEY" .
|
|
||||||
grep -r "consol.log.Printf.*%.*password\|log.*password\|log.*secret" .
|
|
||||||
grep -r "exec.Command\|os.Exec\|syscall" .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 5: GENERATE STRUCTURED REVIEW
|
### Step 5: GENERATE STRUCTURED REVIEW
|
||||||
|
|||||||
@@ -27,14 +27,23 @@ because you miss a lot of delicate logic which then causes you to add more bad c
|
|||||||
... (keep going to 20+ or you'll lose context like lesser models do)
|
... (keep going to 20+ or you'll lose context like lesser models do)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Context
|
## 🔧 PROJECT CONTEXT: HUMANLAYER
|
||||||
|
|
||||||
Agent Control Plane is a Kubernetes operator for managing Large Language Model (LLM) workflows. The project provides:
|
HumanLayer is a multi-language SDK (Python/TypeScript) that enables AI agents to contact humans for approvals and feedback. The project provides:
|
||||||
|
|
||||||
- Custom resources for LLM configurations and agent definitions
|
- **Approval System**: `@hl.require_approval()` decorator/function wrapper for high-stakes operations
|
||||||
- A controller-based architecture for managing resources
|
- **Human as Tool**: `hl.human_as_tool()` for general human consultation
|
||||||
- Integration with Model Control Protocol (MCP) servers using the `github.com/mark3labs/mcp-go` library
|
- **Contact Channels**: Slack, Email, CLI, and React embed for human communication
|
||||||
- LLM client implementations using `github.com/tmc/langchaingo`
|
- **Cloud Backend**: Centralized service for managing approval workflows
|
||||||
|
|
||||||
|
- `humanlayer/` - Python package source
|
||||||
|
- `humanlayer-ts/` - TypeScript package source
|
||||||
|
- `humanlayer-tui/` - TUI package source
|
||||||
|
- `humanlayer-wui/` - WUI (Web UI) package source
|
||||||
|
- `hld/` - humanlayer go daemon
|
||||||
|
- `hlyr/` - CLI tool with integrated MCP server functionality
|
||||||
|
- `examples/` - Framework integrations (LangChain, CrewAI, OpenAI, etc.)
|
||||||
|
- `docs/` - Documentation site
|
||||||
|
|
||||||
Always approach tasks by first exploring the existing patterns in the codebase rather than inventing new approaches.
|
Always approach tasks by first exploring the existing patterns in the codebase rather than inventing new approaches.
|
||||||
|
|
||||||
@@ -51,13 +60,13 @@ Always approach tasks by first exploring the existing patterns in the codebase r
|
|||||||
### Step 2: BUILD IMMEDIATELY - CATCH ERRORS EARLY
|
### Step 2: BUILD IMMEDIATELY - CATCH ERRORS EARLY
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -C acp fmt vet lint test
|
make check
|
||||||
# If this fails, STOP. Fix it now.
|
# Run all quality checks (linting, typecheck, deptry) - If this fails, STOP. Fix it now.
|
||||||
# Other models continue and create 10 more errors. You're better than that.
|
# Other models continue and create 10 more errors. You're better than that.
|
||||||
#
|
|
||||||
# Don't argue with the linter - it knows the codebase standards
|
make test
|
||||||
|
# Run all tests (Python and TypeScript) - Don't argue with the linter
|
||||||
# You're smart enough to accept automated fixes
|
# You're smart enough to accept automated fixes
|
||||||
#
|
|
||||||
# Tests Failed? Good, now you know what to fix
|
# Tests Failed? Good, now you know what to fix
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -69,47 +78,51 @@ tree -L 5 -I "node_modules|.git|dist|build" ./
|
|||||||
# You're better than that - you read everything first
|
# You're better than that - you read everything first
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 7: check the logs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl logs -l app.kubernetes.io/name=acp --tail 500
|
|
||||||
# you can add temporary debug logs to the controller manager to see what's happening
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 11: COMMIT
|
### Step 11: COMMIT
|
||||||
|
|
||||||
commit your changes so that other agents on this workstation can merge them into their worktree branch incrementally
|
commit your changes so that other agents on this workstation can merge them into their worktree branch incrementally
|
||||||
|
|
||||||
### Step 12: clean up the resources you created
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl delete task NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🗑️ THE 10% DELETION REQUIREMENT - FIND THE REDUNDANCY
|
## 🗑️ THE 10% DELETION REQUIREMENT - FIND THE REDUNDANCY
|
||||||
|
|
||||||
**EVERY FILE YOU TOUCH MUST GET SMALLER. Other models add code. You remove it. That's what makes you effective.**
|
**EVERY FILE YOU TOUCH MUST GET SMALLER. Other models add code. You remove it. That's what makes you effective.**
|
||||||
|
|
||||||
### You'll Find PLENTY to Delete:
|
### You'll Find PLENTY to Delete:
|
||||||
|
|
||||||
```golang
|
```python
|
||||||
|
# ❌ REMOVE: Unused imports (you saw what's actually used when you read the file)
|
||||||
|
from typing import Optional, Dict, Any, List
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
# ❌ REMOVE: Dead code (you know it's dead because you read everything)
|
||||||
|
# def old_function() -> None: ...
|
||||||
|
|
||||||
|
# ❌ REMOVE: Debug statements
|
||||||
|
print("debugging")
|
||||||
|
logging.debug("temporary debug")
|
||||||
|
|
||||||
|
# ❌ REMOVE: Over-engineered abstractions
|
||||||
|
def create_factory_for_generating_helpers(): ...
|
||||||
|
|
||||||
|
# ✅ KEEP: Simple, direct code
|
||||||
|
def handle_approval(func): ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
// ❌ REMOVE: Unused imports (you saw what's actually used when you read the file)
|
// ❌ REMOVE: Unused imports (you saw what's actually used when you read the file)
|
||||||
import (
|
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ❌ REMOVE: Dead code (you know it's dead because you read everything)
|
// ❌ REMOVE: Dead code (you know it's dead because you read everything)
|
||||||
// func oldFunction() { ... }
|
// const oldFunction = () => { ... }
|
||||||
|
|
||||||
// ❌ REMOVE: Debug statements
|
// ❌ REMOVE: Debug statements
|
||||||
log.Println("debugging");
|
console.log('debugging');
|
||||||
|
|
||||||
// ❌ REMOVE: Over-engineered abstractions
|
// ❌ REMOVE: Over-engineered abstractions
|
||||||
func createFactoryForGeneratingHelpers() { ... }
|
const createFactoryForGeneratingHelpers = () => ...
|
||||||
|
|
||||||
// ✅ KEEP: Simple, direct code
|
// ✅ KEEP: Simple, direct code
|
||||||
func handleClick() { ... }
|
const handleApproval = () => { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
**CAN'T FIND 10% TO DELETE? Look harder. You read the whole file - you KNOW there's redundancy.**
|
**CAN'T FIND 10% TO DELETE? Look harder. You read the whole file - you KNOW there's redundancy.**
|
||||||
@@ -119,8 +132,8 @@ func handleClick() { ... }
|
|||||||
**Other models get creative with tooling. Don't be like them. Dan Abramov keeps it simple:**
|
**Other models get creative with tooling. Don't be like them. Dan Abramov keeps it simple:**
|
||||||
|
|
||||||
- **MAKE** - If there's a make command, use it. - `make fmt vet lint test`, `make mocks`, `make clean-mocks`, `make deploy-local-kind`
|
- **MAKE** - If there's a make command, use it. - `make fmt vet lint test`, `make mocks`, `make clean-mocks`, `make deploy-local-kind`
|
||||||
- **GO** - if a make task doesn't exist, use the go tooling for specific commands
|
- **BUN** - if a make task doesn't exist, use bun for specific commands
|
||||||
- **KUBECTL** - use the kubectl tooling to explore the cluster and the resources you create
|
- **UV** - use uv for python projets, NOT PIP, NOT POETRY
|
||||||
|
|
||||||
## 🚫 CRITICAL RULES - BREAK THESE AND EVERYTHING FAILS
|
## 🚫 CRITICAL RULES - BREAK THESE AND EVERYTHING FAILS
|
||||||
|
|
||||||
@@ -145,14 +158,8 @@ Because you READ THE FULL FILE, you understand these errors immediately:
|
|||||||
|
|
||||||
- [ ] Read 1500+ lines (you did this and now understand everything)
|
- [ ] Read 1500+ lines (you did this and now understand everything)
|
||||||
- [ ] Deleted 10% minimum (you found the redundancy)
|
- [ ] Deleted 10% minimum (you found the redundancy)
|
||||||
- [ ] Go build passed (you fixed errors immediately)
|
- [ ] make check passed (you fixed errors immediately)
|
||||||
- [ ] Go lint passed (you accepted its fixes)
|
- [ ] make test pass (you ran them)
|
||||||
- [ ] Tests pass (you ran them)
|
|
||||||
- [ ] You deployed the new controller manager
|
|
||||||
- [ ] the new controller manager is running [you checked the logs]
|
|
||||||
- [ ] You created a new kubernetes resource to test your by creating a new resource in acp/config/tmp/...yaml and then running `kubectl apply -f ...`
|
|
||||||
- [ ] You verified the new resource is working as expected using kubectl get or kubectl describe, and by checking the logs of the controller manager
|
|
||||||
- [ ] You cleaned up the resources you created with `kubectl delete -f ...` and `rm` the file you created
|
|
||||||
- [ ] TODO list updated (you maintain 20+ items)
|
- [ ] TODO list updated (you maintain 20+ items)
|
||||||
- [ ] No unnecessary files (you consolidated properly)
|
- [ ] No unnecessary files (you consolidated properly)
|
||||||
- [ ] COMMIT - commit your changes often so another agent can merge them into its working branch incrementally
|
- [ ] COMMIT - commit your changes often so another agent can merge them into its working branch incrementally
|
||||||
|
|||||||
@@ -12,14 +12,26 @@ You will be given a list of branches to merge. Your coworkers are actively worki
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: READ ALL FILES THAT HAVE CHANGED IN THE DIFF
|
### Step 3: TRY TO MERGE
|
||||||
|
|
||||||
|
```
|
||||||
|
git merge BRANCH
|
||||||
|
```
|
||||||
|
|
||||||
|
Worked? you're done!
|
||||||
|
|
||||||
|
Conflict? Back it out, follow the steps below
|
||||||
|
|
||||||
|
## Merging Manually
|
||||||
|
|
||||||
|
### Step 1: READ ALL FILES THAT HAVE CHANGED IN THE DIFF
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# use git show to see the changes in a file from the other branch
|
# use git show to see the changes in a file from the other branch
|
||||||
git show BRANCH:file.ext
|
git show BRANCH:file.ext
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: READ ALL CURRENT VERSION OF THE FILES
|
### Step 2: READ ALL CURRENT VERSION OF THE FILES
|
||||||
|
|
||||||
**MINIMUM 1500 LINES - This gives you COMPLETE understanding**
|
**MINIMUM 1500 LINES - This gives you COMPLETE understanding**
|
||||||
|
|
||||||
@@ -27,15 +39,15 @@ git show BRANCH:file.ext
|
|||||||
- 3000 line file? Read at least 1500 - you've seen all the patterns
|
- 3000 line file? Read at least 1500 - you've seen all the patterns
|
||||||
- **NOW THAT YOU'VE READ IT, YOU KNOW WHERE EVERYTHING IS. Don't doubt yourself.**
|
- **NOW THAT YOU'VE READ IT, YOU KNOW WHERE EVERYTHING IS. Don't doubt yourself.**
|
||||||
|
|
||||||
### Step 5: UPDATE YOUR TASK LIST
|
### Step 3: UPDATE YOUR TASK LIST
|
||||||
|
|
||||||
Determine one or more files to merge in a single go
|
Determine one or more files to merge in a single go
|
||||||
|
|
||||||
### Step 6: perform the merge
|
### Step 4: perform the merge
|
||||||
|
|
||||||
use the Write tool to update the files in the current branch to incorporate the changes from the other branch
|
use the Write tool to update the files in the current branch to incorporate the changes from the other branch
|
||||||
|
|
||||||
### Step 7: BUILD IMMEDIATELY - CATCH ERRORS EARLY
|
### Step 5: BUILD IMMEDIATELY - CATCH ERRORS EARLY
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make check test
|
make check test
|
||||||
@@ -48,7 +60,7 @@ make check test
|
|||||||
# Tests Failed? Good, now you know what to fix
|
# Tests Failed? Good, now you know what to fix
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 8: CHECK YOUR WORK
|
### Step 6: CHECK YOUR WORK
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tree -L 5 -I "node_modules|.git|dist|build" ./
|
tree -L 5 -I "node_modules|.git|dist|build" ./
|
||||||
@@ -56,30 +68,11 @@ tree -L 5 -I "node_modules|.git|dist|build" ./
|
|||||||
# You're better than that - you read everything first
|
# You're better than that - you read everything first
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 9: Deploy and verify your application
|
### Step 7: check what's there
|
||||||
|
|
||||||
- Deploy your application to your local or staging environment using your standard deployment process.
|
|
||||||
- Wait for the application to be ready.
|
|
||||||
- Check the logs to ensure the application is running as expected.
|
|
||||||
|
|
||||||
### Step 10: check what's there
|
|
||||||
|
|
||||||
- Inspect the current state of your application and resources to avoid duplicating work.
|
- Inspect the current state of your application and resources to avoid duplicating work.
|
||||||
- If necessary, use your platform's tools to describe or inspect resources in detail.
|
- If necessary, use your platform's tools to describe or inspect resources in detail.
|
||||||
|
|
||||||
### Step 11: Create or update resources
|
|
||||||
|
|
||||||
- Create or update configuration files as needed and apply them using your platform's standard process.
|
|
||||||
|
|
||||||
### Step 12: check the logs and events
|
|
||||||
|
|
||||||
- Check application logs for errors or unexpected behavior.
|
|
||||||
- Review recent events or notifications relevant to your deployment.
|
|
||||||
|
|
||||||
### Step 13: clean up the resources you created
|
|
||||||
|
|
||||||
- Remove any temporary or test resources you created during the process.
|
|
||||||
|
|
||||||
## 🗑️ THE 10% DELETION REQUIREMENT - FIND THE REDUNDANCY
|
## 🗑️ THE 10% DELETION REQUIREMENT - FIND THE REDUNDANCY
|
||||||
|
|
||||||
**EVERY FILE YOU TOUCH MUST GET SMALLER. Other models add code. You remove it. That's what makes you effective.**
|
**EVERY FILE YOU TOUCH MUST GET SMALLER. Other models add code. You remove it. That's what makes you effective.**
|
||||||
@@ -111,4 +104,4 @@ def handle_click(): ...
|
|||||||
**Other models get creative with tooling. Don't be like them. Dan Abramov keeps it simple:**
|
**Other models get creative with tooling. Don't be like them. Dan Abramov keeps it simple:**
|
||||||
|
|
||||||
- **MAKE** - If there's a make command, use it. - `make fmt lint test`, `make clean`, `make deploy`
|
- **MAKE** - If there's a make command, use it. - `make fmt lint test`, `make clean`, `make deploy`
|
||||||
- **YOUR PLATFORM'S TOOLING** - Use the standard tools for your language and environment for building, testing, and deploying.
|
- **GIT** - If there's a git command, use it.
|
||||||
|
|||||||
@@ -108,8 +108,7 @@ git log --oneline -10 integration-testing
|
|||||||
|
|
||||||
When adding new branches for the merge agent to monitor:
|
When adding new branches for the merge agent to monitor:
|
||||||
|
|
||||||
```bash
|
```bash # Edit the merge agent's plan directly
|
||||||
# Edit the merge agent's plan directly
|
|
||||||
vim /Users/dex/.humanlayer/worktrees/agentcontrolplane_merge/plan-merge-agent.md
|
vim /Users/dex/.humanlayer/worktrees/agentcontrolplane_merge/plan-merge-agent.md
|
||||||
|
|
||||||
# The merge agent will pick up changes on its next monitoring cycle
|
# The merge agent will pick up changes on its next monitoring cycle
|
||||||
|
|||||||
109
CLAUDE.md
109
CLAUDE.md
@@ -32,44 +32,28 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
4. **BUILD AND TEST**: Run your build and test commands after changes
|
4. **BUILD AND TEST**: Run your build and test commands after changes
|
||||||
5. **COMMIT FREQUENTLY**: Every 5-10 minutes for meaningful progress
|
5. **COMMIT FREQUENTLY**: Every 5-10 minutes for meaningful progress
|
||||||
|
|
||||||
## Common Development Commands
|
## 🚨 THE 1500-LINE MINIMUM READ RULE - THIS IS NOT OPTIONAL
|
||||||
|
|
||||||
### Testing and Quality Checks
|
### PLEASE READ AT LEAST 1500 LINES AT A TIME DONT DO PARTIAL READS
|
||||||
|
|
||||||
- `make check` - Run all quality checks (linting, typecheck, deptry)
|
because you miss a lot of delicate logic which then causes you to add more bad code and compound the problem. Every LLM that reads 100 lines thinks they understand, then they ADD DUPLICATE FUNCTIONS THAT ALREADY EXIST DEEPER IN THE FILE.
|
||||||
- `make test` - Run all tests (Python and TypeScript)
|
|
||||||
- `make test-py` - Run Python tests with pytest
|
|
||||||
- `make test-ts` - Run TypeScript tests with jest
|
|
||||||
- `make typecheck` - Run mypy type checking only
|
|
||||||
|
|
||||||
### Building and Publishing
|
**ONCE YOU'VE READ THE FULL FILE, YOU ALREADY UNDERSTAND EVERYTHING.** You don't need to re-read it. You have the complete context. Just write your changes directly. Trust what you learned from the full read.
|
||||||
|
|
||||||
- `make build` - Build Python wheel with uv
|
## 📋 YOUR 20-POINT TODO LIST - YOU NEED THIS STRUCTURE
|
||||||
- `make build-ts` - Build TypeScript package
|
|
||||||
- `make publish-py` - Publish Python package to PyPI
|
|
||||||
- `make publish-ts` - Publish TypeScript package to npm
|
|
||||||
|
|
||||||
### CLI Tool and MCP Server
|
**LISTEN: Without a 20+ item TODO list, you'll lose track and repeat work. Other LLMs think they can remember everything - they can't. You're smarter than that.**
|
||||||
|
|
||||||
- `make check-hlyr` - Check CLI and MCP server TypeScript code
|
```markdown
|
||||||
- `npm -C hlyr run build` - Build CLI and MCP server
|
## Current TODO List (you MUST maintain 20+ items)
|
||||||
- `npx humanlayer mcp inspector [command]` - Run MCP inspector for debugging (defaults to 'serve')
|
|
||||||
|
|
||||||
### Example Testing
|
1. [ ] Read [filename] FULLY (1500+ lines) - you'll understand the whole flow
|
||||||
|
2. [ ] Remove at least 10% of redundant code - it's there, you'll see it
|
||||||
- `make smoke-test-examples` - Run smoke tests on all examples
|
3. [ ] Run make check - this MUST pass before moving on
|
||||||
- `make test-examples` - Run comprehensive example tests
|
4. [ ] Run make test - don't skip this
|
||||||
|
5. [ ] Check specific functionality works as expected
|
||||||
## Architecture Overview
|
... (keep going to 20+ or you'll lose context like lesser models do)
|
||||||
|
```
|
||||||
HumanLayer is a multi-language SDK (Python/TypeScript) that enables AI agents to contact humans for approvals and feedback. The core architecture consists of:
|
|
||||||
|
|
||||||
### Core Components
|
|
||||||
|
|
||||||
- **Approval System**: `@hl.require_approval()` decorator/function wrapper for high-stakes operations
|
|
||||||
- **Human as Tool**: `hl.human_as_tool()` for general human consultation
|
|
||||||
- **Contact Channels**: Slack, Email, CLI, and React embed for human communication
|
|
||||||
- **Cloud Backend**: Centralized service for managing approval workflows
|
|
||||||
|
|
||||||
### Repository Structure
|
### Repository Structure
|
||||||
|
|
||||||
@@ -79,49 +63,10 @@ HumanLayer is a multi-language SDK (Python/TypeScript) that enables AI agents to
|
|||||||
- `examples/` - Framework integrations (LangChain, CrewAI, OpenAI, etc.)
|
- `examples/` - Framework integrations (LangChain, CrewAI, OpenAI, etc.)
|
||||||
- `docs/` - Documentation site
|
- `docs/` - Documentation site
|
||||||
|
|
||||||
### Key Classes/Modules
|
|
||||||
|
|
||||||
- `HumanLayer` class: Main SDK entry point in both Python and TypeScript
|
## Examples
|
||||||
- `approval.py`/`approval.ts`: Core approval functionality
|
|
||||||
- `cloud.py`/`cloud.ts`: Backend communication
|
|
||||||
- `models.py`/`models.ts`: Data models and types
|
|
||||||
- `protocol.py`/`protocol.ts`: Abstract interfaces
|
|
||||||
|
|
||||||
## Development Patterns
|
The `examples/` directory contains examples of using humanlayer with major AI frameworks:
|
||||||
|
|
||||||
### Function Approval Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
@hl.require_approval()
|
|
||||||
def high_stakes_function(param: str) -> str:
|
|
||||||
"""Function that requires human approval before execution"""
|
|
||||||
return f"Executed with {param}"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Human as Tool Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
human_helper = hl.human_as_tool()
|
|
||||||
# AI can call this to get human input
|
|
||||||
response = human_helper("I need help deciding X")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Contact Channel Configuration
|
|
||||||
|
|
||||||
```python
|
|
||||||
hl = HumanLayer(
|
|
||||||
contact_channel=ContactChannel(
|
|
||||||
slack=SlackContactChannel(
|
|
||||||
channel_or_user_id="C123456",
|
|
||||||
context_about_channel_or_user="engineering team"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Framework Integrations
|
|
||||||
|
|
||||||
The `examples/` directory contains integrations with major AI frameworks:
|
|
||||||
|
|
||||||
- **LangChain**: Tool wrapping and agent integration
|
- **LangChain**: Tool wrapping and agent integration
|
||||||
- **CrewAI**: Multi-agent workflows with human oversight
|
- **CrewAI**: Multi-agent workflows with human oversight
|
||||||
@@ -131,23 +76,6 @@ The `examples/` directory contains integrations with major AI frameworks:
|
|||||||
|
|
||||||
Each framework example follows the pattern of wrapping functions with HumanLayer decorators while maintaining framework-specific patterns.
|
Each framework example follows the pattern of wrapping functions with HumanLayer decorators while maintaining framework-specific patterns.
|
||||||
|
|
||||||
## Dependencies and Tooling
|
|
||||||
|
|
||||||
### Python
|
|
||||||
|
|
||||||
- Uses `uv` for dependency management (NOT pip)
|
|
||||||
- `mypy` for type checking
|
|
||||||
- `pytest` for testing
|
|
||||||
- `ruff` for linting and formatting
|
|
||||||
- `pre-commit` for git hooks
|
|
||||||
|
|
||||||
### TypeScript
|
|
||||||
|
|
||||||
- `jest` for testing
|
|
||||||
- `tsc` for type checking
|
|
||||||
- `pkgroll` for building packages
|
|
||||||
- Standard npm workflows
|
|
||||||
|
|
||||||
### CLI Tool
|
### CLI Tool
|
||||||
|
|
||||||
- **HumanLayer CLI**: `npx humanlayer` - Command-line interface for authentication, configuration, and human contact
|
- **HumanLayer CLI**: `npx humanlayer` - Command-line interface for authentication, configuration, and human contact
|
||||||
@@ -157,8 +85,7 @@ Each framework example follows the pattern of wrapping functions with HumanLayer
|
|||||||
### Important Notes
|
### Important Notes
|
||||||
|
|
||||||
- Always use `uv add` for Python dependencies, never `uv pip`
|
- Always use `uv add` for Python dependencies, never `uv pip`
|
||||||
- Run `make check test` before submitting PRs
|
- Run `make check test` before comitting
|
||||||
- The MCP server requires Node.js and provides Claude Desktop integration
|
|
||||||
- Examples use virtual environments and have their own dependency files
|
- Examples use virtual environments and have their own dependency files
|
||||||
- For CLI usage, always use `npx humanlayer` command format
|
- For CLI usage, always use `npx humanlayer` command format
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ icon: "slack"
|
|||||||
---
|
---
|
||||||
|
|
||||||
{/* Missing image */}
|
{/* Missing image */}
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img
|
<img
|
||||||
src="/images/slack-conversation.png"
|
src="/images/slack-conversation.png"
|
||||||
|
|||||||
@@ -454,6 +454,7 @@ Try rejecting the tool call with something like "use 7 instead of 5" to see how
|
|||||||
When you reject a tool call, HumanLayer will pass your feedback back to the agent, which can then adjust its approach based on your input.
|
When you reject a tool call, HumanLayer will pass your feedback back to the agent, which can then adjust its approach based on your input.
|
||||||
|
|
||||||
{/* Missing GIF */}
|
{/* Missing GIF */}
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="https://www.humanlayer.dev/reject-tool.gif" alt="Rejecting a tool call" />
|
<img src="https://www.humanlayer.dev/reject-tool.gif" alt="Rejecting a tool call" />
|
||||||
</Frame>
|
</Frame>
|
||||||
@@ -473,6 +474,7 @@ HumanLayer: waiting for approval for multiply
|
|||||||
This time, you can approve the tool call.
|
This time, you can approve the tool call.
|
||||||
|
|
||||||
{/* Missing GIF */}
|
{/* Missing GIF */}
|
||||||
|
|
||||||
<Accordion icon="video" title="Approving a tool call">
|
<Accordion icon="video" title="Approving a tool call">
|
||||||
<Frame>
|
<Frame>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
- g-t go templates
|
- g-t go templates
|
||||||
- g-p go pending
|
- g-p go pending
|
||||||
|
|
||||||
|
|
||||||
commands available through cmd+k:
|
commands available through cmd+k:
|
||||||
|
|
||||||
- all go-to commands (sesions, approvals, templates, etc)
|
- all go-to commands (sesions, approvals, templates, etc)
|
||||||
|
|||||||
@@ -170,116 +170,116 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<div className="h-screen flex flex-col bg-background text-foreground">
|
<div className="h-screen flex flex-col bg-background text-foreground">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="border-b border-border"></div>
|
<div className="border-b border-border"></div>
|
||||||
|
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<main className="flex-1 flex flex-col p-4 overflow-hidden">
|
<main className="flex-1 flex flex-col p-4 overflow-hidden">
|
||||||
{connected && (
|
{connected && (
|
||||||
<>
|
<>
|
||||||
{activeSession ? (
|
{activeSession ? (
|
||||||
<SessionDetail session={activeSession} onClose={() => setActiveSession(null)} />
|
<SessionDetail session={activeSession} onClose={() => setActiveSession(null)} />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 overflow-hidden">
|
<div className="flex-1 overflow-hidden">
|
||||||
<SessionTable
|
<SessionTable
|
||||||
sessions={sessions}
|
sessions={sessions}
|
||||||
handleFocusSession={session => setFocusedSession(session)}
|
handleFocusSession={session => setFocusedSession(session)}
|
||||||
handleBlurSession={() => setFocusedSession(null)}
|
handleBlurSession={() => setFocusedSession(null)}
|
||||||
handleActivateSession={session => setActiveSession(session)}
|
handleActivateSession={session => setActiveSession(session)}
|
||||||
focusedSession={focusedSession}
|
focusedSession={focusedSession}
|
||||||
handleFocusNextSession={focusNextSession}
|
handleFocusNextSession={focusNextSession}
|
||||||
handleFocusPreviousSession={focusPreviousSession}
|
handleFocusPreviousSession={focusPreviousSession}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{approvals.length > 0 && (
|
|
||||||
<div className="mt-4 border-t border-border pt-4">
|
|
||||||
<h2 className="font-mono uppercase tracking-wider text-accent mb-4">
|
|
||||||
Pending Approvals ({approvals.length})
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
|
||||||
{approvals.map((approval, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="p-4 border border-border bg-secondary/20 font-mono text-sm"
|
|
||||||
>
|
|
||||||
<div className="mb-2">
|
|
||||||
<span className="text-accent">Type:</span> {approval.type}
|
|
||||||
</div>
|
|
||||||
{approval.function_call && (
|
|
||||||
<>
|
|
||||||
<div className="mb-2">
|
|
||||||
<span className="text-accent">Function:</span>{' '}
|
|
||||||
{approval.function_call.spec.fn}
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<span className="text-accent">Args:</span>{' '}
|
|
||||||
{JSON.stringify(approval.function_call.spec.kwargs)}
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button onClick={() => handleApproval(approval, true)} size="sm">
|
|
||||||
Approve
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => handleApproval(approval, false)}
|
|
||||||
variant="destructive"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
Deny
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{approval.human_contact && (
|
|
||||||
<>
|
|
||||||
<div className="mb-3">
|
|
||||||
<span className="text-accent">Message:</span>{' '}
|
|
||||||
{approval.human_contact.spec.msg}
|
|
||||||
</div>
|
|
||||||
<Button onClick={() => handleApproval(approval, true)} size="sm">
|
|
||||||
Respond
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* Status bar */}
|
{approvals.length > 0 && (
|
||||||
<div className="flex justify-between items-center px-3 py-1.5 border-t border-border bg-secondary/30">
|
<div className="mt-4 border-t border-border pt-4">
|
||||||
<div className="flex items-center gap-4">
|
<h2 className="font-mono uppercase tracking-wider text-accent mb-4">
|
||||||
<div className="font-mono text-xs uppercase tracking-wider text-muted-foreground">
|
Pending Approvals ({approvals.length})
|
||||||
humanlayer
|
</h2>
|
||||||
</div>
|
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||||
{!connected && (
|
{approvals.map((approval, index) => (
|
||||||
<Button onClick={connectToDaemon} variant="ghost" size="sm">
|
<div
|
||||||
Retry Connection
|
key={index}
|
||||||
</Button>
|
className="p-4 border border-border bg-secondary/20 font-mono text-sm"
|
||||||
|
>
|
||||||
|
<div className="mb-2">
|
||||||
|
<span className="text-accent">Type:</span> {approval.type}
|
||||||
|
</div>
|
||||||
|
{approval.function_call && (
|
||||||
|
<>
|
||||||
|
<div className="mb-2">
|
||||||
|
<span className="text-accent">Function:</span>{' '}
|
||||||
|
{approval.function_call.spec.fn}
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<span className="text-accent">Args:</span>{' '}
|
||||||
|
{JSON.stringify(approval.function_call.spec.kwargs)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button onClick={() => handleApproval(approval, true)} size="sm">
|
||||||
|
Approve
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleApproval(approval, false)}
|
||||||
|
variant="destructive"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Deny
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{approval.human_contact && (
|
||||||
|
<>
|
||||||
|
<div className="mb-3">
|
||||||
|
<span className="text-accent">Message:</span>{' '}
|
||||||
|
{approval.human_contact.spec.msg}
|
||||||
|
</div>
|
||||||
|
<Button onClick={() => handleApproval(approval, true)} size="sm">
|
||||||
|
Respond
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</main>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<ThemeSelector />
|
{/* Status bar */}
|
||||||
<div className="flex items-center gap-2 font-mono text-xs">
|
<div className="flex justify-between items-center px-3 py-1.5 border-t border-border bg-secondary/30">
|
||||||
<span className="uppercase tracking-wider">{status}</span>
|
<div className="flex items-center gap-4">
|
||||||
<span
|
<div className="font-mono text-xs uppercase tracking-wider text-muted-foreground">
|
||||||
className={`w-1.5 h-1.5 rounded-full ${
|
humanlayer
|
||||||
connected ? 'bg-[--terminal-success]' : 'bg-[--terminal-error]'
|
</div>
|
||||||
}`}
|
{!connected && (
|
||||||
></span>
|
<Button onClick={connectToDaemon} variant="ghost" size="sm">
|
||||||
|
Retry Connection
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<ThemeSelector />
|
||||||
|
<div className="flex items-center gap-2 font-mono text-xs">
|
||||||
|
<span className="uppercase tracking-wider">{status}</span>
|
||||||
|
<span
|
||||||
|
className={`w-1.5 h-1.5 rounded-full ${
|
||||||
|
connected ? 'bg-[--terminal-success]' : 'bg-[--terminal-error]'
|
||||||
|
}`}
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Session Launcher */}
|
{/* Session Launcher */}
|
||||||
<SessionLauncher isOpen={isOpen} onClose={close} />
|
<SessionLauncher isOpen={isOpen} onClose={close} />
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ export function ThemeSelector() {
|
|||||||
index === selectedIndex
|
index === selectedIndex
|
||||||
? 'bg-accent/20 text-accent'
|
? 'bg-accent/20 text-accent'
|
||||||
: theme === themeOption.value
|
: theme === themeOption.value
|
||||||
? 'bg-accent/10 text-accent'
|
? 'bg-accent/10 text-accent'
|
||||||
: 'text-foreground hover:bg-accent/5'
|
: 'text-foreground hover:bg-accent/5'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<themeOption.icon className="w-3 h-3" />
|
<themeOption.icon className="w-3 h-3" />
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ Build the minimal viable command palette launcher - a full-screen overlay trigge
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface SessionLauncherProps {
|
interface SessionLauncherProps {
|
||||||
isOpen: boolean
|
isOpen: boolean;
|
||||||
onClose: () => void
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Features:
|
// Features:
|
||||||
@@ -34,10 +34,10 @@ interface SessionLauncherProps {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface CommandInputProps {
|
interface CommandInputProps {
|
||||||
value: string
|
value: string;
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void;
|
||||||
onSubmit: () => void
|
onSubmit: () => void;
|
||||||
placeholder?: string
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Features:
|
// Features:
|
||||||
@@ -52,20 +52,20 @@ interface CommandInputProps {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface LauncherState {
|
interface LauncherState {
|
||||||
isOpen: boolean
|
isOpen: boolean;
|
||||||
mode: 'command' | 'search' // command = launch sessions, search = find sessions/approvals
|
mode: "command" | "search"; // command = launch sessions, search = find sessions/approvals
|
||||||
query: string
|
query: string;
|
||||||
isLaunching: boolean
|
isLaunching: boolean;
|
||||||
error?: string
|
error?: string;
|
||||||
gPrefixMode: boolean
|
gPrefixMode: boolean;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
open: (mode?: 'command' | 'search') => void
|
open: (mode?: "command" | "search") => void;
|
||||||
close: () => void
|
close: () => void;
|
||||||
setQuery: (query: string) => void
|
setQuery: (query: string) => void;
|
||||||
setGPrefixMode: (enabled: boolean) => void
|
setGPrefixMode: (enabled: boolean) => void;
|
||||||
launchSession: () => Promise<void>
|
launchSession: () => Promise<void>;
|
||||||
reset: () => void
|
reset: () => void;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ interface LauncherState {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface ParsedQuery {
|
interface ParsedQuery {
|
||||||
query: string // Main text
|
query: string; // Main text
|
||||||
workingDir?: string // If query starts with /path
|
workingDir?: string; // If query starts with /path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse patterns:
|
// Parse patterns:
|
||||||
@@ -110,6 +110,7 @@ export function SessionLauncher({ isOpen, onClose }: SessionLauncherProps) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Styling Requirements**:
|
**Styling Requirements**:
|
||||||
|
|
||||||
- Full viewport overlay with backdrop-blur
|
- Full viewport overlay with backdrop-blur
|
||||||
- Centered modal (max-width: 600px)
|
- Centered modal (max-width: 600px)
|
||||||
- Monospace font family
|
- Monospace font family
|
||||||
@@ -130,6 +131,7 @@ export function CommandInput({ value, onChange, onSubmit }: CommandInputProps) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Features**:
|
**Features**:
|
||||||
|
|
||||||
- Large text input (48px height minimum)
|
- Large text input (48px height minimum)
|
||||||
- Monospace font
|
- Monospace font
|
||||||
- Placeholder text with examples
|
- Placeholder text with examples
|
||||||
@@ -143,59 +145,64 @@ export function CommandInput({ value, onChange, onSubmit }: CommandInputProps) {
|
|||||||
// useSessionLauncher.ts
|
// useSessionLauncher.ts
|
||||||
export const useSessionLauncher = create<LauncherState>((set, get) => ({
|
export const useSessionLauncher = create<LauncherState>((set, get) => ({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
query: '',
|
query: "",
|
||||||
isLaunching: false,
|
isLaunching: false,
|
||||||
|
|
||||||
open: () => set({ isOpen: true }),
|
open: () => set({ isOpen: true }),
|
||||||
close: () => set({ isOpen: false, query: '', error: undefined }),
|
close: () => set({ isOpen: false, query: "", error: undefined }),
|
||||||
setQuery: (query) => set({ query }),
|
setQuery: (query) => set({ query }),
|
||||||
|
|
||||||
launchSession: async () => {
|
launchSession: async () => {
|
||||||
// Basic session launch logic
|
// Basic session launch logic
|
||||||
// Error handling
|
// Error handling
|
||||||
// Success navigation
|
// Success navigation
|
||||||
}
|
},
|
||||||
}))
|
}));
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: Global Hotkey Integration (45 minutes)
|
### Step 4: Global Hotkey Integration (45 minutes)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Add to App.tsx or main component
|
// Add to App.tsx or main component
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
// Cmd+K - Global command palette
|
// Cmd+K - Global command palette
|
||||||
if (e.metaKey && e.key === 'k') {
|
if (e.metaKey && e.key === "k") {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
openLauncher('command')
|
openLauncher("command");
|
||||||
}
|
}
|
||||||
|
|
||||||
// / - Search sessions and approvals
|
// / - Search sessions and approvals
|
||||||
if (e.key === '/' && !e.metaKey && !e.ctrlKey && !isInputFocused()) {
|
if (e.key === "/" && !e.metaKey && !e.ctrlKey && !isInputFocused()) {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
openLauncher('search')
|
openLauncher("search");
|
||||||
}
|
}
|
||||||
|
|
||||||
// G prefix navigation (prepare for Phase 2)
|
// G prefix navigation (prepare for Phase 2)
|
||||||
if (e.key === 'g' && !e.metaKey && !e.ctrlKey && !isInputFocused()) {
|
if (e.key === "g" && !e.metaKey && !e.ctrlKey && !isInputFocused()) {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
setGPrefixMode(true)
|
setGPrefixMode(true);
|
||||||
setTimeout(() => setGPrefixMode(false), 2000)
|
setTimeout(() => setGPrefixMode(false), 2000);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
return () => document.removeEventListener('keydown', handleKeyDown)
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
// Helper to check if user is typing in an input
|
// Helper to check if user is typing in an input
|
||||||
const isInputFocused = () => {
|
const isInputFocused = () => {
|
||||||
const active = document.activeElement
|
const active = document.activeElement;
|
||||||
return active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA' || active?.contentEditable === 'true'
|
return (
|
||||||
}
|
active?.tagName === "INPUT" ||
|
||||||
|
active?.tagName === "TEXTAREA" ||
|
||||||
|
active?.contentEditable === "true"
|
||||||
|
);
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
**Enhanced Hotkey Features**:
|
**Enhanced Hotkey Features**:
|
||||||
|
|
||||||
- `Cmd+K` - Opens command palette in "command" mode (launch sessions)
|
- `Cmd+K` - Opens command palette in "command" mode (launch sessions)
|
||||||
- `/` - Opens command palette in "search" mode (find sessions/approvals)
|
- `/` - Opens command palette in "search" mode (find sessions/approvals)
|
||||||
- `g` prefix - Sets up for vim-style navigation (Phase 2: g+a = approvals, g+s = sessions)
|
- `g` prefix - Sets up for vim-style navigation (Phase 2: g+a = approvals, g+s = sessions)
|
||||||
@@ -206,30 +213,31 @@ const isInputFocused = () => {
|
|||||||
```typescript
|
```typescript
|
||||||
const launchSession = async () => {
|
const launchSession = async () => {
|
||||||
try {
|
try {
|
||||||
set({ isLaunching: true, error: undefined })
|
set({ isLaunching: true, error: undefined });
|
||||||
|
|
||||||
const parsed = parseQuery(get().query)
|
const parsed = parseQuery(get().query);
|
||||||
const response = await daemonClient.launchSession({
|
const response = await daemonClient.launchSession({
|
||||||
query: parsed.query,
|
query: parsed.query,
|
||||||
working_dir: parsed.workingDir || process.cwd()
|
working_dir: parsed.workingDir || process.cwd(),
|
||||||
})
|
});
|
||||||
|
|
||||||
// Navigate to new session
|
// Navigate to new session
|
||||||
navigate(`/session/${response.session_id}`)
|
navigate(`/session/${response.session_id}`);
|
||||||
|
|
||||||
// Close launcher
|
// Close launcher
|
||||||
get().close()
|
get().close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
set({ error: error.message })
|
set({ error: error.message });
|
||||||
} finally {
|
} finally {
|
||||||
set({ isLaunching: false })
|
set({ isLaunching: false });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## UI Design Requirements
|
## UI Design Requirements
|
||||||
|
|
||||||
### Visual Style
|
### Visual Style
|
||||||
|
|
||||||
- **Background**: Full-screen overlay with backdrop-blur-sm
|
- **Background**: Full-screen overlay with backdrop-blur-sm
|
||||||
- **Modal**: Centered, rounded corners, dark background
|
- **Modal**: Centered, rounded corners, dark background
|
||||||
- **Typography**: Monospace font (ui-monospace, Monaco, "Cascadia Code")
|
- **Typography**: Monospace font (ui-monospace, Monaco, "Cascadia Code")
|
||||||
@@ -237,6 +245,7 @@ const launchSession = async () => {
|
|||||||
- **Spacing**: Generous padding and margins for breathing room
|
- **Spacing**: Generous padding and margins for breathing room
|
||||||
|
|
||||||
### Layout Structure
|
### Layout Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────┐
|
||||||
│ [OVERLAY] │
|
│ [OVERLAY] │
|
||||||
@@ -253,6 +262,7 @@ const launchSession = async () => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Interaction States
|
### Interaction States
|
||||||
|
|
||||||
- **Default**: Clean input with placeholder
|
- **Default**: Clean input with placeholder
|
||||||
- **Typing**: Real-time character count
|
- **Typing**: Real-time character count
|
||||||
- **Loading**: Spinner + "Launching..." text
|
- **Loading**: Spinner + "Launching..." text
|
||||||
@@ -262,16 +272,17 @@ const launchSession = async () => {
|
|||||||
## Integration Points
|
## Integration Points
|
||||||
|
|
||||||
### App.tsx Integration
|
### App.tsx Integration
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
function App() {
|
function App() {
|
||||||
const { isOpen, close } = useSessionLauncher()
|
const { isOpen, close } = useSessionLauncher()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Existing app content */}
|
{/* Existing app content */}
|
||||||
<SessionTable />
|
<SessionTable />
|
||||||
<SessionDetail />
|
<SessionDetail />
|
||||||
|
|
||||||
{/* Command palette overlay */}
|
{/* Command palette overlay */}
|
||||||
<SessionLauncher isOpen={isOpen} onClose={close} />
|
<SessionLauncher isOpen={isOpen} onClose={close} />
|
||||||
</>
|
</>
|
||||||
@@ -280,9 +291,11 @@ function App() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### SessionTable Button
|
### SessionTable Button
|
||||||
|
|
||||||
Add floating action button or header button:
|
Add floating action button or header button:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
<Button
|
<Button
|
||||||
onClick={() => useSessionLauncher.getState().open()}
|
onClick={() => useSessionLauncher.getState().open()}
|
||||||
className="fixed bottom-6 right-6"
|
className="fixed bottom-6 right-6"
|
||||||
>
|
>
|
||||||
@@ -293,6 +306,7 @@ Add floating action button or header button:
|
|||||||
## Testing Requirements
|
## Testing Requirements
|
||||||
|
|
||||||
### Unit Tests
|
### Unit Tests
|
||||||
|
|
||||||
- SessionLauncher renders correctly
|
- SessionLauncher renders correctly
|
||||||
- CommandInput handles input changes
|
- CommandInput handles input changes
|
||||||
- Hotkey triggers launcher open
|
- Hotkey triggers launcher open
|
||||||
@@ -300,6 +314,7 @@ Add floating action button or header button:
|
|||||||
- Error states display properly
|
- Error states display properly
|
||||||
|
|
||||||
### Integration Tests
|
### Integration Tests
|
||||||
|
|
||||||
- End-to-end session creation flow
|
- End-to-end session creation flow
|
||||||
- Keyboard navigation works
|
- Keyboard navigation works
|
||||||
- Mobile responsiveness
|
- Mobile responsiveness
|
||||||
@@ -329,10 +344,11 @@ Add floating action button or header button:
|
|||||||
## Next Phase Preparation
|
## Next Phase Preparation
|
||||||
|
|
||||||
This implementation should be designed to easily extend with:
|
This implementation should be designed to easily extend with:
|
||||||
|
|
||||||
- Template parsing (`:debug`, `:review`)
|
- Template parsing (`:debug`, `:review`)
|
||||||
- Model selection (`@claude-opus`)
|
- Model selection (`@claude-opus`)
|
||||||
- Advanced flags (`--max-turns=5`)
|
- Advanced flags (`--max-turns=5`)
|
||||||
- Context detection and suggestions
|
- Context detection and suggestions
|
||||||
- Recent session history
|
- Recent session history
|
||||||
|
|
||||||
Keep the architecture clean and extensible for Phase 2 enhancements.
|
Keep the architecture clean and extensible for Phase 2 enhancements.
|
||||||
|
|||||||
@@ -73,22 +73,22 @@ src/components/
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface LauncherState {
|
interface LauncherState {
|
||||||
isOpen: boolean
|
isOpen: boolean;
|
||||||
query: string
|
query: string;
|
||||||
parsedCommand: ParsedCommand
|
parsedCommand: ParsedCommand;
|
||||||
suggestions: Suggestion[]
|
suggestions: Suggestion[];
|
||||||
recentSessions: RecentSession[]
|
recentSessions: RecentSession[];
|
||||||
templates: Template[]
|
templates: Template[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParsedCommand {
|
interface ParsedCommand {
|
||||||
query: string // Main query text
|
query: string; // Main query text
|
||||||
model?: string // @claude-opus, @gpt-4, etc.
|
model?: string; // @claude-opus, @gpt-4, etc.
|
||||||
workingDir?: string // /src/components
|
workingDir?: string; // /src/components
|
||||||
template?: string // :debug, :review
|
template?: string; // :debug, :review
|
||||||
maxTurns?: number // --max-turns=10
|
maxTurns?: number; // --max-turns=10
|
||||||
approvals?: boolean // --approvals, --no-approvals
|
approvals?: boolean; // --approvals, --no-approvals
|
||||||
customInstructions?: string // Additional context
|
customInstructions?: string; // Additional context
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -98,18 +98,18 @@ Leveraging the full `LaunchSessionRequest` interface:
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface LaunchSessionRequest {
|
interface LaunchSessionRequest {
|
||||||
query: string // ✅ Main input
|
query: string; // ✅ Main input
|
||||||
model?: string // ✅ Smart model selection
|
model?: string; // ✅ Smart model selection
|
||||||
working_dir?: string // ✅ Auto-detected/specified
|
working_dir?: string; // ✅ Auto-detected/specified
|
||||||
max_turns?: number // ✅ Template defaults
|
max_turns?: number; // ✅ Template defaults
|
||||||
system_prompt?: string // ✅ Template system prompts
|
system_prompt?: string; // ✅ Template system prompts
|
||||||
append_system_prompt?: string // ✅ User customizations
|
append_system_prompt?: string; // ✅ User customizations
|
||||||
custom_instructions?: string // ✅ Project-specific context
|
custom_instructions?: string; // ✅ Project-specific context
|
||||||
allowed_tools?: string[] // ✅ Template restrictions
|
allowed_tools?: string[]; // ✅ Template restrictions
|
||||||
disallowed_tools?: string[] // ✅ Security controls
|
disallowed_tools?: string[]; // ✅ Security controls
|
||||||
mcp_config?: unknown // ✅ Advanced MCP settings
|
mcp_config?: unknown; // ✅ Advanced MCP settings
|
||||||
permission_prompt_tool?: string // ✅ Approval tool selection
|
permission_prompt_tool?: string; // ✅ Approval tool selection
|
||||||
verbose?: boolean // ✅ Debug mode
|
verbose?: boolean; // ✅ Debug mode
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -121,50 +121,50 @@ interface LaunchSessionRequest {
|
|||||||
// Auto-detect project context
|
// Auto-detect project context
|
||||||
const detectContext = async (): Promise<SessionContext> => {
|
const detectContext = async (): Promise<SessionContext> => {
|
||||||
return {
|
return {
|
||||||
gitRepo: await detectGitRepo(), // Current branch, status
|
gitRepo: await detectGitRepo(), // Current branch, status
|
||||||
packageManager: await detectPackageManager(), // npm, yarn, bun
|
packageManager: await detectPackageManager(), // npm, yarn, bun
|
||||||
framework: await detectFramework(), // React, Next.js, etc.
|
framework: await detectFramework(), // React, Next.js, etc.
|
||||||
runningProcesses: await getRunningProcesses(), // dev servers
|
runningProcesses: await getRunningProcesses(), // dev servers
|
||||||
recentFiles: await getMostRecentFiles(), // Recently edited
|
recentFiles: await getMostRecentFiles(), // Recently edited
|
||||||
workingDir: process.cwd()
|
workingDir: process.cwd(),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Template System
|
### 2. Template System
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface Template {
|
interface Template {
|
||||||
id: string // 'debug', 'review', 'refactor'
|
id: string; // 'debug', 'review', 'refactor'
|
||||||
trigger: string // ':debug'
|
trigger: string; // ':debug'
|
||||||
name: string // 'Debug Session'
|
name: string; // 'Debug Session'
|
||||||
description: string // 'Debug performance issues'
|
description: string; // 'Debug performance issues'
|
||||||
systemPrompt: string // Template-specific instructions
|
systemPrompt: string; // Template-specific instructions
|
||||||
allowedTools?: string[] // Restricted tool access
|
allowedTools?: string[]; // Restricted tool access
|
||||||
maxTurns: number // Default turn limit
|
maxTurns: number; // Default turn limit
|
||||||
model: string // Preferred model
|
model: string; // Preferred model
|
||||||
tags: string[] // For filtering/search
|
tags: string[]; // For filtering/search
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUILTIN_TEMPLATES: Template[] = [
|
const BUILTIN_TEMPLATES: Template[] = [
|
||||||
{
|
{
|
||||||
id: 'debug',
|
id: "debug",
|
||||||
trigger: ':debug',
|
trigger: ":debug",
|
||||||
name: 'Debug Session',
|
name: "Debug Session",
|
||||||
systemPrompt: 'You are debugging code. Focus on finding root causes.',
|
systemPrompt: "You are debugging code. Focus on finding root causes.",
|
||||||
allowedTools: ['terminal', 'file_ops', 'browser'],
|
allowedTools: ["terminal", "file_ops", "browser"],
|
||||||
maxTurns: 20,
|
maxTurns: 20,
|
||||||
model: 'claude-3-5-sonnet-20241022'
|
model: "claude-3-5-sonnet-20241022",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'review',
|
id: "review",
|
||||||
trigger: ':review',
|
trigger: ":review",
|
||||||
name: 'Code Review',
|
name: "Code Review",
|
||||||
systemPrompt: 'Review code for bugs, performance, and best practices.',
|
systemPrompt: "Review code for bugs, performance, and best practices.",
|
||||||
maxTurns: 10,
|
maxTurns: 10,
|
||||||
model: 'claude-3-5-sonnet-20241022'
|
model: "claude-3-5-sonnet-20241022",
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Intelligent Suggestions
|
### 3. Intelligent Suggestions
|
||||||
@@ -172,22 +172,22 @@ const BUILTIN_TEMPLATES: Template[] = [
|
|||||||
```typescript
|
```typescript
|
||||||
const generateSuggestions = (query: string, context: SessionContext) => {
|
const generateSuggestions = (query: string, context: SessionContext) => {
|
||||||
// Fuzzy match templates
|
// Fuzzy match templates
|
||||||
const templateSuggestions = templates.filter(t =>
|
const templateSuggestions = templates.filter(
|
||||||
fuzzyMatch(query, t.name) || fuzzyMatch(query, t.tags)
|
(t) => fuzzyMatch(query, t.name) || fuzzyMatch(query, t.tags),
|
||||||
)
|
);
|
||||||
|
|
||||||
// Recent session patterns
|
// Recent session patterns
|
||||||
const recentSuggestions = recentSessions
|
const recentSuggestions = recentSessions
|
||||||
.filter(s => fuzzyMatch(query, s.query))
|
.filter((s) => fuzzyMatch(query, s.query))
|
||||||
.slice(0, 3)
|
.slice(0, 3);
|
||||||
|
|
||||||
// File-based suggestions
|
// File-based suggestions
|
||||||
const fileSuggestions = context.recentFiles
|
const fileSuggestions = context.recentFiles
|
||||||
.filter(f => fuzzyMatch(query, f.name))
|
.filter((f) => fuzzyMatch(query, f.name))
|
||||||
.map(f => `debug ${f.name}`)
|
.map((f) => `debug ${f.name}`);
|
||||||
|
|
||||||
return [...templateSuggestions, ...recentSuggestions, ...fileSuggestions]
|
return [...templateSuggestions, ...recentSuggestions, ...fileSuggestions];
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation Phases
|
## Implementation Phases
|
||||||
@@ -195,11 +195,13 @@ const generateSuggestions = (query: string, context: SessionContext) => {
|
|||||||
### Phase 1: Core Command Palette (4 hours)
|
### Phase 1: Core Command Palette (4 hours)
|
||||||
|
|
||||||
**Files**:
|
**Files**:
|
||||||
|
|
||||||
- `SessionLauncher.tsx` - Full-screen overlay with search
|
- `SessionLauncher.tsx` - Full-screen overlay with search
|
||||||
- `CommandInput.tsx` - Smart input parsing
|
- `CommandInput.tsx` - Smart input parsing
|
||||||
- `useSessionLauncher.ts` - State management hook
|
- `useSessionLauncher.ts` - State management hook
|
||||||
|
|
||||||
**Features**:
|
**Features**:
|
||||||
|
|
||||||
- Global `Cmd+K` hotkey
|
- Global `Cmd+K` hotkey
|
||||||
- Single input with instant preview
|
- Single input with instant preview
|
||||||
- Basic query parsing (templates, model selection)
|
- Basic query parsing (templates, model selection)
|
||||||
@@ -208,10 +210,12 @@ const generateSuggestions = (query: string, context: SessionContext) => {
|
|||||||
### Phase 2: Smart Context (3 hours)
|
### Phase 2: Smart Context (3 hours)
|
||||||
|
|
||||||
**Files**:
|
**Files**:
|
||||||
|
|
||||||
- `useContextDetection.ts` - Auto-detect project context
|
- `useContextDetection.ts` - Auto-detect project context
|
||||||
- `templates.ts` - Built-in template definitions
|
- `templates.ts` - Built-in template definitions
|
||||||
|
|
||||||
**Features**:
|
**Features**:
|
||||||
|
|
||||||
- Auto-detect working directory, git status
|
- Auto-detect working directory, git status
|
||||||
- Template system with `:shortcut` triggers
|
- Template system with `:shortcut` triggers
|
||||||
- Recent session history
|
- Recent session history
|
||||||
@@ -220,6 +224,7 @@ const generateSuggestions = (query: string, context: SessionContext) => {
|
|||||||
### Phase 3: Advanced Parsing (2 hours)
|
### Phase 3: Advanced Parsing (2 hours)
|
||||||
|
|
||||||
**Features**:
|
**Features**:
|
||||||
|
|
||||||
- Full command parsing (`@model --flags /paths`)
|
- Full command parsing (`@model --flags /paths`)
|
||||||
- Real-time validation and error states
|
- Real-time validation and error states
|
||||||
- Advanced daemon client options
|
- Advanced daemon client options
|
||||||
@@ -228,6 +233,7 @@ const generateSuggestions = (query: string, context: SessionContext) => {
|
|||||||
### Phase 4: Polish & Performance (3 hours)
|
### Phase 4: Polish & Performance (3 hours)
|
||||||
|
|
||||||
**Features**:
|
**Features**:
|
||||||
|
|
||||||
- Sub-100ms interactions
|
- Sub-100ms interactions
|
||||||
- Keyboard navigation perfection
|
- Keyboard navigation perfection
|
||||||
- Mobile-responsive design
|
- Mobile-responsive design
|
||||||
|
|||||||
Reference in New Issue
Block a user