afriend 0.6.1__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.
- afriend-0.6.1/LICENSE +21 -0
- afriend-0.6.1/PKG-INFO +579 -0
- afriend-0.6.1/README.md +555 -0
- afriend-0.6.1/VERSION +1 -0
- afriend-0.6.1/pyproject.toml +156 -0
- afriend-0.6.1/setup.cfg +4 -0
- afriend-0.6.1/src/afriend/__init__.py +34 -0
- afriend-0.6.1/src/afriend/__main__.py +8 -0
- afriend-0.6.1/src/afriend/adapters.py +501 -0
- afriend-0.6.1/src/afriend/assets/__init__.py +0 -0
- afriend-0.6.1/src/afriend/assets/adapters/agy.toml +73 -0
- afriend-0.6.1/src/afriend/assets/adapters/claude.toml +101 -0
- afriend-0.6.1/src/afriend/assets/adapters/codex.toml +114 -0
- afriend-0.6.1/src/afriend/assets/adapters/ollama.toml +41 -0
- afriend-0.6.1/src/afriend/assets/adapters/opencode.toml +116 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/afriend/SKILL.md +418 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/afriend/references/ledger.md +157 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/afriend/references/modes.md +481 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/afriend/references/troubleshooting.md +255 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/configure/SKILL.md +56 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/resolve/SKILL.md +42 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/review/SKILL.md +32 -0
- afriend-0.6.1/src/afriend/assets/entrypoints/status/SKILL.md +39 -0
- afriend-0.6.1/src/afriend/assets/harnesses/agy/afriend-reviewer.md +19 -0
- afriend-0.6.1/src/afriend/assets/lenses/assumptions.md +21 -0
- afriend-0.6.1/src/afriend/assets/lenses/ops.md +19 -0
- afriend-0.6.1/src/afriend/assets/lenses/scope.md +19 -0
- afriend-0.6.1/src/afriend/assets/lenses/security.md +20 -0
- afriend-0.6.1/src/afriend/assets/lenses/spec-vs-reality.md +20 -0
- afriend-0.6.1/src/afriend/assets/lenses/testability.md +19 -0
- afriend-0.6.1/src/afriend/authority.py +130 -0
- afriend-0.6.1/src/afriend/ceilings.py +190 -0
- afriend-0.6.1/src/afriend/childenv.py +154 -0
- afriend-0.6.1/src/afriend/claimschema.py +159 -0
- afriend-0.6.1/src/afriend/cli.py +92 -0
- afriend-0.6.1/src/afriend/cliargs.py +508 -0
- afriend-0.6.1/src/afriend/commands/__init__.py +0 -0
- afriend-0.6.1/src/afriend/commands/checkpoint.py +336 -0
- afriend-0.6.1/src/afriend/commands/confinement.py +97 -0
- afriend-0.6.1/src/afriend/commands/critique.py +347 -0
- afriend-0.6.1/src/afriend/commands/crossexam.py +732 -0
- afriend-0.6.1/src/afriend/commands/doctor.py +222 -0
- afriend-0.6.1/src/afriend/commands/environment.py +340 -0
- afriend-0.6.1/src/afriend/commands/exits.py +22 -0
- afriend-0.6.1/src/afriend/commands/friends.py +396 -0
- afriend-0.6.1/src/afriend/commands/haltstate.py +229 -0
- afriend-0.6.1/src/afriend/commands/init.py +426 -0
- afriend-0.6.1/src/afriend/commands/judging.py +93 -0
- afriend-0.6.1/src/afriend/commands/legacyroles.py +328 -0
- afriend-0.6.1/src/afriend/commands/profiles.py +97 -0
- afriend-0.6.1/src/afriend/commands/providers.py +43 -0
- afriend-0.6.1/src/afriend/commands/resolve.py +312 -0
- afriend-0.6.1/src/afriend/commands/resume.py +737 -0
- afriend-0.6.1/src/afriend/commands/resumevalidation.py +94 -0
- afriend-0.6.1/src/afriend/commands/run.py +762 -0
- afriend-0.6.1/src/afriend/commands/runmeta.py +777 -0
- afriend-0.6.1/src/afriend/commands/runmeta_migration.py +53 -0
- afriend-0.6.1/src/afriend/commands/runmeta_outcome.py +110 -0
- afriend-0.6.1/src/afriend/commands/runmeta_restore.py +193 -0
- afriend-0.6.1/src/afriend/commands/scopeanchor.py +67 -0
- afriend-0.6.1/src/afriend/commands/setup.py +136 -0
- afriend-0.6.1/src/afriend/commands/status.py +480 -0
- afriend-0.6.1/src/afriend/contracts.py +51 -0
- afriend-0.6.1/src/afriend/dispatch.py +497 -0
- afriend-0.6.1/src/afriend/envelopes.py +258 -0
- afriend-0.6.1/src/afriend/errors.py +31 -0
- afriend-0.6.1/src/afriend/events.py +300 -0
- afriend-0.6.1/src/afriend/failures.py +203 -0
- afriend-0.6.1/src/afriend/http_transport.py +349 -0
- afriend-0.6.1/src/afriend/ids.py +41 -0
- afriend-0.6.1/src/afriend/isolation.py +162 -0
- afriend-0.6.1/src/afriend/jsonio.py +81 -0
- afriend-0.6.1/src/afriend/judgebatch.py +134 -0
- afriend-0.6.1/src/afriend/judgeprompt.py +192 -0
- afriend-0.6.1/src/afriend/ledger.py +275 -0
- afriend-0.6.1/src/afriend/merge.py +190 -0
- afriend-0.6.1/src/afriend/normalize.py +391 -0
- afriend-0.6.1/src/afriend/orchestrator.py +418 -0
- afriend-0.6.1/src/afriend/outcomes.py +429 -0
- afriend-0.6.1/src/afriend/paths.py +26 -0
- afriend-0.6.1/src/afriend/presets.py +80 -0
- afriend-0.6.1/src/afriend/procgroup.py +141 -0
- afriend-0.6.1/src/afriend/procio.py +215 -0
- afriend-0.6.1/src/afriend/progress.py +301 -0
- afriend-0.6.1/src/afriend/prompt.py +133 -0
- afriend-0.6.1/src/afriend/providerconfig.py +248 -0
- afriend-0.6.1/src/afriend/readiness.py +355 -0
- afriend-0.6.1/src/afriend/report.py +661 -0
- afriend-0.6.1/src/afriend/resolutions.py +243 -0
- afriend-0.6.1/src/afriend/reviewcompleteness.py +72 -0
- afriend-0.6.1/src/afriend/reviewprofiles.py +126 -0
- afriend-0.6.1/src/afriend/reviewstate.py +138 -0
- afriend-0.6.1/src/afriend/roster.py +220 -0
- afriend-0.6.1/src/afriend/rosterfile.py +132 -0
- afriend-0.6.1/src/afriend/rounds.py +662 -0
- afriend-0.6.1/src/afriend/runstore.py +333 -0
- afriend-0.6.1/src/afriend/sandbox.py +488 -0
- afriend-0.6.1/src/afriend/secureio.py +381 -0
- afriend-0.6.1/src/afriend/sessionconfig.py +353 -0
- afriend-0.6.1/src/afriend/snapshotgit.py +168 -0
- afriend-0.6.1/src/afriend/snapshots.py +663 -0
- afriend-0.6.1/src/afriend/snapshotvalidation.py +199 -0
- afriend-0.6.1/src/afriend/spawn.py +447 -0
- afriend-0.6.1/src/afriend/themes.py +227 -0
- afriend-0.6.1/src/afriend/trust.py +142 -0
- afriend-0.6.1/src/afriend/verdicts.py +383 -0
- afriend-0.6.1/src/afriend/verdictschema.py +217 -0
- afriend-0.6.1/src/afriend/workspaceassets.py +375 -0
- afriend-0.6.1/src/afriend.egg-info/PKG-INFO +579 -0
- afriend-0.6.1/src/afriend.egg-info/SOURCES.txt +239 -0
- afriend-0.6.1/src/afriend.egg-info/dependency_links.txt +1 -0
- afriend-0.6.1/src/afriend.egg-info/entry_points.txt +2 -0
- afriend-0.6.1/src/afriend.egg-info/top_level.txt +1 -0
- afriend-0.6.1/tests/test_abort_reentry.py +27 -0
- afriend-0.6.1/tests/test_adapters.py +366 -0
- afriend-0.6.1/tests/test_advisory_host_participation.py +210 -0
- afriend-0.6.1/tests/test_agy_harness.py +166 -0
- afriend-0.6.1/tests/test_auth_abort_findings.py +418 -0
- afriend-0.6.1/tests/test_auth_markers_claude.py +108 -0
- afriend-0.6.1/tests/test_auth_markers_codex.py +79 -0
- afriend-0.6.1/tests/test_auth_markers_ollama.py +70 -0
- afriend-0.6.1/tests/test_authority.py +771 -0
- afriend-0.6.1/tests/test_authority_resume_external_tools.py +78 -0
- afriend-0.6.1/tests/test_bounded_json.py +56 -0
- afriend-0.6.1/tests/test_ceiling_reach.py +90 -0
- afriend-0.6.1/tests/test_ceilings.py +88 -0
- afriend-0.6.1/tests/test_childenv.py +83 -0
- afriend-0.6.1/tests/test_claimschema.py +202 -0
- afriend-0.6.1/tests/test_cli_entry.py +90 -0
- afriend-0.6.1/tests/test_cliargs.py +201 -0
- afriend-0.6.1/tests/test_compatibility_distributions.py +30 -0
- afriend-0.6.1/tests/test_confine_optin.py +73 -0
- afriend-0.6.1/tests/test_confinement_record.py +149 -0
- afriend-0.6.1/tests/test_crossexam_roster_findings.py +88 -0
- afriend-0.6.1/tests/test_deny_capability_probe.py +253 -0
- afriend-0.6.1/tests/test_discard_consecutive.py +77 -0
- afriend-0.6.1/tests/test_dispatch_findings.py +137 -0
- afriend-0.6.1/tests/test_docs.py +775 -0
- afriend-0.6.1/tests/test_doctor_host_policy.py +92 -0
- afriend-0.6.1/tests/test_early_answer_stop.py +133 -0
- afriend-0.6.1/tests/test_envelope_fixtures.py +259 -0
- afriend-0.6.1/tests/test_envelope_rule_conditions.py +90 -0
- afriend-0.6.1/tests/test_environment.py +65 -0
- afriend-0.6.1/tests/test_errors.py +19 -0
- afriend-0.6.1/tests/test_events.py +307 -0
- afriend-0.6.1/tests/test_exits.py +53 -0
- afriend-0.6.1/tests/test_explicit_friend_preflight.py +150 -0
- afriend-0.6.1/tests/test_extra_args_placement.py +92 -0
- afriend-0.6.1/tests/test_failures.py +317 -0
- afriend-0.6.1/tests/test_final_quality_fixes.py +222 -0
- afriend-0.6.1/tests/test_friend_key.py +29 -0
- afriend-0.6.1/tests/test_guided_init.py +284 -0
- afriend-0.6.1/tests/test_http_transport.py +332 -0
- afriend-0.6.1/tests/test_ids.py +60 -0
- afriend-0.6.1/tests/test_isolation.py +380 -0
- afriend-0.6.1/tests/test_judge_never_sees_itself.py +66 -0
- afriend-0.6.1/tests/test_judgeprompt.py +231 -0
- afriend-0.6.1/tests/test_judging_recovery.py +622 -0
- afriend-0.6.1/tests/test_ledger.py +242 -0
- afriend-0.6.1/tests/test_max_loc.py +29 -0
- afriend-0.6.1/tests/test_merge.py +246 -0
- afriend-0.6.1/tests/test_normalize.py +452 -0
- afriend-0.6.1/tests/test_orchestrator.py +264 -0
- afriend-0.6.1/tests/test_outcome_runtime.py +438 -0
- afriend-0.6.1/tests/test_outcomes.py +761 -0
- afriend-0.6.1/tests/test_output_limits.py +91 -0
- afriend-0.6.1/tests/test_plugin_sync.py +113 -0
- afriend-0.6.1/tests/test_presets.py +81 -0
- afriend-0.6.1/tests/test_profiles_command.py +158 -0
- afriend-0.6.1/tests/test_progress.py +222 -0
- afriend-0.6.1/tests/test_providerconfig.py +255 -0
- afriend-0.6.1/tests/test_pump_failure_signal.py +80 -0
- afriend-0.6.1/tests/test_pump_findings.py +126 -0
- afriend-0.6.1/tests/test_readiness.py +293 -0
- afriend-0.6.1/tests/test_release_workflow.py +180 -0
- afriend-0.6.1/tests/test_report.py +717 -0
- afriend-0.6.1/tests/test_report_completeness.py +75 -0
- afriend-0.6.1/tests/test_report_repository_scope.py +90 -0
- afriend-0.6.1/tests/test_report_verdicts.py +64 -0
- afriend-0.6.1/tests/test_repository_scope_absence.py +23 -0
- afriend-0.6.1/tests/test_repository_scope_docs.py +26 -0
- afriend-0.6.1/tests/test_resolutions.py +325 -0
- afriend-0.6.1/tests/test_resolve_discovery.py +258 -0
- afriend-0.6.1/tests/test_resume_budget_findings.py +253 -0
- afriend-0.6.1/tests/test_resume_crash_safety.py +764 -0
- afriend-0.6.1/tests/test_resume_findings.py +534 -0
- afriend-0.6.1/tests/test_resume_overbudget_e2e.py +102 -0
- afriend-0.6.1/tests/test_reviewcompleteness.py +75 -0
- afriend-0.6.1/tests/test_reviewprofiles.py +64 -0
- afriend-0.6.1/tests/test_reviewstate.py +162 -0
- afriend-0.6.1/tests/test_reviewstate_properties.py +88 -0
- afriend-0.6.1/tests/test_roster.py +446 -0
- afriend-0.6.1/tests/test_rosterfile.py +121 -0
- afriend-0.6.1/tests/test_round_audit.py +735 -0
- afriend-0.6.1/tests/test_round_authority.py +195 -0
- afriend-0.6.1/tests/test_run_end_to_end_authority.py +55 -0
- afriend-0.6.1/tests/test_run_end_to_end_basics.py +619 -0
- afriend-0.6.1/tests/test_run_end_to_end_crossexam.py +509 -0
- afriend-0.6.1/tests/test_run_end_to_end_flags.py +761 -0
- afriend-0.6.1/tests/test_run_end_to_end_gate.py +333 -0
- afriend-0.6.1/tests/test_run_end_to_end_isolation.py +719 -0
- afriend-0.6.1/tests/test_run_end_to_end_lenses.py +461 -0
- afriend-0.6.1/tests/test_run_end_to_end_loop.py +465 -0
- afriend-0.6.1/tests/test_run_end_to_end_orchestrator.py +767 -0
- afriend-0.6.1/tests/test_run_end_to_end_orchestrator_loop_history.py +412 -0
- afriend-0.6.1/tests/test_run_end_to_end_roster.py +709 -0
- afriend-0.6.1/tests/test_run_end_to_end_signals.py +313 -0
- afriend-0.6.1/tests/test_run_permissions.py +109 -0
- afriend-0.6.1/tests/test_runmeta_host_roles.py +622 -0
- afriend-0.6.1/tests/test_runmeta_migration.py +762 -0
- afriend-0.6.1/tests/test_runstore.py +188 -0
- afriend-0.6.1/tests/test_sandbox.py +585 -0
- afriend-0.6.1/tests/test_sandbox_findings.py +119 -0
- afriend-0.6.1/tests/test_sandbox_install_root.py +67 -0
- afriend-0.6.1/tests/test_scratch_placement.py +226 -0
- afriend-0.6.1/tests/test_secure_ancestor_containment.py +255 -0
- afriend-0.6.1/tests/test_sessionconfig.py +111 -0
- afriend-0.6.1/tests/test_signal_group_denied.py +86 -0
- afriend-0.6.1/tests/test_skill_layer.py +195 -0
- afriend-0.6.1/tests/test_snapshot_binding.py +491 -0
- afriend-0.6.1/tests/test_snapshot_creation.py +214 -0
- afriend-0.6.1/tests/test_snapshot_history_scope.py +109 -0
- afriend-0.6.1/tests/test_snapshot_identity.py +603 -0
- afriend-0.6.1/tests/test_spawn.py +365 -0
- afriend-0.6.1/tests/test_status.py +606 -0
- afriend-0.6.1/tests/test_stdin_pump.py +72 -0
- afriend-0.6.1/tests/test_stream_answer_order.py +73 -0
- afriend-0.6.1/tests/test_successor_id_collision.py +89 -0
- afriend-0.6.1/tests/test_task10_dogfood_regression.py +24 -0
- afriend-0.6.1/tests/test_task6_resume_legacy.py +144 -0
- afriend-0.6.1/tests/test_theme_checkpoint.py +143 -0
- afriend-0.6.1/tests/test_themes.py +277 -0
- afriend-0.6.1/tests/test_trailing_comma_repair.py +110 -0
- afriend-0.6.1/tests/test_truncated_never_extracted.py +56 -0
- afriend-0.6.1/tests/test_trust.py +161 -0
- afriend-0.6.1/tests/test_verdicts.py +301 -0
- afriend-0.6.1/tests/test_verdicts_lifecycle.py +328 -0
- afriend-0.6.1/tests/test_verdictschema.py +247 -0
- afriend-0.6.1/tests/test_version_sync.py +101 -0
- afriend-0.6.1/tests/test_workspaceasset_limits.py +255 -0
- afriend-0.6.1/tests/test_workspaceassets.py +652 -0
afriend-0.6.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tim
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
afriend-0.6.1/PKG-INFO
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: afriend
|
|
3
|
+
Version: 0.6.1
|
|
4
|
+
Summary: Cross-examine specs, plans, and reviews using other agent CLIs as adversarial reviewers
|
|
5
|
+
Author: Tim
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/livingstaccato/afriend
|
|
8
|
+
Project-URL: Issues, https://github.com/livingstaccato/afriend/issues
|
|
9
|
+
Keywords: claude,codex,agent,code-review,cli
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
# afriend
|
|
28
|
+
|
|
29
|
+
> Hand your spec, plan, or review to agent CLIs — `claude`, `codex`,
|
|
30
|
+
> Antigravity (`agy`),
|
|
31
|
+
> `opencode` — under adversarial lenses, then merge their critiques into one
|
|
32
|
+
> ranked findings report.
|
|
33
|
+
|
|
34
|
+
[](pyproject.toml)
|
|
35
|
+
[](pyproject.toml)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](tests/)
|
|
38
|
+
|
|
39
|
+
It automates a workflow you may already do by hand: run a review, paste the
|
|
40
|
+
findings into a different model, ask whether they hold up, carry the argument
|
|
41
|
+
back. Doing that manually means holding a claim ledger in your head. This
|
|
42
|
+
keeps the ledger on disk.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 📋 Contents
|
|
47
|
+
|
|
48
|
+
- [Why more than one model](#-why-more-than-one-model)
|
|
49
|
+
- [Install](#-install)
|
|
50
|
+
- [Quickstart](#-quickstart)
|
|
51
|
+
- [How it works](#-how-it-works)
|
|
52
|
+
- [Lenses](#-lenses)
|
|
53
|
+
- [What you get back](#-what-you-get-back)
|
|
54
|
+
- [What's implemented](#-whats-implemented)
|
|
55
|
+
- [Documentation](#-documentation)
|
|
56
|
+
- [Development](#-development)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🎯 Why more than one model
|
|
61
|
+
|
|
62
|
+
A single reviewer produces confident prose. Several reviewers produce claims
|
|
63
|
+
that **can be compared** — and the disagreements are where the real problems
|
|
64
|
+
are.
|
|
65
|
+
|
|
66
|
+
This tool's own design spec was built exactly this way:
|
|
67
|
+
|
|
68
|
+
| Reviewer | Result |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `codex` | 17 findings |
|
|
71
|
+
| `claude` | 15 findings, plus one marked `unproven` — *"lens leaks attribution"* |
|
|
72
|
+
| Antigravity (`agy`) | independently reproduced two of `claude`'s findings, **and** caught a shared-worktree race neither of the other two flagged |
|
|
73
|
+
|
|
74
|
+
That `unproven` claim was later confirmed and fixed. No single reviewer's pass
|
|
75
|
+
would have surfaced all of it — see the [revision history in the design
|
|
76
|
+
spec](docs/superpowers/specs/2026-08-22-adversarial-friends-design.md#19-revision-history)
|
|
77
|
+
for the full account.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 📦 Install
|
|
82
|
+
|
|
83
|
+
Requires **Python 3.11+** and at least one agent CLI. Judging modes additionally
|
|
84
|
+
require two independent non-host friends. The runner itself is **stdlib-only**
|
|
85
|
+
— zero runtime dependencies.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv tool install afriend
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`afriend` is the canonical distribution. `adversarial-friends` and `afriends`
|
|
92
|
+
are compatibility/reservation distributions that install the matching
|
|
93
|
+
`afriend` release; they add no alternate import or command.
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary>Other install methods</summary>
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# From git, for a version that is not yet released
|
|
100
|
+
uv tool install git+https://github.com/livingstaccato/afriend
|
|
101
|
+
|
|
102
|
+
# From a local checkout
|
|
103
|
+
git clone https://github.com/livingstaccato/afriend
|
|
104
|
+
cd afriend
|
|
105
|
+
uv tool install .
|
|
106
|
+
|
|
107
|
+
# Without installing at all
|
|
108
|
+
python -m afriend doctor
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
Then confirm what's actually available:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
afriend doctor
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`doctor` reports shared readiness — including `ready`,
|
|
120
|
+
`reachable-unconfigured`, `unavailable`, `disabled`, `host-excluded`, and
|
|
121
|
+
`policy-blocked` — plus what each friend can genuinely enforce. For Ollama,
|
|
122
|
+
reachability alone is insufficient because dispatch also requires a model.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🚀 Quickstart
|
|
127
|
+
|
|
128
|
+
In an agent host, select `/afriend` to route an explicit afriend
|
|
129
|
+
request, or select `$afriend:afriend` directly. It hands review,
|
|
130
|
+
status, setup/configuration, and resolution requests to focused skills:
|
|
131
|
+
`review`, `status`, `configure`, and `resolve`.
|
|
132
|
+
|
|
133
|
+
Conversational phrases such as `afriend review` and `afriend status` are
|
|
134
|
+
routing language, not executable aliases: the stable CLI commands remain
|
|
135
|
+
`afriend run`, `afriend status`, and `afriend doctor`.
|
|
136
|
+
`afriend resume <run-id>` similarly routes to `afriend run --resume <run-id>`;
|
|
137
|
+
it is not a claim resolution and needs no disposition or evidence.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
afriend run docs/my-design.md
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
On the first review request in a host task, `/afriend` presents one compact
|
|
144
|
+
preflight before dispatch:
|
|
145
|
+
|
|
146
|
+
> About to start afriend to review `<artifact>` in `<mode>` mode
|
|
147
|
+
> with `<profile>`. Scope: `<repository snapshot|document only>`. Friends:
|
|
148
|
+
> `<name, provider, lens, role>`; external tools: `<denied|explicit grant>`.
|
|
149
|
+
|
|
150
|
+
You can accept it, choose a task-only profile or mode, change the task-only
|
|
151
|
+
roster, or stop. It is shown again only before a requested new loop iteration.
|
|
152
|
+
The preflight describes authority; it never grants external tools, provider
|
|
153
|
+
enablement, unsafe arguments, or sandbox exceptions. Direct CLI runs stay
|
|
154
|
+
non-interactive.
|
|
155
|
+
|
|
156
|
+
The built-in profiles are `quick` (one `report` fan-out), `balanced`
|
|
157
|
+
(`crossexam`), and `thorough` (`loop`). `quick` is the default. Use
|
|
158
|
+
`--profile NAME` for one run; an explicit `--mode` wins over the profile's
|
|
159
|
+
mode and other explicit safe run flags win over profile values.
|
|
160
|
+
|
|
161
|
+
The host is the orchestrator. In Codex, Codex remains the orchestrator and is
|
|
162
|
+
included as a friend by default. The report labels it
|
|
163
|
+
`host-self-review (advisory)`, `independent=false`: it contributes findings
|
|
164
|
+
and advisory verdicts, but cannot satisfy two-independent-friend admission,
|
|
165
|
+
`--require-friends` participation, judging quorum, gate clearance, or loop
|
|
166
|
+
convergence. Judging modes need two independent non-host friends in addition
|
|
167
|
+
to any host; a `report` can be host-only. Non-Codex hosts are excluded by
|
|
168
|
+
default. `--include-self` and `--exclude-self` are mutually exclusive
|
|
169
|
+
overrides.
|
|
170
|
+
|
|
171
|
+
Manage persistent user defaults, including a default Ollama model, with:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
afriend providers list
|
|
175
|
+
afriend providers enable claude
|
|
176
|
+
afriend providers disable opencode
|
|
177
|
+
afriend providers set-model ollama qwen3:8b
|
|
178
|
+
afriend providers clear-model ollama
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Set up those defaults with an inspectable no-write preview, then apply only
|
|
182
|
+
the exact choices you name:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
afriend init --guided
|
|
186
|
+
afriend init --guided --default-profile balanced --enable-provider claude
|
|
187
|
+
afriend init --guided --apply --default-profile balanced --enable-provider claude
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Guided setup reports discovered providers, the Codex advisory-host role when
|
|
191
|
+
applicable, built-in profiles, and that external tools remain denied. Plain
|
|
192
|
+
`afriend init` remains the direct roster-generation command.
|
|
193
|
+
|
|
194
|
+
Manage named user profiles separately:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
afriend profiles list
|
|
198
|
+
afriend profiles create focused --base quick --timeout 300
|
|
199
|
+
afriend profiles set-default focused
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Profiles can carry only review-safe mode, preset, lenses, friend/timeout
|
|
203
|
+
ceilings, and round/iteration ceilings. They cannot select providers, a
|
|
204
|
+
friend roster, models, credentials, forwarded environment, external tools,
|
|
205
|
+
unsafe arguments, or sandbox exceptions.
|
|
206
|
+
|
|
207
|
+
For one automatic roster, `--enable-provider NAME` and
|
|
208
|
+
`--disable-provider NAME` override those defaults. Disabled providers are not
|
|
209
|
+
probed. An explicit `--friend` roster remains authoritative and may name the
|
|
210
|
+
host or a disabled provider.
|
|
211
|
+
|
|
212
|
+
External-tool authority is separate from provider selection. The repeatable
|
|
213
|
+
required-value form is `--allow-external-tools=PROVIDER`; the explicit global
|
|
214
|
+
grant is `--allow-external-tools=*`. Unknown, duplicate, or mixed `*` plus
|
|
215
|
+
provider grants are invalid, and the old valueless form is invalid.
|
|
216
|
+
`--unsafe-extra-args` requires the global `*` grant plus
|
|
217
|
+
`--i-accept-unsandboxed`. Grants do not change provider defaults and must be
|
|
218
|
+
repeated as the same normalized set on resume.
|
|
219
|
+
|
|
220
|
+
**Stdout** carries one thing — the run directory. Read `report.md` inside it:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
cat "$(afriend run docs/my-design.md --mode report)/report.md"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Pick your reviewers and lenses explicitly:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
afriend run spec.md --friend codex:security --friend claude:ops
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
A third slot picks the model — required for `ollama`, which has no default:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
afriend run spec.md --friend ollama:security:qwen3:0.6b
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
> ⚠️ `--friend` **replaces** discovery rather than adding to it. One
|
|
239
|
+
> `--friend` flag means a one-friend roster. A `report` with one friend is
|
|
240
|
+
> allowed as a recorded downgrade in `run.json` and `report.md` rather than
|
|
241
|
+
> being presented as a full review. `crossexam`, `gate`, and `loop` require at
|
|
242
|
+
> least two independent non-host friends; with fewer, they refuse with exit 3
|
|
243
|
+
> before a run directory is created.
|
|
244
|
+
|
|
245
|
+
Runtime depends on the slowest selected friend, document size, and mode.
|
|
246
|
+
Progress goes to **stderr**: a line per friend as it finishes, and a heartbeat
|
|
247
|
+
naming whatever is still outstanding, so a quiet run is distinguishable from
|
|
248
|
+
a hung one. `--no-progress` silences it for a caller that captures both
|
|
249
|
+
streams together; reducing `--timeout` turns slow friends into failures rather
|
|
250
|
+
than making them faster.
|
|
251
|
+
|
|
252
|
+
Each run also appends safe lifecycle records to `events.jsonl`: `run_started`,
|
|
253
|
+
friend completion/failure, round completion, and `run_finished`. Records carry
|
|
254
|
+
only identity, mode/profile, scope, status, round, duration, and next action;
|
|
255
|
+
they never copy prompts, raw outputs, diagnostics, credentials, environment,
|
|
256
|
+
or authority grants. After the run, inspect it without dispatching anything:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
afriend status <run-id-or-path>
|
|
260
|
+
afriend status <run-id-or-path> --watch
|
|
261
|
+
afriend status <run-id-or-path> --json
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`--watch` prints only new lifecycle events until the terminal event and treats
|
|
265
|
+
an unterminated final event line as still being written. Runs without an event
|
|
266
|
+
stream remain inspectable from `run.json`, `claims.jsonl`, and `report.md`.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## ⚙️ How it works
|
|
271
|
+
|
|
272
|
+

|
|
273
|
+
|
|
274
|
+
Every friend gets its **own** prompt built from its **own** lens, runs in its
|
|
275
|
+
**own** isolated directory, in its **own** process group:
|
|
276
|
+
|
|
277
|
+
| Stage | What happens |
|
|
278
|
+
|---|---|
|
|
279
|
+
| 🔍 **Resolve** | Discover agent CLIs on `PATH`, round-robin a lens to each |
|
|
280
|
+
| ✍️ **Prompt** | Build a per-friend prompt: shared contract header + that friend's lens prose + the artifact |
|
|
281
|
+
| 🔒 **Isolate** | Each friend's effective scope selects its isolation directory: repo scope gets a private `git worktree` from one shared snapshot, while doc scope gets an artifact-only directory. Adapter read-only flags and, where required, OS confinement (`sandbox-exec` / `bwrap`) are then applied separately as a second line of defense — or the friend is refused |
|
|
282
|
+
| 🛂 **Deny remote authority** | External tools are denied by default. An adapter that cannot neutralize provider-managed tools, plugins, apps, or MCP servers is `policy-blocked` unless `--allow-external-tools=PROVIDER` explicitly opts that provider in for this run |
|
|
283
|
+
| 🧰 **Stage harnesses** | Adapter-owned workspace assets are copied into each isolated run workspace. Antigravity receives the controlled `afriend-reviewer` agent selected with `--agent`, `--disable-slash-commands`, `--mode plan`, and `--sandbox` |
|
|
284
|
+
| ⚡ **Dispatch** | Parallel, one thread per friend, each in its own process group with a kill deadline of `--timeout + 60s` |
|
|
285
|
+
| 🧩 **Normalize** | Unwrap the CLI's own JSON envelope, strip ANSI, recover the payload, validate against the claim schema |
|
|
286
|
+
| 🔗 **Merge** | Exact-merge identical claims into aliases — accumulating origins so corroboration survives |
|
|
287
|
+
| 📄 **Report** | Rank findings, render `report.md`, write the append-only ledger |
|
|
288
|
+
|
|
289
|
+
The snapshot includes **untracked** files (`git stash create` omits them), and
|
|
290
|
+
the working tree is never touched — a friend reviewing your repo can't see a
|
|
291
|
+
half-staged index or scribble on your checkout.
|
|
292
|
+
|
|
293
|
+
There are two supported ways to select review context:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
afriend run docs/plan.md --mode report
|
|
297
|
+
afriend run /tmp/reviews/plan.md --repo "$PWD" --mode report
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The first form selects scope automatically only when the artifact's resolved
|
|
301
|
+
final target is inside the invocation repository: it receives a repository
|
|
302
|
+
snapshot. An in-repository symlink whose target resolves outside that
|
|
303
|
+
repository is doc scope only, and no repository snapshot is minted; a path
|
|
304
|
+
outside a Git repository likewise produces a visible doc-scope warning on
|
|
305
|
+
stderr before friends start. The second form selects the named repository
|
|
306
|
+
explicitly; `--repo` must name that repository's Git worktree root. It is the
|
|
307
|
+
deliberate route for an independently frozen external artifact together
|
|
308
|
+
with selected repository code. It does not grant new provider, external-tool,
|
|
309
|
+
or write authority. Normal untracked, non-ignored files are included in an
|
|
310
|
+
automatic snapshot.
|
|
311
|
+
|
|
312
|
+
Gitignored artifacts are intentionally excluded from automatic Git-blob
|
|
313
|
+
binding and fail rather than falling back to a stale `HEAD` version. Use the
|
|
314
|
+
explicit `--repo` form when an ignored or outside artifact needs the named
|
|
315
|
+
repository's code context.
|
|
316
|
+
|
|
317
|
+
On Linux, a confined friend uses `bwrap` with the required system paths
|
|
318
|
+
read-only. If `/etc/resolv.conf` resolves to a safe regular file elsewhere on
|
|
319
|
+
the host, the sandbox exposes that resolver target read-only too. DNS therefore
|
|
320
|
+
continues to work without granting general access to the host filesystem.
|
|
321
|
+
|
|
322
|
+
For a provider without a verified read-only/write-protection mode, make
|
|
323
|
+
`sandbox-exec` (macOS) or `bwrap` (Linux) available, or choose a provider
|
|
324
|
+
with a verified read-only/write-protection mode. That mode controls writes,
|
|
325
|
+
not filesystem reads, and does not replace OS read confinement. A provider
|
|
326
|
+
with a verified read-only/write-protection mode does not need
|
|
327
|
+
`--allow-unsandboxed-friend`. That flag is explicit risk acceptance, not a
|
|
328
|
+
normal fix: the affected provider runs without OS confinement and retains
|
|
329
|
+
same-user filesystem read access.
|
|
330
|
+
|
|
331
|
+
<details>
|
|
332
|
+
<summary>Full run flow, step by step</summary>
|
|
333
|
+
|
|
334
|
+

|
|
335
|
+
|
|
336
|
+
</details>
|
|
337
|
+
|
|
338
|
+
### Corroboration is the point
|
|
339
|
+
|
|
340
|
+
Two friends independently reaching the same conclusion is the strongest signal
|
|
341
|
+
this tool produces, so deduplication is built to never destroy it:
|
|
342
|
+
|
|
343
|
+

|
|
344
|
+
|
|
345
|
+
Dedup is **deliberately** exact-match — whitespace and case only. Two friends
|
|
346
|
+
describing one defect in different words produce two claims, which costs a
|
|
347
|
+
round. Guessing at equivalence would corrupt the ledger, which is worse.
|
|
348
|
+
|
|
349
|
+
### Claim states in cross-examination
|
|
350
|
+
|
|
351
|
+
Every claim `--mode crossexam` produces ends in one of eight states. Two of
|
|
352
|
+
them — `deadlocked` and `settled-upheld` — deliberately need a human, and the
|
|
353
|
+
report says so rather than quietly resolving them.
|
|
354
|
+
|
|
355
|
+

|
|
356
|
+
|
|
357
|
+
### The gate loop
|
|
358
|
+
|
|
359
|
+
`--mode gate` is the one that fails a build, and clearing it is a
|
|
360
|
+
back-and-forth rather than a single command:
|
|
361
|
+
|
|
362
|
+

|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 🔬 Lenses
|
|
367
|
+
|
|
368
|
+
A lens is prose, not a config string. Its text is injected into that friend's
|
|
369
|
+
prompt, so it shapes what the friend actually looks for.
|
|
370
|
+
|
|
371
|
+
| Lens | Default scope | Requires a failure scenario |
|
|
372
|
+
|---|---|---|
|
|
373
|
+
| `assumptions` | doc | ✅ |
|
|
374
|
+
| `security` | repo | ✅ |
|
|
375
|
+
| `ops` | repo | ✅ |
|
|
376
|
+
| `testability` | repo | ✅ |
|
|
377
|
+
| `spec-vs-reality` | repo | ✅ |
|
|
378
|
+
| `scope` | doc | ❌ — advisory only |
|
|
379
|
+
|
|
380
|
+
`scope` is the one lens that doesn't demand a concrete failure scenario;
|
|
381
|
+
"this is more than you need" is a legitimate finding without one. Claims from
|
|
382
|
+
it are marked *(advisory)* in the report so they never carry the same weight
|
|
383
|
+
as a reproducible defect.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 📂 What you get back
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
<run-dir>/
|
|
391
|
+
├── report.md ← ranked findings, corroboration, downgrades
|
|
392
|
+
├── run.json ← machine-readable: friends, statuses, downgrades
|
|
393
|
+
├── events.jsonl ← append-only safe lifecycle events
|
|
394
|
+
├── claims.jsonl ← append-only ledger: claims, aliases
|
|
395
|
+
├── artifact/ ← frozen copy of what was reviewed, hashed
|
|
396
|
+
└── round-1/
|
|
397
|
+
├── <friend>.prompt ← exactly what this friend was asked
|
|
398
|
+
├── <friend>.raw ← its unmodified stdout
|
|
399
|
+
├── <friend>.err ← its stderr (always present, even when empty)
|
|
400
|
+
├── <friend>.meta ← argv, exit code, duration, timeout, orphan status
|
|
401
|
+
└── <friend>.sandbox ← the OS policy it ran under, when one was applied
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Runs land under `${XDG_STATE_HOME:-~/.local/state}/afriend/runs/`,
|
|
405
|
+
or wherever `--out` points.
|
|
406
|
+
|
|
407
|
+
Everything a friend was asked and everything it said is on disk. When a run
|
|
408
|
+
comes back thin, that's what you read — not a guess.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## ✅ What's implemented
|
|
413
|
+
|
|
414
|
+
**All four modes run.**
|
|
415
|
+
|
|
416
|
+
| Mode | What it does |
|
|
417
|
+
|---|---|
|
|
418
|
+
| `report` | The default: one critique fan-out. Every friend critiques in parallel; claims merge into one ranked report. |
|
|
419
|
+
| `crossexam` | Then friends judge the claims they did not write, blind, for three total rounds by default or until each settles or deadlocks. |
|
|
420
|
+
| `gate` | Then every non-advisory claim that did not clear needs an explicit resolution — this is the one that fails a build. |
|
|
421
|
+
| `loop` | Repeats for a maximum of five iterations by default, until two consecutive dry rounds surface nothing new. |
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
afriend run docs/design.md --mode crossexam
|
|
425
|
+
afriend run docs/design.md --mode gate # exit 1 while anything blocks
|
|
426
|
+
afriend resolve <run-id> --list
|
|
427
|
+
afriend resolve <run-id> --next
|
|
428
|
+
afriend resolve <run-id> --claim c-0001@1 \
|
|
429
|
+
--disposition fixed --evidence src/auth.py:38
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Disagreement is the output rather than a problem: two judges who still
|
|
433
|
+
disagree at `--max-rounds` leave the claim `deadlocked`, and the report
|
|
434
|
+
quotes both sides verbatim instead of resolving it by majority.
|
|
435
|
+
|
|
436
|
+
A resolution is an **attestation**, and the tool says so. It cannot know a
|
|
437
|
+
defect is gone — only whether the location you named actually changed since
|
|
438
|
+
the run started. A fix that landed outside the reviewed artifact is fine.
|
|
439
|
+
`--disposition fixed` requires a verifiably changed location; unchanged or
|
|
440
|
+
unverifiable evidence is refused. Use `accepted-risk` when verification is
|
|
441
|
+
intentionally unavailable.
|
|
442
|
+
|
|
443
|
+
Deduplication is judgment the runner declines to fake. `--merge exact`
|
|
444
|
+
(the default) merges only identical claims and always finishes unaided;
|
|
445
|
+
`--merge orchestrator` stops with exit `10`, writes the claims to
|
|
446
|
+
`REQUEST.json`, and waits for you to say which are duplicates:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
afriend run docs/design.md --merge orchestrator # exit 10, writes REQUEST.json
|
|
450
|
+
# ...fill in the merges, save as RESPONSE.json...
|
|
451
|
+
afriend run --resume <run-id> # round 1 is not re-run
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Resume verifies the original frozen artifact hash and saved Git snapshot
|
|
455
|
+
before dispatch; it never substitutes current files. It uses the saved
|
|
456
|
+
repository scope and rejects `--repo`, so a resume cannot replace the original
|
|
457
|
+
automatic or explicit selection. Security grants are
|
|
458
|
+
also never restored from `run.json`: options such as
|
|
459
|
+
`--allow-external-tools=PROVIDER` grants (or the global `=*` grant) must be
|
|
460
|
+
repeated as the same normalized set.
|
|
461
|
+
|
|
462
|
+
Tired of `--friend` flags? `afriend init` writes a roster from what is
|
|
463
|
+
actually installed, and `~/.config/afriend/roster.toml` is picked
|
|
464
|
+
up automatically. A repo-local roster never is — a cloned repo does not get
|
|
465
|
+
to choose who reviews it (§13).
|
|
466
|
+
|
|
467
|
+
The same halt serves unparseable output (§14.2): repair is a pure
|
|
468
|
+
transformation with no model call, so when it fails the runner asks you to
|
|
469
|
+
read the raw text rather than discarding whatever the friend found.
|
|
470
|
+
|
|
471
|
+
**There is no `--max-spend-usd`.** A dollar cap needs per-CLI cost reporting
|
|
472
|
+
nobody has captured, and a flag that silently never fires is worse than none
|
|
473
|
+
— you would set it and believe you were protected. Use `--max-calls`, which
|
|
474
|
+
is derived from your roster and actually enforced.
|
|
475
|
+
|
|
476
|
+
| Friend | Status |
|
|
477
|
+
|---|---|
|
|
478
|
+
| `claude` | ✅ ships |
|
|
479
|
+
| `codex` | ✅ ships |
|
|
480
|
+
| `agy` | ✅ ships |
|
|
481
|
+
| `opencode` | ✅ ships — no read-only mode, reported honestly |
|
|
482
|
+
| `ollama` | ✅ ships — local models over HTTP, no schema/read-only to enforce; needs an explicit model |
|
|
483
|
+
|
|
484
|
+
Antigravity's controlled reviewer is staged into the run's isolated workspace;
|
|
485
|
+
it does not edit global Antigravity configuration. The staged agent and
|
|
486
|
+
`--sandbox` are defense in depth, but sandbox does not mean external tools
|
|
487
|
+
were denied. Antigravity remains `external_tools=uncontrolled` because its
|
|
488
|
+
CLI cannot prove every plugin/MCP integration disabled invocation-locally.
|
|
489
|
+
That is an accepted
|
|
490
|
+
best-effort limitation: Antigravity is policy-blocked by default, while
|
|
491
|
+
`--allow-external-tools=agy` records it as `explicitly-allowed`.
|
|
492
|
+
|
|
493
|
+
Antigravity is the shipped Google harness, identified as `agy` in CLI and
|
|
494
|
+
provider configuration.
|
|
495
|
+
|
|
496
|
+
### Exit codes
|
|
497
|
+
|
|
498
|
+
| Code | Meaning |
|
|
499
|
+
|---|---|
|
|
500
|
+
| `0` | the run reached terminal states with nothing blocked |
|
|
501
|
+
| `1` | a `gate` still has claims needing a resolution, or every dispatched friend failed |
|
|
502
|
+
| `2` | usage error — bad flag, unknown CLI, missing artifact |
|
|
503
|
+
| `3` | no usable friends found, or a judging mode resolved fewer than two independent non-host friends; refused before a run directory |
|
|
504
|
+
| `10` | `--merge orchestrator` is waiting for you to adjudicate merges |
|
|
505
|
+
| `11` | a ceiling was hit — including natural `--max-loop-iterations` exhaustion without convergence; the run was truncated, not decided |
|
|
506
|
+
| `12` | `--require-friends N` was set and fewer than `N` friends answered |
|
|
507
|
+
| `128+N` | aborted by signal N — isolation torn down, friends killed |
|
|
508
|
+
|
|
509
|
+
A ceiling outranks everything below it, so a CI wrapper can read `11` as
|
|
510
|
+
"retry" and `1` as "block" without ambiguity.
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## 📚 Documentation
|
|
515
|
+
|
|
516
|
+
| Where | What |
|
|
517
|
+
|---|---|
|
|
518
|
+
| [docs/](docs/README.md) | Documentation index |
|
|
519
|
+
| [/afriend router](src/afriend/assets/entrypoints/afriend/SKILL.md) | Explicit product router and review workflow |
|
|
520
|
+
| [review](src/afriend/assets/entrypoints/review/SKILL.md) | Start and interpret a review run |
|
|
521
|
+
| [status](src/afriend/assets/entrypoints/status/SKILL.md) | Read-only provider and named-run status |
|
|
522
|
+
| [configure](src/afriend/assets/entrypoints/configure/SKILL.md) | Explicit provider-default changes |
|
|
523
|
+
| [resolve](src/afriend/assets/entrypoints/resolve/SKILL.md) | Named-run claim resolutions |
|
|
524
|
+
| [modes](src/afriend/assets/entrypoints/afriend/references/modes.md) | `report`, `crossexam`, `gate`, and `loop` |
|
|
525
|
+
| [architecture/](docs/architecture/README.md) | Diagrams and their sources |
|
|
526
|
+
| [design spec](docs/superpowers/specs/2026-08-22-adversarial-friends-design.md) | The full design, including the adversarial review that produced it |
|
|
527
|
+
|
|
528
|
+
### Using it as a skill or plugin
|
|
529
|
+
|
|
530
|
+
The skill payload ships **inside the wheel** as package data, and is mirrored
|
|
531
|
+
under [`plugins/`](plugins/) for loaders that can't install a Python package:
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
# Claude Code
|
|
535
|
+
/plugin marketplace add /path/to/afriend/plugins
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Plugins package capabilities; the afriend plugin provides exactly
|
|
539
|
+
five skills: `/afriend`, `review`, `status`, `configure`, and `resolve`.
|
|
540
|
+
`/afriend` is the only router and short slash selector; direct qualified
|
|
541
|
+
selection is `$afriend:afriend`. The CLI never runs automatically
|
|
542
|
+
by itself. Generic “review this,” “poke holes,” “second opinion,” and
|
|
543
|
+
architectural decision requests stay ordinary Codex work.
|
|
544
|
+
|
|
545
|
+
The package must therefore be installed for the skill to work — `afriend
|
|
546
|
+
doctor` is the check. Conversational forms route to stable commands; they are
|
|
547
|
+
not executable aliases.
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## 🛠 Development
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
make install # uv sync
|
|
555
|
+
make test # pytest
|
|
556
|
+
make quality # every portable CI gate, wheel checks, and tests
|
|
557
|
+
make diagrams # re-render docs/architecture/*.puml
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
`make quality` runs every portable CI gate, including wheel construction and
|
|
561
|
+
isolated installation. Linux CI additionally installs bubblewrap and requires
|
|
562
|
+
the real OS-confinement tests to execute; macOS cannot reproduce that Linux-
|
|
563
|
+
specific assertion locally. Use `make act-ci` for the closest local Linux run.
|
|
564
|
+
|
|
565
|
+
Two gates catch drift that is otherwise silent:
|
|
566
|
+
|
|
567
|
+
- **`plugin-sync`** — `src/afriend/assets/` is canonical; its
|
|
568
|
+
entrypoints project directly to plugin skills and runtime assets project
|
|
569
|
+
beneath `skills/afriend/`. Edit assets, then `make plugin-sync-copy`.
|
|
570
|
+
- **`version-sync`** — `VERSION` must match the `version` field in every
|
|
571
|
+
plugin manifest.
|
|
572
|
+
|
|
573
|
+
See [AGENTS.md](AGENTS.md) for repository layout and conventions.
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## 📄 License
|
|
578
|
+
|
|
579
|
+
MIT — see [LICENSE](LICENSE).
|