Files
humanlayer/Makefile
Sundeep Malladi fd6739dffe Suppress approval notifications for already-approved messages (and some other stuff) (#391)
* Fix auto-accept edits toggle to update store directly

- Update SessionDetail shift+tab handler to directly update store
- Ensures immediate UI feedback when toggling auto-accept mode
- Add debug logging to track toggle behavior

* Add SessionSettingsChanged event for real-time settings updates

Implements a new event type to propagate session settings changes (like auto-accept edits)
from backend to frontend via SSE. This ensures the UI stays in sync when settings are
modified through any interface (REST API or RPC).

Key changes:
- Add EventSessionSettingsChanged constant to event bus types
- Add UpdateSessionSettings method to SessionManager interface to centralize event publishing
- Update REST API handler to use session manager instead of direct store updates
- Fix SSE handler to recognize and forward session_settings_changed events
- Add TypeScript types and event handling in frontend
- Wire up event handler in Layout.tsx to update local state
- Add make task for regenerating SDKs from OpenAPI specs

The implementation follows the existing architectural pattern where managers (not handlers
or stores) are responsible for publishing events to maintain consistency.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add approval notifications to Layout component

Implements proper notification handling for approval events in the main Layout component.
This ensures users are notified when AI agents request approval for tool usage.

Key changes:
- Add notification tracking with isItemNotified/addNotifiedItem to prevent duplicate notifications
- Implement onNewApproval handler to show notifications when approvals are requested
- Move session fetching outside completion block for better code reuse
- Add proper TypeScript types for event data
- Show tool-specific approval messages with fallback for error cases

The implementation follows the event-based notification pattern, as the investigation
showed that status-based notifications (waiting_input transitions) never occur in practice.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add delay to prevent notifications for auto-approved items

Implements a 100ms delay in the onNewApproval handler to check if an approval
was auto-resolved before showing a notification. This prevents the brief flash
of approval notifications for edit operations when auto-accept mode is enabled.

The solution works by:
- Waiting 100ms after receiving a new_approval event
- Checking if the session is still in waiting_input status
- Skipping the notification if the status has already changed (indicating auto-approval)

This handles the race condition where the backend sends both new_approval and
approval_resolved events in quick succession for auto-approved items.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix auto-approval notification logic with approval cache

Replaces the flawed session status check with a proper approval resolution cache.
The previous approach incorrectly skipped notifications for manual approvals (like Bash)
because it checked session status before updating it to waiting_input.

The new implementation:
- Tracks resolved approvals in a cache when onApprovalResolved fires
- Checks this cache after 100ms delay to determine if an approval was auto-resolved
- Works correctly for both auto-approved (Edit tools) and manual approval cases

Also adds timestamps to console logs for better debugging of event timing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(wui): rework shift+tab to toggle auto-accept edits for selected sessions

- Add bulkSetAutoAcceptEdits method to store for bulk updating auto-accept settings
- Replace shift+tab view mode toggle with auto-accept toggle functionality
- Similar behavior to 'e' archive hotkey: works on selected sessions or focused session
- Shows success/error toast notifications for user feedback
- Intelligently toggles status: if all selected sessions have auto-accept enabled, disables it; otherwise enables it

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(wui): improve archive hotkey behavior and add auto-accept indicator

- Fix archive hotkey ('e') to properly handle mixed selection states
- Show warning when trying to bulk archive sessions with different archived states
- Add visual indicator (⏵⏵) for running sessions with auto-accept edits enabled
- Improve error handling and logging for archive operations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* MCT

* test tweaks

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-31 15:51:52 -05:00

652 lines
23 KiB
Makefile

.PHONY: setup
setup: ## Set up the repository with all dependencies and builds
hack/setup_repo.sh
# CI-specific targets
.PHONY: setup-ci ci-tools
## CI Setup Commands
setup-ci: ci-tools setup ## Complete CI setup including CI-specific tools
@echo "✅ CI setup complete"
ci-tools: ## Install CI-specific tools
@echo "Installing CI-specific tools..."
@command -v claude >/dev/null 2>&1 || npm install -g @anthropic-ai/claude-code
@command -v uv >/dev/null 2>&1 || (curl -LsSf https://astral.sh/uv/install.sh | sh)
@command -v golangci-lint >/dev/null 2>&1 || go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
thoughts:
humanlayer thoughts init --directory humanlayer
.PHONY: worktree
worktree: ## Create a new worktree for development work (use hack/create_worktree.sh branch_name for specific names)
hack/create_worktree.sh
.PHONY: check-py
check-py: ## Run code quality tools.
@. ./hack/run_silent.sh && print_header "humanlayer" "Python checks"
@. ./hack/run_silent.sh && run_with_quiet "Dependencies synced" "uv sync -q"
@. ./hack/run_silent.sh && run_silent "Pre-commit hooks passed" "uv run pre-commit run -a"
@. ./hack/run_silent.sh && run_silent "Type checking passed (mypy)" "uv run mypy"
@. ./hack/run_silent.sh && run_silent "Dependency analysis passed" "uv run deptry ."
.PHONY: check-ts
check-ts:
@. ./hack/run_silent.sh && print_header "humanlayer-ts" "TypeScript checks"
@. ./hack/run_silent.sh && run_silent "Type checking passed" "npm --silent -C humanlayer-ts run check"
@. ./hack/run_silent.sh && print_header "humanlayer-ts-vercel-ai-sdk" "TypeScript checks"
@. ./hack/run_silent.sh && run_silent "Type checking passed" "npm --silent -C humanlayer-ts-vercel-ai-sdk run check"
check-hlyr:
@$(MAKE) -C hlyr check VERBOSE=$(VERBOSE)
check-wui:
@$(MAKE) -C humanlayer-wui check VERBOSE=$(VERBOSE)
check-hld:
@$(MAKE) -C hld check VERBOSE=$(VERBOSE)
check-claudecode-go:
@$(MAKE) -C claudecode-go check VERBOSE=$(VERBOSE)
.PHONY: check-header
check-header:
@sh -n ./hack/run_silent.sh || (echo "❌ Shell script syntax error in hack/run_silent.sh" && exit 1)
@. ./hack/run_silent.sh && print_main_header "Running Checks"
# Summary removed - tracking doesn't work across sub-makes
.PHONY: check
check: check-header check-py check-ts check-hlyr check-wui check-hld check-claudecode-go
typecheck: ## just the typechecks
@. ./hack/run_silent.sh && run_silent "Static type checking: mypy" "uv run mypy"
.PHONY: test-py
test-py: ## Test the code with pytest
@. ./hack/run_silent.sh && print_header "humanlayer" "Python tests"
@. ./hack/run_silent.sh && run_silent_with_test_count "Pytest passed" "uv run pytest ./humanlayer --cov --cov-config=pyproject.toml --cov-report=xml --junitxml=junit.xml" "pytest"
.PHONY: test-ts
test-ts: ## Test the code with jest
@. ./hack/run_silent.sh && print_header "humanlayer-ts" "TypeScript tests"
@. ./hack/run_silent.sh && run_silent "Jest passed" "npm --silent -C humanlayer-ts run test"
@. ./hack/run_silent.sh && print_header "humanlayer-ts-vercel-ai-sdk" "TypeScript tests"
@. ./hack/run_silent.sh && run_silent "Jest passed" "npm --silent -C humanlayer-ts-vercel-ai-sdk run test"
.PHONY: test-hlyr
test-hlyr: ## Test hlyr CLI tool
@$(MAKE) -C hlyr test VERBOSE=$(VERBOSE)
.PHONY: test-hld
test-hld: ## Test hld daemon (unit and integration tests)
@$(MAKE) -C hld test VERBOSE=$(VERBOSE)
.PHONY: test-hld-integration
test-hld-integration: ## Test hld daemon (including integration tests)
@$(MAKE) -C hld test
.PHONY: e2e-test
e2e-test: ## Run end-to-end REST API tests
@$(MAKE) -C hld e2e-test
.PHONY: test-claudecode-go
test-claudecode-go: ## Test claudecode-go
@$(MAKE) -C claudecode-go test VERBOSE=$(VERBOSE)
.PHONY: test-header
test-header:
@sh -n ./hack/run_silent.sh || (echo "❌ Shell script syntax error in hack/run_silent.sh" && exit 1)
@. ./hack/run_silent.sh && print_main_header "Running Tests"
.PHONY: clean-wui-release
clean-wui-release: ## clean WUI release
rm -rf humanlayer-wui/src-tauri/target/release/
.PHONY: test-wui
test-wui: ## Test humanlayer-wui
@$(MAKE) -C humanlayer-wui test VERBOSE=$(VERBOSE)
.PHONY: test
test: test-header test-py test-ts test-hlyr test-wui test-hld test-claudecode-go
.PHONY: check-test
check-test: ## Run all checks and tests
@$(MAKE) check
@$(MAKE) test
.PHONY: check-verbose
check-verbose: ## Run checks with verbose output
@VERBOSE=1 $(MAKE) check
.PHONY: test-verbose
test-verbose: ## Run tests with verbose output
@VERBOSE=1 $(MAKE) test
.PHONY: build
build: clean-build ## Build wheel file using uv
: 🚀 Creating wheel file
uv build
.PHONY: build-ts
build-ts:
npm -C humanlayer-ts run build
.PHONY: clean-build
clean-build: ## clean build artifacts
@rm -rf dist
.PHONY: publish-py
publish-py: ## publish a release to pypi. with UV_PUBLISH_TOKEN
: 🚀 Publishing.
uv publish
.PHONY: publish
publish: publish-py
.PHONY: publish-ts
publish-ts: build-ts
npm -C humanlayer-ts publish
.PHONY: build-and-publish
build-and-publish: build publish ## Build and publish.
.PHONY: generate-sdks
generate-sdks: ## Regenerate all SDKs from OpenAPI specs
@echo "Regenerating TypeScript SDK from OpenAPI spec..."
@$(MAKE) -C hld generate-sdks
@echo "Updating SDK in humanlayer-wui..."
@$(MAKE) -C humanlayer-wui install
@echo "SDK regeneration complete!"
.PHONY: help
help:
grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := help
.PHONY: smoke-test-examples-py
smoke-test-examples-py:
examples/langchain/venv/bin/pip install -r examples/langchain/requirements.txt
: 🦾 human_as_tool_linkedin
examples/langchain/venv/bin/python examples/langchain/04-human_as_tool_linkedin.py
: 🦾 human_as_tool_linkedin
examples/langchain/venv/bin/python examples/langchain/04-human_as_tool_linkedin_frustration.py
examples/langchain/venv/bin/python examples/langchain/09-email-contact.py
.PHONY: smoke-test-examples-ts
smoke-test-examples-ts:
npm -C examples/ts_openai_client install
: 🦾 human-as-tool
npm -C examples/ts_openai_client run human-as-tool
: 🦾 human-email
npm -C examples/ts_openai_client run human-email
npm -C examples/ts_langchain install
: 🦾 ts_langchain
npm -C examples/ts_langchain run example
npm -C examples/ts_email_classifier install
: 🦾 ts_email_classifier
npm -C examples/ts_email_classifier run human-review-sync
: skipping async for now
: npm -C examples/ts_email_classifier run human-review-async
.PHONY: smoke-test-examples
smoke-test-examples: smoke-test-examples-py smoke-test-examples-ts
.PHONY: test-examples
test-examples:
:
: 🦾 controlflow
:
examples/controlflow/venv/bin/pip install -r examples/controlflow/requirements.txt
examples/controlflow/venv/bin/python examples/controlflow/controlflow_math.py
:
: 🚣 crewai
:
examples/crewai/venv/bin/pip install -r examples/crewai/requirements.txt
examples/crewai/venv/bin/python examples/crewai/crewai_math.py
examples/crewai/venv/bin/python examples/crewai/crewai_onboarding_agent.py
examples/crewai/venv/bin/python examples/crewai/crewai_onboarding_agent_human_as_tool.py
:
: 🚣 griptape
:
examples/griptape/venv/bin/pip install -r examples/griptape/requirements.txt
examples/griptape/venv/bin/python examples/griptape/01-math_example.py
:
: 🦜⛓️ langchain
:
examples/langchain/venv/bin/pip install -r examples/langchain/requirements.txt
examples/langchain/venv/bin/python examples/langchain/01-math_example.py
examples/langchain/venv/bin/python examples/langchain/02-customer_email.py
examples/langchain/venv/bin/python examples/langchain/04-human_as_tool_linkedin.py
examples/langchain/venv/bin/python examples/langchain/04-human_as_tool_onboarding.py
examples/langchain/venv/bin/python examples/langchain/04-human_as_tool_linkedin_frustration.py
examples/langchain/venv/bin/python examples/langchain/05-approvals_and_humans_composite.py
examples/langchain/venv/bin/python examples/langchain/08-email-channel.py
examples/langchain/venv/bin/python examples/langchain/09-email-contact.py
:
: 🦜⛓️ langchain-anthropic
:
examples/langchain-anthropic/venv/bin/pip install -r examples/langchain-anthropic/requirements.txt
examples/langchain-anthropic/venv/bin/python examples/langchain-anthropic/01-math_example.py
:
: 🧠 OpenAI
:
examples/openai_client/venv/bin/pip install -r examples/openai_client/requirements.txt
examples/openai_client/venv/bin/python examples/openai_client/02-imperative_fetch.py
examples/openai_client/venv/bin/python examples/openai_client/03-imperative_fetch_based.py
:
: 🦜⛓️ ts_langchain
:
npm run --prefix examples/ts_langchain example
:
: 🧠 ts_openai
:
npm run --prefix examples/ts_openai_client example
npm run --prefix examples/ts_openai_client human-as-tool
npm run --prefix examples/ts_openai_client human-email
npm run --prefix examples/ts_openai_client agent-side
.PHONY: githooks
githooks:
:
: 🚀 Installing pre-push hook
:
echo 'make check test' > .git/hooks/pre-push
chmod +x .git/hooks/pre-push
.PHONY: update-examples-versions
VERSION?=
update-examples-versions:
@if [ -z "$(VERSION)" ]; then \
echo "VERSION is not set"; \
exit 1; \
fi; \
: 🚀 Updating examples versions to $(VERSION)
find examples/*/requirements.txt -type f -exec sed -i '' 's/humanlayer==.*$$/humanlayer==$(VERSION)/g' {} +
.PHONY: update-examples-ts-versions
update-examples-ts-versions:
find examples/*/package.json -type f -exec sed -i '' 's/@humanlayer\/sdk": ".*"/@humanlayer\/sdk": "$(VERSION)"/g' {} +
.PHONY: update-examples-tokens
HUMANLAYER_API_KEY?=
HUMANLAYER_API_BASE?=
update-examples-tokens:
@if [ -z "$(HUMANLAYER_API_KEY)" ]; then \
echo "HUMANLAYER_API_KEY must be set"; \
exit 1; \
fi; \
: 🚀 Updating examples .env files with new tokens
find examples/*/.env -type f -exec sh -c ' \
echo "" >> "{}"; \
echo "# added by Makefile at $(shell date)" >> "{}"; \
echo "HUMANLAYER_API_KEY=$(HUMANLAYER_API_KEY)" >> "{}"; \
echo "HUMANLAYER_API_BASE=$(HUMANLAYER_API_BASE)" >> "{}"; \
' \;
## Release process
#way too manual for now. on a branch with a clean git workspace
#- TS - publish release candidate
#- TS - udpate examples to alpha versions, run smoketests
#- TS - publish full version
#- TS - udpate examples to published version, run smoketests
#
#- PY - publish release candidate
#- PY - udpate examples to alpha versions, run smoketests
#- PY - publish full version
#- PY - udpate examples to published version, run smoketests
#- COMMIT
#- MERGE
#- TAG
#- TS - update to new alpha version
#- PY - update to new alpha version
current-ts-version = $(shell cat humanlayer-ts/package.json | jq -r '.version')
current-py-version = $(shell cat pyproject.toml | grep version | head -1 | cut -d'"' -f2)
new-version = $(shell echo $(current-ts-version) | sed 's/-alpha.*//')
increment := patch
next-alpha-version = $(shell npx semver -i $(increment) $(new-version))-alpha.1
_check-uv-publish-token:
@if [ -z "$(UV_PUBLISH_TOKEN)" ]; then \
echo "UV_PUBLISH_TOKEN must be set"; \
echo " export UV_PUBLISH_TOKEN=..."; \
echo; \
exit 1; \
fi
.PHONY: _release-plan-versions
_release-plan-versions:
@echo "Current versions:"
@echo " TS: $(current-ts-version)"
@echo " PY: $(current-py-version)"
@echo " New version: $(new-version)"
@echo " Next alpha version: $(next-alpha-version)"
_release-branch-check:
@if [ "$(shell git rev-parse --abbrev-ref HEAD)" != "release-$(new-version)" ]; then \
echo "Must be on branch release-$(new-version)"; \
echo; \
echo " git checkout -b release-$(new-version)"; \
echo; \
exit 1; \
else \
echo; \
echo "On branch release-$(new-version)"; \
echo; \
fi
_staging-env-check:
@if [ -z "$(HUMANLAYER_API_BASE)" ]; then \
echo "HUMANLAYER_API_BASE must be set"; \
echo " export HUMANLAYER_API_BASE=https://api.dev.humanlayer.dev/humanlayer/v1"; \
echo " export HUMANLAYER_API_KEY="; \
echo; \
exit 1; \
else \
echo "HUMANLAYER_API_BASE is set to $(HUMANLAYER_API_BASE)"; \
echo; \
fi
_production-env-check:
@if [ ! -z "$(HUMANLAYER_API_BASE)" ] && [ "$(HUMANLAYER_API_BASE)" != "https://api.humanlayer.dev/humanlayer/v1" ]; then \
echo "HUMANLAYER_API_BASE must be empty or set to https://api.humanlayer.dev/humanlayer/v1"; \
echo " unset HUMANLAYER_API_BASE"; \
echo " export HUMANLAYER_API_KEY="; \
echo; \
exit 1; \
else \
echo "HUMANLAYER_API_BASE is set to $(HUMANLAYER_API_BASE)"; \
echo; \
fi
.PHONY: release-plan
release-plan: _release-plan-versions _release-branch-check _staging-env-check
@echo
@echo "Release steps:"
@echo "1. Publish TypeScript alpha:"
@echo " - cd humanlayer-ts && npm publish --tag alpha --access public"
@echo " - make update-examples-ts-versions VERSION=$(current-ts-version)"
@echo " - make smoke-test-examples-ts"
@echo
@echo "2. Publish Python alpha:"
@echo " - make build-and-publish"
@echo " - make update-examples-versions VERSION=$(current-py-version)"
@echo " - make smoke-test-examples-py"
@echo
@echo "3. Switch to production env"
@: check with the user to ensure they are pointed at production
@echo " - export HUMANLAYER_API_BASE=https://api.humanlayer.dev/humanlayer/v1"
@echo " - export HUMANLAYER_API_KEY="
@echo
@echo "4. Publish TypeScript:"
@echo " - sed -i '' 's/$(current-ts-version)/$(new-version)/' humanlayer-ts/package.json"
@echo " - cd humanlayer-ts && npm publish --access public"
@echo " - make update-examples-ts-versions VERSION=$(new-version)"
@echo " - make smoke-test-examples-ts"
@echo
@echo "5. Publish Python:"
@echo " - sed -i '' 's/$(current-py-version)/$(new-version)/' pyproject.toml"
@echo " - make build-and-publish"
@echo " - make update-examples-versions VERSION=$(new-version)"
@echo " - make smoke-test-examples-py"
@echo
@echo "6. Finalize:"
@echo " - git commit -am 'release: v$(new-version)' && git push upstream release-$(new-version)"
@echo " - git tag v$(new-version)"
@echo " - git push upstream release-$(new-version) --tags"
@echo
@echo "7. Next alpha:"
@echo " - Update version in package.json to $(next-alpha-version)"
@echo " - sed -i '' 's/$(new-version)/$(next-alpha-version)/' humanlayer-ts/package.json"
@echo " - Update version in pyproject.toml to $(next-alpha-version)"
@echo " - sed -i '' 's/$(new-version)/$(next-alpha-version)/' pyproject.toml"
@echo " - git commit -am 'bump to next alpha'"
@echo " - git diff PREVIOUS_TAG | claude -p 'update the changelog' --allowedTools="Edit"
@echo " - git push upstream release-$(new-version)"
.PHONY: release-alpha
release-alpha: _check-uv-publish-token release-plan
: confirming release plan
@read -p "Press Enter to continue..."
@echo "Releasing..."
cd humanlayer-ts && npm run build && npm publish --tag alpha --access public
:
: waiting for ts publish to complete
:
sleep 30
@$(MAKE) update-examples-ts-versions VERSION=$(current-ts-version)
@$(MAKE) smoke-test-examples-ts
@$(MAKE) build-and-publish
:
: waiting for py publish to complete
:
sleep 30
@$(MAKE) update-examples-versions VERSION=$(current-py-version)
@$(MAKE) smoke-test-examples-py
@echo "Alpha tested against staging, to proceed, update env vars to point at production"
@echo
@echo " export HUMANLAYER_API_BASE=https://api.humanlayer.dev/humanlayer/v1"
@echo " export HUMANLAYER_API_KEY=..."
@echo " (manual) promote saas release to production"
@echo " make release-and-test-prod"
.PHONY: release-and-test-prod
release-and-test-prod: _release-plan-versions _release-branch-check _production-env-check
@echo "Releasing..."
@echo "Publish TypeScript:"
sed -i '' 's/$(current-ts-version)/$(new-version)/' humanlayer-ts/package.json
cat humanlayer-ts/package.json | grep version
@read -p "Press Enter to continue..."
cd humanlayer-ts && npm run build && npm publish --access public
@$(MAKE) update-examples-ts-versions VERSION=$(new-version)
:
: waiting for ts publish to complete
:
sleep 30
@$(MAKE) smoke-test-examples-ts
@echo "Publish Python:"
sed -i '' 's/$(current-py-version)/$(new-version)/' pyproject.toml
cat pyproject.toml | grep version
@read -p "Press Enter to continue..."
@$(MAKE) build-and-publish
@$(MAKE) update-examples-versions VERSION=$(new-version)
:
: waiting for py publish to complete
:
@sleep 30
@$(MAKE) smoke-test-examples-py
@echo "Finalize:"
git commit -am 'release: v$(current-ts-version)' && git push upstream release-$(new-version)
git tag v$(current-ts-version)
git push upstream release-$(new-version) --tags
@echo "Next alpha:"
sed -i '' 's/$(new-version)/$(next-alpha-version)/' humanlayer-ts/package.json
sed -i '' 's/$(new-version)/$(next-alpha-version)/' pyproject.toml
git commit -am 'release: bump to next alpha'
git push upstream release-$(new-version)
hub compare
.PHONY: check-local
check-local:
@if [[ $$(git rev-parse --abbrev-ref HEAD) == local/* ]]; then \
echo "blocking push of local branch"; \
exit 1; \
fi
logfileprefix = $(shell date +%Y-%m-%d-%H-%M-%S)
.PHONY: wui
wui: wui-dev
.PHONY: daemon
daemon: daemon-dev
# Build nightly daemon binary
.PHONY: daemon-nightly-build
daemon-nightly-build:
cd hld && go build -o hld-nightly ./cmd/hld
@echo "Built nightly daemon binary: hld/hld-nightly"
# Run nightly daemon
.PHONY: daemon-nightly
daemon-nightly: daemon-nightly-build
@mkdir -p ~/.humanlayer/logs
$(eval TIMESTAMP := $(shell date +%Y-%m-%d-%H-%M-%S))
echo "$(TIMESTAMP) starting nightly daemon in $$(pwd)" > ~/.humanlayer/logs/daemon-nightly-$(TIMESTAMP).log
cd hld && ./run-with-logging.sh ~/.humanlayer/logs/daemon-nightly-$(TIMESTAMP).log ./hld-nightly
# Build and install nightly WUI
.PHONY: wui-nightly-build
wui-nightly-build:
cd humanlayer-wui && bun run tauri build --bundles app
@echo "Build complete. Installing to ~/Applications..."
cp -r humanlayer-wui/src-tauri/target/release/bundle/macos/CodeLayer.app ~/Applications/
@echo "Installed WUI nightly to ~/Applications/CodeLayer.app"
# Build humanlayer binary for bundling
.PHONY: humanlayer-build
humanlayer-build:
@echo "Building humanlayer CLI binary..."
cd hlyr && bun install && bun run build
@echo "humanlayer binary built at hlyr/dist/index.js"
# Build humanlayer standalone binary (requires bun)
.PHONY: humanlayer-binary-darwin-arm64
humanlayer-binary-darwin-arm64: humanlayer-build
@echo "Creating standalone humanlayer binary for macOS ARM64..."
cd hlyr && bun build ./dist/index.js --compile --target=bun-darwin-arm64 --outfile=humanlayer-darwin-arm64
chmod +x hlyr/humanlayer-darwin-arm64
@echo "Standalone binary created at hlyr/humanlayer-darwin-arm64"
# Build CodeLayer with bundled daemon and humanlayer
.PHONY: codelayer-bundle
codelayer-bundle:
@echo "Building daemon for bundling..."
cd hld && GOOS=darwin GOARCH=arm64 go build -o hld-darwin-arm64 ./cmd/hld
@echo "Building humanlayer for bundling..."
cd hlyr && bun install && bun run build
cd hlyr && bun build ./dist/index.js --compile --target=bun-darwin-arm64 --outfile=humanlayer-darwin-arm64
@echo "Copying binaries to CodeLayer resources..."
mkdir -p humanlayer-wui/src-tauri/bin
cp hld/hld-darwin-arm64 humanlayer-wui/src-tauri/bin/hld
cp hlyr/humanlayer-darwin-arm64 humanlayer-wui/src-tauri/bin/humanlayer
chmod +x humanlayer-wui/src-tauri/bin/hld
chmod +x humanlayer-wui/src-tauri/bin/humanlayer
@echo "Building CodeLayer with bundled binaries..."
cd humanlayer-wui && bun run tauri build
@echo "Build complete!"
# Open nightly WUI
.PHONY: wui-nightly
wui-nightly: wui-nightly-build
@echo "Opening WUI nightly..."
open ~/Applications/CodeLayer.app
# Copy production database to timestamped dev database
.PHONY: copy-db-to-dev
copy-db-to-dev:
@mkdir -p ~/.humanlayer/dev
$(eval TIMESTAMP := $(shell date +%Y-%m-%d-%H-%M-%S))
$(eval DEV_DB := ~/.humanlayer/dev/daemon-$(TIMESTAMP).db)
@if [ -f ~/.humanlayer/daemon.db ]; then \
cp ~/.humanlayer/daemon.db $(DEV_DB); \
echo "Copied production database to: $(DEV_DB)" >&2; \
echo "$(DEV_DB)"; \
else \
echo "Error: Production database not found at ~/.humanlayer/daemon.db" >&2; \
exit 1; \
fi
# Clone nightly database to dev database (with backup)
.PHONY: clone-nightly-db-to-dev-db
clone-nightly-db-to-dev-db:
@mkdir -p ~/.humanlayer/dev
$(eval TIMESTAMP := $(shell date +%Y-%m-%d-%H-%M-%S))
@# Backup existing dev database if it exists
@if [ -f ~/.humanlayer/daemon-dev.db ]; then \
echo "Backing up existing dev database..."; \
cp ~/.humanlayer/daemon-dev.db ~/.humanlayer/dev/daemon-dev-backup-$(TIMESTAMP).db; \
echo "Backed up to: ~/.humanlayer/dev/daemon-dev-backup-$(TIMESTAMP).db"; \
fi
@# Copy nightly database to dev
@if [ -f ~/.humanlayer/daemon.db ]; then \
cp ~/.humanlayer/daemon.db ~/.humanlayer/daemon-dev.db; \
echo "Cloned nightly database to: ~/.humanlayer/daemon-dev.db"; \
else \
echo "Error: Nightly database not found at ~/.humanlayer/daemon.db"; \
exit 1; \
fi
# Clean up dev databases and logs older than 10 days
.PHONY: cleanup-dev
cleanup-dev:
@echo "Cleaning up dev artifacts older than 10 days..."
@# Clean old dev databases
@if [ -d ~/.humanlayer/dev ]; then \
find ~/.humanlayer/dev -name "daemon-*.db" -type f -mtime +10 -delete -print | sed 's/^/Deleted database: /'; \
fi
@# Clean old dev logs
@if [ -d ~/.humanlayer/logs ]; then \
find ~/.humanlayer/logs -name "*-dev-*.log" -type f -mtime +10 -delete -print | sed 's/^/Deleted log: /'; \
fi
@echo "Cleanup complete."
# Build dev daemon binary
.PHONY: daemon-dev-build
daemon-dev-build:
cd hld && go build -o hld-dev ./cmd/hld
@echo "Built dev daemon binary: hld/hld-dev"
# Run dev daemon with persistent dev database
.PHONY: daemon-dev
daemon-dev: daemon-dev-build
@mkdir -p ~/.humanlayer/logs
$(eval TIMESTAMP := $(shell date +%Y-%m-%d-%H-%M-%S))
@echo "Starting dev daemon with database: ~/.humanlayer/daemon-dev.db"
echo "$(TIMESTAMP) starting dev daemon in $$(pwd)" > ~/.humanlayer/logs/daemon-dev-$(TIMESTAMP).log
cd hld && HUMANLAYER_DATABASE_PATH=~/.humanlayer/daemon-dev.db \
HUMANLAYER_DAEMON_SOCKET=~/.humanlayer/daemon-dev.sock \
HUMANLAYER_DAEMON_HTTP_PORT=0 \
HUMANLAYER_DAEMON_VERSION_OVERRIDE=$$(git branch --show-current) \
./run-with-logging.sh ~/.humanlayer/logs/daemon-dev-$(TIMESTAMP).log ./hld-dev
# Run dev WUI with custom socket
.PHONY: wui-dev
wui-dev:
cd humanlayer-wui && HUMANLAYER_DAEMON_SOCKET=~/.humanlayer/daemon-dev.sock bun run tauri dev
# Alias for wui-dev that ensures daemon is built first
.PHONY: codelayer-dev
codelayer-dev: daemon-dev-build wui-dev
# Show current dev environment setup
.PHONY: dev-status
dev-status:
@echo "=== Development Environment Status ==="
@echo "Dev Socket: ~/.humanlayer/daemon-dev.sock"
@echo "Nightly Socket: ~/.humanlayer/daemon.sock"
@echo ""
@echo "Dev Databases:"
@ls -la ~/.humanlayer/dev/*.db 2>/dev/null || echo " No dev databases found"
@echo ""
@echo "Active daemons:"
@ps aux | grep -E "hld(-dev|-nightly)?$$" | grep -v grep || echo " No daemons running"