agentlint 2.5.2__tar.gz → 2.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentlint-2.5.2 → agentlint-2.5.3}/CHANGELOG.md +19 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/PKG-INFO +1 -1
- {agentlint-2.5.2 → agentlint-2.5.3}/pyproject.toml +1 -1
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/__init__.py +1 -1
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/queue.py +18 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/cli.py +67 -4
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_agentchute_policy_queue_sync.py +30 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_cli.py +124 -8
- {agentlint-2.5.2 → agentlint-2.5.3}/uv.lock +1 -1
- {agentlint-2.5.2 → agentlint-2.5.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.github/workflows/ci.yml +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.github/workflows/publish.yml +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.gitignore +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/.python-version +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/CODE_OF_CONDUCT.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/CONTRIBUTING.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/LICENSE +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/README.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/SECURITY.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/agentlint.yml +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/agentlint.yml.example +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/codecov.yml +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/README.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/adapters.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/architecture.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/configuration.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/custom-rules.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/mcp.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/migration-v2.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-claude.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-codex.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-continue.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-cursor.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-gemini.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-generic.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-grok.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-kimi.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-mcp.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/setup-openai.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/docs/subagent-safety.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/.claude-plugin/plugin.json +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/agents/doctor.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/agents/fix.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/agents/security-audit.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/bin/resolve-and-run.sh +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/commands/lint-config.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/commands/lint-status.md +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/hooks/hooks.json +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/plugin/mcp.json +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/__main__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/_utils.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/base.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/claude.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/codex.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/continue_dev.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/cursor.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/gemini.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/generic.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/grok.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/kimi.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/mcp.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/adapters/openai_agents.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/client.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/feeds.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/policy.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/settings.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agentchute/sync.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/agents_md.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/circuit_breaker.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/config.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/core/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/core/models.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/detector.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/engine.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/filters.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/base.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/claude_hooks.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/cursor_hooks.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/gemini_hooks.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/formats/plain_json.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/mcp_server.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/models.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/_dangerous_patterns.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/bash_rate_limiter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/cloud_infra_mutation.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/cloud_paid_resource_creation.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/cloud_resource_deletion.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/cross_account_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/destructive_confirmation_gate.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/docker_volume_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/dry_run_required.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/network_firewall_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/operation_journal.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/package_manager_in_chroot.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/production_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/remote_boot_partition_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/remote_chroot_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/ssh_destructive_command_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/subagent_safety_briefing.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/subagent_transcript_audit.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/autopilot/system_scheduler_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/_helpers.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/a11y_form_labels.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/a11y_heading_hierarchy.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/a11y_image_alt.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/a11y_interactive_elements.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/mobile_responsive_patterns.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/mobile_touch_targets.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/style_focus_visible.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/frontend/style_no_arbitrary_values.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_bare_except.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_dangerous_migration.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_sql_injection.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_unnecessary_async.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_unsafe_shell.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/python/no_wildcard_import.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/commit_message_format.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/naming_conventions.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/no_dead_imports.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/no_error_handling_removal.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/no_file_creation_sprawl.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/no_large_diff.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/quality/self_review_prompt.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/react/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/react/react_empty_state.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/react/react_lazy_loading.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/react/react_query_loading_state.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/env_credential_reference.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_bash_file_write.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_blocked_domain_fetch.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_compromised_action.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_leaked_secret_pattern.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_malicious_url_fetch.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/security/no_network_exfil.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/_helpers.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/seo_open_graph.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/seo_page_metadata.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/seo_semantic_html.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/seo/seo_structured_data.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/cicd_pipeline_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/cli_integration.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/dependency_hygiene.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/drift_detector.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/file_scope.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/git_checkpoint.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/max_file_size.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_compromised_dependency.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_debug_artifacts.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_destructive_commands.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_env_commit.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_force_push.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_nvd_critical_cve_install.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_push_to_main.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_secrets.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_skip_hooks.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_test_weakening.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_todo_left.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_vulnerable_import.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/no_vulnerable_version_install.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/package_publish_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/test_with_changes.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/token_budget.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/packs/universal/token_burn_against_team_budget.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/py.typed +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/recorder.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/reporter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/session.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/setup.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/template.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/utils/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/utils/bash.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/utils/git.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/src/agentlint/utils/paths.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/conftest.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/__init__.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/security/test_no_blocked_domain_fetch.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/security/test_no_compromised_action.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/security/test_no_leaked_secret_pattern.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/security/test_no_malicious_url_fetch.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_autopilot_integration.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_autopilot_pack_loads.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_autopilot_post.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_autopilot_pre.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_cicd_pipeline_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_cloud_infra_mutation.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_cloud_paid_resource_creation.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_cloud_resource_deletion.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_docker_volume_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_edge_cases.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_env_credential_reference.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_frontend.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_git_checkpoint.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_network_firewall_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_no_compromised_dependency.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_no_nvd_critical_cve_install.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_no_vulnerable_import.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_no_vulnerable_version_install.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_package_manager_in_chroot.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_package_publish_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_python_post.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_python_pre.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_quality_post.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_quality_pre.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_quality_stop.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_react.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_remote_boot_partition_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_remote_chroot_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_security_pre.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_seo.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_ssh_destructive_command_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_subagent_safety_briefing.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_subagent_transcript_audit.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_system_scheduler_guard.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_token_budget.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_token_burn_against_team_budget.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_universal_post.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_universal_pre.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/packs/test_universal_stop.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_adapter_utils.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_agentchute_client.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_agentchute_feeds.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_agents_md.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_bash_utils.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_ci.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_circuit_breaker.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_cli_integration.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_codex_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_config.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_continue_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_cursor_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_custom_rules.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_detector.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_engine.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_file_scope.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_filters.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_gemini_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_generic_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_git.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_git_utils_checkpoint.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_grok_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_integration.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_kimi_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_main_module.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_mcp_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_mcp_server.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_models.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_openai_adapter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_packs_loader.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_paths_utils.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_quality_new_rules.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_recorder.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_reporter.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_rule_inventory.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_session.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_setup.py +0 -0
- {agentlint-2.5.2 → agentlint-2.5.3}/tests/test_template.py +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.5.3 (2026-05-25) — AgentChute Queue Baseline
|
|
4
|
+
|
|
5
|
+
This patch release fixes AgentChute onboarding for existing AgentLint users
|
|
6
|
+
with local event history.
|
|
7
|
+
|
|
8
|
+
- Fixes `agentlint queue flush`, which could crash because the CLI wrapper did
|
|
9
|
+
not pass the newer queue batch/time-budget arguments.
|
|
10
|
+
- Baselines the local AgentChute queue when a machine is paired with a new
|
|
11
|
+
license key, so pre-pairing local history is not uploaded as fresh dashboard
|
|
12
|
+
activity.
|
|
13
|
+
- Adds `agentlint queue discard-pending --yes` for support cases where a user
|
|
14
|
+
intentionally wants to advance the local queue cursor without uploading
|
|
15
|
+
pending events.
|
|
16
|
+
- Improves `agentlint policy explain` by listing active rule IDs, making policy
|
|
17
|
+
template debugging easier.
|
|
18
|
+
- Adds regression coverage for the `block-curl-sh` policy template simulation.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
3
22
|
## v2.5.2 (2026-05-25) — AgentChute Credential Persistence
|
|
4
23
|
|
|
5
24
|
This patch release fixes the first-run AgentChute pairing flow so users can run
|
|
@@ -89,6 +89,24 @@ def enqueue_event(event: dict, *, session_key: str, config: Any | None = None) -
|
|
|
89
89
|
return event_id
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
def mark_existing_events_delivered() -> int:
|
|
93
|
+
"""Advance delivery to the current queue tail without deleting events.
|
|
94
|
+
|
|
95
|
+
This is used when AgentChute is first paired, or when the local license key
|
|
96
|
+
changes. Local history can predate the cloud workspace and should not be
|
|
97
|
+
uploaded as new team activity.
|
|
98
|
+
"""
|
|
99
|
+
lines = _read_lines()
|
|
100
|
+
total = len(lines)
|
|
101
|
+
cursor = int(_load_json(_cursor_path(), {"offset": 0}).get("offset", 0))
|
|
102
|
+
if cursor >= total:
|
|
103
|
+
_clear_retry()
|
|
104
|
+
return 0
|
|
105
|
+
_save_json(_cursor_path(), {"offset": total})
|
|
106
|
+
_clear_retry()
|
|
107
|
+
return total - cursor
|
|
108
|
+
|
|
109
|
+
|
|
92
110
|
def trigger_background_flush(config: Any | None = None) -> None:
|
|
93
111
|
"""Start a detached flusher process if AgentChute is enabled.
|
|
94
112
|
|
|
@@ -91,6 +91,26 @@ def _agentchute_policy_metadata() -> dict:
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
|
|
94
|
+
def _baseline_agentchute_queue_if_new_key(previous_key: str | None, next_key: str) -> int:
|
|
95
|
+
if previous_key == next_key:
|
|
96
|
+
return 0
|
|
97
|
+
try:
|
|
98
|
+
from agentlint.agentchute.queue import mark_existing_events_delivered
|
|
99
|
+
|
|
100
|
+
return mark_existing_events_delivered()
|
|
101
|
+
except Exception:
|
|
102
|
+
logger.debug("Failed to baseline AgentChute queue", exc_info=True)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _echo_queue_baseline(skipped: int) -> None:
|
|
107
|
+
if skipped:
|
|
108
|
+
click.echo(
|
|
109
|
+
f"AgentChute queue: skipped {skipped} pre-pairing event(s); "
|
|
110
|
+
"future events will upload normally"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
94
114
|
def _codex_hooks_enabled() -> bool:
|
|
95
115
|
"""Return whether current Codex native hooks are enabled in config.toml."""
|
|
96
116
|
config_path = Path.home() / ".codex" / "config.toml"
|
|
@@ -1145,6 +1165,7 @@ def onboard(
|
|
|
1145
1165
|
click.echo(f"Would persist AgentChute credentials to {local_credentials_path()}")
|
|
1146
1166
|
click.echo(f"Would persist AgentChute env vars to {_default_shell_profile()}")
|
|
1147
1167
|
else:
|
|
1168
|
+
skipped = _baseline_agentchute_queue_if_new_key(existing_key, team_key)
|
|
1148
1169
|
credentials_path = save_local_credentials(
|
|
1149
1170
|
api_url=api_url,
|
|
1150
1171
|
license_key=team_key,
|
|
@@ -1156,6 +1177,7 @@ def onboard(
|
|
|
1156
1177
|
os.environ["AGENTCHUTE_ENABLED"] = "true"
|
|
1157
1178
|
click.echo(f"Saved AgentChute credentials to {credentials_path}")
|
|
1158
1179
|
click.echo(f"Saved AgentChute env vars to {profile}")
|
|
1180
|
+
_echo_queue_baseline(skipped)
|
|
1159
1181
|
|
|
1160
1182
|
for platform in selected:
|
|
1161
1183
|
state, path = _hook_status(platform, project_dir)
|
|
@@ -1399,9 +1421,10 @@ def env_group():
|
|
|
1399
1421
|
@click.option("--profile", default=None, help="Shell profile to update")
|
|
1400
1422
|
def env_install(team_key: str, api_url: str | None, profile: str | None):
|
|
1401
1423
|
"""Persist AgentChute env vars into the shell profile."""
|
|
1402
|
-
from agentlint.agentchute.settings import get_api_url, save_local_credentials
|
|
1424
|
+
from agentlint.agentchute.settings import get_api_url, get_license_key, save_local_credentials
|
|
1403
1425
|
|
|
1404
1426
|
resolved_api_url = api_url or get_api_url()
|
|
1427
|
+
skipped = _baseline_agentchute_queue_if_new_key(get_license_key(), team_key)
|
|
1405
1428
|
credentials_path = save_local_credentials(
|
|
1406
1429
|
api_url=resolved_api_url,
|
|
1407
1430
|
license_key=team_key,
|
|
@@ -1417,6 +1440,7 @@ def env_install(team_key: str, api_url: str | None, profile: str | None):
|
|
|
1417
1440
|
os.environ["AGENTCHUTE_ENABLED"] = "true"
|
|
1418
1441
|
click.echo(f"Saved AgentChute credentials to {credentials_path}")
|
|
1419
1442
|
click.echo(f"Saved AgentChute env vars to {path}")
|
|
1443
|
+
_echo_queue_baseline(skipped)
|
|
1420
1444
|
click.echo("This terminal can use AgentChute immediately.")
|
|
1421
1445
|
click.echo("Future terminals can also load env vars with: source " + str(path))
|
|
1422
1446
|
|
|
@@ -1478,10 +1502,11 @@ def env_remove(profile: str | None):
|
|
|
1478
1502
|
@click.option("--manual", is_flag=True, help="Paste a license key instead of using dashboard pairing")
|
|
1479
1503
|
def login(dashboard_url: str | None, api_url: str | None, project_dir: str | None, manual: bool):
|
|
1480
1504
|
"""Pair this machine with AgentChute through the dashboard."""
|
|
1481
|
-
from agentlint.agentchute.settings import get_api_url, save_local_credentials
|
|
1505
|
+
from agentlint.agentchute.settings import get_api_url, get_license_key, save_local_credentials
|
|
1482
1506
|
|
|
1483
1507
|
project_dir = _resolve_project_dir(project_dir)
|
|
1484
1508
|
api_url = api_url or get_api_url()
|
|
1509
|
+
existing_key = get_license_key()
|
|
1485
1510
|
dashboard_url = dashboard_url or (
|
|
1486
1511
|
"http://localhost:3001/dashboard/settings/license"
|
|
1487
1512
|
if "localhost" in api_url
|
|
@@ -1532,6 +1557,7 @@ def login(dashboard_url: str | None, api_url: str | None, project_dir: str | Non
|
|
|
1532
1557
|
webbrowser.open(dashboard_url)
|
|
1533
1558
|
team_key = click.prompt("Paste the AgentChute license key", hide_input=True)
|
|
1534
1559
|
|
|
1560
|
+
skipped = _baseline_agentchute_queue_if_new_key(existing_key, team_key)
|
|
1535
1561
|
credentials_path = save_local_credentials(
|
|
1536
1562
|
api_url=api_url,
|
|
1537
1563
|
license_key=team_key,
|
|
@@ -1546,6 +1572,7 @@ def login(dashboard_url: str | None, api_url: str | None, project_dir: str | Non
|
|
|
1546
1572
|
_ensure_agentchute_enabled_config(config_path)
|
|
1547
1573
|
click.echo(f"Paired. AgentChute credentials saved to {credentials_path}")
|
|
1548
1574
|
click.echo(f"Shell env vars saved to {profile}")
|
|
1575
|
+
_echo_queue_baseline(skipped)
|
|
1549
1576
|
click.echo("This terminal can use AgentChute immediately.")
|
|
1550
1577
|
|
|
1551
1578
|
|
|
@@ -1569,9 +1596,41 @@ def queue_status_command():
|
|
|
1569
1596
|
|
|
1570
1597
|
@queue_group.command("flush")
|
|
1571
1598
|
@click.option("--max-events", default=None, type=int, help="Maximum events to flush")
|
|
1572
|
-
|
|
1599
|
+
@click.option("--batch-size", default=50, type=int, help="Maximum events per API batch")
|
|
1600
|
+
@click.option("--time-budget", default=3.0, type=float, help="Maximum seconds to spend flushing")
|
|
1601
|
+
def queue_flush_command(max_events: int | None, batch_size: int, time_budget: float):
|
|
1573
1602
|
"""Flush queued AgentChute events now."""
|
|
1574
|
-
_flush_agentchute_queue(
|
|
1603
|
+
_flush_agentchute_queue(
|
|
1604
|
+
max_events=max_events,
|
|
1605
|
+
batch_size=batch_size,
|
|
1606
|
+
time_budget=time_budget,
|
|
1607
|
+
dry_run=False,
|
|
1608
|
+
background=False,
|
|
1609
|
+
)
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
@queue_group.command("discard-pending")
|
|
1613
|
+
@click.option("--yes", is_flag=True, help="Skip confirmation")
|
|
1614
|
+
def queue_discard_pending_command(yes: bool):
|
|
1615
|
+
"""Mark pending AgentChute events delivered without uploading them."""
|
|
1616
|
+
from agentlint.agentchute.queue import mark_existing_events_delivered, queue_status
|
|
1617
|
+
|
|
1618
|
+
status_data = queue_status()
|
|
1619
|
+
pending = int(status_data.get("pending", 0) or 0)
|
|
1620
|
+
if pending <= 0:
|
|
1621
|
+
click.echo("AgentChute queue: no pending events.")
|
|
1622
|
+
return
|
|
1623
|
+
if not yes and not click.confirm(
|
|
1624
|
+
f"Discard {pending} pending AgentChute event(s) without uploading?",
|
|
1625
|
+
default=False,
|
|
1626
|
+
):
|
|
1627
|
+
click.echo("AgentChute queue unchanged.")
|
|
1628
|
+
return
|
|
1629
|
+
skipped = mark_existing_events_delivered()
|
|
1630
|
+
click.echo(
|
|
1631
|
+
f"AgentChute queue: discarded {skipped} pending event(s); "
|
|
1632
|
+
"future events will upload normally"
|
|
1633
|
+
)
|
|
1575
1634
|
|
|
1576
1635
|
|
|
1577
1636
|
@queue_group.command("inspect")
|
|
@@ -2325,6 +2384,7 @@ def policy_explain_command(output_format: str):
|
|
|
2325
2384
|
rules = [rule for rule in policy.get("rules") or [] if isinstance(rule, dict)]
|
|
2326
2385
|
active_rules = [rule for rule in rules if rule.get("enabled", True) is not False]
|
|
2327
2386
|
locked_rules = [rule for rule in active_rules if rule.get("locked") is True]
|
|
2387
|
+
active_rule_ids = [str(rule.get("id")) for rule in active_rules if rule.get("id")]
|
|
2328
2388
|
packs = required_packs(policy)
|
|
2329
2389
|
missing = missing_required_packs(policy)
|
|
2330
2390
|
cloud_feeds = [
|
|
@@ -2339,6 +2399,7 @@ def policy_explain_command(output_format: str):
|
|
|
2339
2399
|
"updated_at": policy.get("updated_at"),
|
|
2340
2400
|
"organization_id": policy.get("organization_id"),
|
|
2341
2401
|
"rules": len(active_rules),
|
|
2402
|
+
"rule_ids": active_rule_ids,
|
|
2342
2403
|
"locked_rules": len(locked_rules),
|
|
2343
2404
|
"required_packs": len(packs),
|
|
2344
2405
|
"cloud_feeds": cloud_feeds,
|
|
@@ -2353,6 +2414,8 @@ def policy_explain_command(output_format: str):
|
|
|
2353
2414
|
if policy.get("updated_at"):
|
|
2354
2415
|
click.echo(f"Updated: {policy['updated_at']}")
|
|
2355
2416
|
click.echo(f"Rules: {len(active_rules)} active, {len(locked_rules)} locked")
|
|
2417
|
+
if active_rule_ids:
|
|
2418
|
+
click.echo(f"Rule IDs: {', '.join(active_rule_ids[:10])}")
|
|
2356
2419
|
click.echo(f"Required packs: {len(packs)}")
|
|
2357
2420
|
if cloud_feeds:
|
|
2358
2421
|
click.echo(f"Cloud feeds: {', '.join(cloud_feeds)}")
|
|
@@ -258,6 +258,36 @@ def test_queue_enqueue_flush_dry_run_and_status(tmp_path, monkeypatch):
|
|
|
258
258
|
assert not (tmp_path / "flush.lock").exists()
|
|
259
259
|
|
|
260
260
|
|
|
261
|
+
def test_queue_mark_existing_events_delivered_preserves_future_pending(tmp_path, monkeypatch):
|
|
262
|
+
from agentlint.agentchute import queue
|
|
263
|
+
|
|
264
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(tmp_path))
|
|
265
|
+
(tmp_path / "queue.jsonl").write_text(
|
|
266
|
+
"\n".join(
|
|
267
|
+
[
|
|
268
|
+
json.dumps({"event_id": "old-1", "event": {"n": 1}}),
|
|
269
|
+
json.dumps({"event_id": "old-2", "event": {"n": 2}}),
|
|
270
|
+
]
|
|
271
|
+
)
|
|
272
|
+
+ "\n",
|
|
273
|
+
encoding="utf-8",
|
|
274
|
+
)
|
|
275
|
+
queue._save_json(tmp_path / "retry.json", {"failures": 2, "next_attempt_at": 123})
|
|
276
|
+
|
|
277
|
+
skipped = queue.mark_existing_events_delivered()
|
|
278
|
+
|
|
279
|
+
assert skipped == 2
|
|
280
|
+
assert queue.queue_status()["pending"] == 0
|
|
281
|
+
assert json.loads((tmp_path / "cursor.json").read_text())["offset"] == 2
|
|
282
|
+
assert not (tmp_path / "retry.json").exists()
|
|
283
|
+
assert queue.mark_existing_events_delivered() == 0
|
|
284
|
+
|
|
285
|
+
with open(tmp_path / "queue.jsonl", "a", encoding="utf-8") as f:
|
|
286
|
+
f.write(json.dumps({"event_id": "new-1", "event": {"n": 3}}) + "\n")
|
|
287
|
+
|
|
288
|
+
assert queue.queue_status()["pending"] == 1
|
|
289
|
+
|
|
290
|
+
|
|
261
291
|
def test_queue_flush_dry_run_counts_limited_poison_entries(tmp_path, monkeypatch):
|
|
262
292
|
from agentlint.agentchute import queue
|
|
263
293
|
|
|
@@ -19,7 +19,7 @@ class TestMainCommand:
|
|
|
19
19
|
result = runner.invoke(main, ["--version"])
|
|
20
20
|
|
|
21
21
|
assert result.exit_code == 0
|
|
22
|
-
assert result.output.strip() == "agentlint 2.5.
|
|
22
|
+
assert result.output.strip() == "agentlint 2.5.3"
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class TestCheckCommand:
|
|
@@ -1106,6 +1106,48 @@ class TestMagicalUxCommands:
|
|
|
1106
1106
|
explain = runner.invoke(main, ["policy", "explain"])
|
|
1107
1107
|
assert "Updated: 2026-05-19T12:00:00Z" in status.output
|
|
1108
1108
|
assert "Updated: 2026-05-19T12:00:00Z" in explain.output
|
|
1109
|
+
assert "Rule IDs: org-block-forbidden-domain" in explain.output
|
|
1110
|
+
|
|
1111
|
+
def test_curl_pipe_policy_template_matches_cached_policy(self, tmp_path, monkeypatch) -> None:
|
|
1112
|
+
policy_dir = tmp_path / "policy"
|
|
1113
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_POLICY_DIR", str(policy_dir))
|
|
1114
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(tmp_path / "queue"))
|
|
1115
|
+
monkeypatch.setenv("AGENTCHUTE_LICENSE_KEY", "ac_team_test_secret")
|
|
1116
|
+
(tmp_path / "agentlint.yml").write_text(
|
|
1117
|
+
"packs:\n - universal\nagentchute:\n enabled: true\n"
|
|
1118
|
+
)
|
|
1119
|
+
policy_dir.mkdir()
|
|
1120
|
+
(policy_dir / "policy.json").write_text(json.dumps({
|
|
1121
|
+
"version": 1,
|
|
1122
|
+
"updated_at": "2026-05-19T12:00:00Z",
|
|
1123
|
+
"rules": [{
|
|
1124
|
+
"id": "org-block-curl-pipe-sh",
|
|
1125
|
+
"enabled": True,
|
|
1126
|
+
"event": "PreToolUse",
|
|
1127
|
+
"tool": "Bash",
|
|
1128
|
+
"severity": "error",
|
|
1129
|
+
"match": {
|
|
1130
|
+
"field": "command",
|
|
1131
|
+
"operator": "contains",
|
|
1132
|
+
"value": "| sh",
|
|
1133
|
+
},
|
|
1134
|
+
"message": "Blocked curl piped to shell.",
|
|
1135
|
+
}],
|
|
1136
|
+
}))
|
|
1137
|
+
|
|
1138
|
+
result = CliRunner().invoke(
|
|
1139
|
+
main,
|
|
1140
|
+
[
|
|
1141
|
+
"test-policy",
|
|
1142
|
+
"block-curl-sh",
|
|
1143
|
+
"--project-dir",
|
|
1144
|
+
str(tmp_path),
|
|
1145
|
+
"--no-refresh",
|
|
1146
|
+
],
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
assert result.exit_code == 0
|
|
1150
|
+
assert "Matched: org-block-curl-pipe-sh (error)" in result.output
|
|
1109
1151
|
|
|
1110
1152
|
def test_policy_template_unknown_name_errors(self) -> None:
|
|
1111
1153
|
result = CliRunner().invoke(main, ["test-policy", "unknown-template", "--no-refresh"])
|
|
@@ -1248,8 +1290,17 @@ class TestMagicalUxCommands:
|
|
|
1248
1290
|
|
|
1249
1291
|
def test_login_persists_key_and_enables_existing_config(self, tmp_path, monkeypatch) -> None:
|
|
1250
1292
|
profile = tmp_path / ".zshrc"
|
|
1293
|
+
queue_dir = tmp_path / "queue"
|
|
1294
|
+
queue_dir.mkdir()
|
|
1295
|
+
(queue_dir / "queue.jsonl").write_text(
|
|
1296
|
+
json.dumps({"event_id": "old-1", "event": {}}) + "\n"
|
|
1297
|
+
+ json.dumps({"event_id": "old-2", "event": {}}) + "\n",
|
|
1298
|
+
encoding="utf-8",
|
|
1299
|
+
)
|
|
1251
1300
|
(tmp_path / "agentlint.yml").write_text("packs:\n - universal\n")
|
|
1252
1301
|
monkeypatch.setenv("AGENTLINT_SHELL_PROFILE", str(profile))
|
|
1302
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(queue_dir))
|
|
1303
|
+
monkeypatch.delenv("AGENTCHUTE_LICENSE_KEY", raising=False)
|
|
1253
1304
|
monkeypatch.setattr("webbrowser.open", lambda _url: True)
|
|
1254
1305
|
runner = CliRunner()
|
|
1255
1306
|
|
|
@@ -1265,18 +1316,20 @@ class TestMagicalUxCommands:
|
|
|
1265
1316
|
"--api-url",
|
|
1266
1317
|
"http://localhost:8000",
|
|
1267
1318
|
],
|
|
1268
|
-
input="
|
|
1319
|
+
input="ac_team_login_baseline\n",
|
|
1269
1320
|
)
|
|
1270
1321
|
|
|
1271
1322
|
assert result.exit_code == 0
|
|
1272
1323
|
assert "Paired. AgentChute credentials saved" in result.output
|
|
1273
1324
|
assert "Shell env vars saved" in result.output
|
|
1274
1325
|
assert "This terminal can use AgentChute immediately." in result.output
|
|
1275
|
-
assert "
|
|
1326
|
+
assert "AgentChute queue: skipped 2 pre-pairing event(s)" in result.output
|
|
1327
|
+
assert json.loads((queue_dir / "cursor.json").read_text())["offset"] == 2
|
|
1328
|
+
assert "AGENTCHUTE_LICENSE_KEY=ac_team_login_baseline" in profile.read_text()
|
|
1276
1329
|
assert "agentchute:" in (tmp_path / "agentlint.yml").read_text()
|
|
1277
1330
|
from agentlint.agentchute.settings import load_local_credentials
|
|
1278
1331
|
|
|
1279
|
-
assert load_local_credentials()["license_key"] == "
|
|
1332
|
+
assert load_local_credentials()["license_key"] == "ac_team_login_baseline"
|
|
1280
1333
|
|
|
1281
1334
|
def test_login_pairs_through_dashboard(self, tmp_path, monkeypatch) -> None:
|
|
1282
1335
|
profile = tmp_path / ".zshrc"
|
|
@@ -1464,17 +1517,80 @@ class TestMagicalUxCommands:
|
|
|
1464
1517
|
def test_queue_flush_command_delegates_to_flush_helper(self, monkeypatch) -> None:
|
|
1465
1518
|
import agentlint.cli
|
|
1466
1519
|
|
|
1467
|
-
calls: list[
|
|
1520
|
+
calls: list[dict] = []
|
|
1468
1521
|
monkeypatch.setattr(
|
|
1469
1522
|
agentlint.cli,
|
|
1470
1523
|
"_flush_agentchute_queue",
|
|
1471
|
-
lambda
|
|
1524
|
+
lambda **kwargs: calls.append(kwargs),
|
|
1472
1525
|
)
|
|
1473
1526
|
|
|
1474
|
-
result = CliRunner().invoke(
|
|
1527
|
+
result = CliRunner().invoke(
|
|
1528
|
+
main,
|
|
1529
|
+
["queue", "flush", "--max-events", "7", "--batch-size", "3", "--time-budget", "1.5"],
|
|
1530
|
+
)
|
|
1475
1531
|
|
|
1476
1532
|
assert result.exit_code == 0
|
|
1477
|
-
assert calls == [
|
|
1533
|
+
assert calls == [{
|
|
1534
|
+
"max_events": 7,
|
|
1535
|
+
"batch_size": 3,
|
|
1536
|
+
"time_budget": 1.5,
|
|
1537
|
+
"dry_run": False,
|
|
1538
|
+
"background": False,
|
|
1539
|
+
}]
|
|
1540
|
+
|
|
1541
|
+
def test_queue_discard_pending_advances_cursor(self, tmp_path, monkeypatch) -> None:
|
|
1542
|
+
queue_dir = tmp_path / "queue"
|
|
1543
|
+
queue_dir.mkdir()
|
|
1544
|
+
(queue_dir / "queue.jsonl").write_text(
|
|
1545
|
+
json.dumps({"event_id": "old-1", "event": {}}) + "\n"
|
|
1546
|
+
+ json.dumps({"event_id": "old-2", "event": {}}) + "\n",
|
|
1547
|
+
encoding="utf-8",
|
|
1548
|
+
)
|
|
1549
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(queue_dir))
|
|
1550
|
+
|
|
1551
|
+
result = CliRunner().invoke(main, ["queue", "discard-pending", "--yes"])
|
|
1552
|
+
|
|
1553
|
+
assert result.exit_code == 0
|
|
1554
|
+
assert "AgentChute queue: discarded 2 pending event(s)" in result.output
|
|
1555
|
+
assert json.loads((queue_dir / "cursor.json").read_text())["offset"] == 2
|
|
1556
|
+
|
|
1557
|
+
def test_queue_discard_pending_can_cancel(self, tmp_path, monkeypatch) -> None:
|
|
1558
|
+
queue_dir = tmp_path / "queue"
|
|
1559
|
+
queue_dir.mkdir()
|
|
1560
|
+
(queue_dir / "queue.jsonl").write_text(
|
|
1561
|
+
json.dumps({"event_id": "old-1", "event": {}}) + "\n",
|
|
1562
|
+
encoding="utf-8",
|
|
1563
|
+
)
|
|
1564
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(queue_dir))
|
|
1565
|
+
|
|
1566
|
+
result = CliRunner().invoke(main, ["queue", "discard-pending"], input="n\n")
|
|
1567
|
+
|
|
1568
|
+
assert result.exit_code == 0
|
|
1569
|
+
assert "AgentChute queue unchanged." in result.output
|
|
1570
|
+
assert not (queue_dir / "cursor.json").exists()
|
|
1571
|
+
|
|
1572
|
+
def test_queue_discard_pending_reports_empty_queue(self, tmp_path, monkeypatch) -> None:
|
|
1573
|
+
monkeypatch.setenv("AGENTLINT_AGENTCHUTE_QUEUE_DIR", str(tmp_path / "queue"))
|
|
1574
|
+
|
|
1575
|
+
result = CliRunner().invoke(main, ["queue", "discard-pending", "--yes"])
|
|
1576
|
+
|
|
1577
|
+
assert result.exit_code == 0
|
|
1578
|
+
assert "AgentChute queue: no pending events." in result.output
|
|
1579
|
+
|
|
1580
|
+
def test_queue_baseline_failure_is_non_fatal(self, monkeypatch) -> None:
|
|
1581
|
+
import agentlint.agentchute.queue
|
|
1582
|
+
import agentlint.cli
|
|
1583
|
+
|
|
1584
|
+
def fail_baseline() -> int:
|
|
1585
|
+
raise OSError("queue locked")
|
|
1586
|
+
|
|
1587
|
+
monkeypatch.setattr(
|
|
1588
|
+
agentlint.agentchute.queue,
|
|
1589
|
+
"mark_existing_events_delivered",
|
|
1590
|
+
fail_baseline,
|
|
1591
|
+
)
|
|
1592
|
+
|
|
1593
|
+
assert agentlint.cli._baseline_agentchute_queue_if_new_key("old-key", "new-key") == 0
|
|
1478
1594
|
|
|
1479
1595
|
def test_status_reports_cached_policy_retry_and_error(self, tmp_path, monkeypatch) -> None:
|
|
1480
1596
|
monkeypatch.setenv("AGENTCHUTE_ENABLED", "true")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|