git-aware-coding-agent 1.0.0__py3-none-any.whl

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.
Files changed (62) hide show
  1. avos_cli/__init__.py +3 -0
  2. avos_cli/agents/avos_ask_agent.md +47 -0
  3. avos_cli/agents/avos_ask_agent_JSON_converter.md +78 -0
  4. avos_cli/agents/avos_hisotry_agent_JSON_converter.md +92 -0
  5. avos_cli/agents/avos_history_agent.md +58 -0
  6. avos_cli/agents/git_diff_agent.md +63 -0
  7. avos_cli/artifacts/__init__.py +17 -0
  8. avos_cli/artifacts/base.py +47 -0
  9. avos_cli/artifacts/commit_builder.py +35 -0
  10. avos_cli/artifacts/doc_builder.py +30 -0
  11. avos_cli/artifacts/issue_builder.py +37 -0
  12. avos_cli/artifacts/pr_builder.py +50 -0
  13. avos_cli/cli/__init__.py +1 -0
  14. avos_cli/cli/main.py +504 -0
  15. avos_cli/commands/__init__.py +1 -0
  16. avos_cli/commands/ask.py +541 -0
  17. avos_cli/commands/connect.py +363 -0
  18. avos_cli/commands/history.py +549 -0
  19. avos_cli/commands/hook_install.py +260 -0
  20. avos_cli/commands/hook_sync.py +231 -0
  21. avos_cli/commands/ingest.py +506 -0
  22. avos_cli/commands/ingest_pr.py +239 -0
  23. avos_cli/config/__init__.py +1 -0
  24. avos_cli/config/hash_store.py +93 -0
  25. avos_cli/config/lock.py +122 -0
  26. avos_cli/config/manager.py +180 -0
  27. avos_cli/config/state.py +90 -0
  28. avos_cli/exceptions.py +272 -0
  29. avos_cli/models/__init__.py +58 -0
  30. avos_cli/models/api.py +75 -0
  31. avos_cli/models/artifacts.py +99 -0
  32. avos_cli/models/config.py +56 -0
  33. avos_cli/models/diff.py +117 -0
  34. avos_cli/models/query.py +234 -0
  35. avos_cli/parsers/__init__.py +21 -0
  36. avos_cli/parsers/artifact_ref_extractor.py +173 -0
  37. avos_cli/parsers/reference_parser.py +117 -0
  38. avos_cli/services/__init__.py +1 -0
  39. avos_cli/services/chronology_service.py +68 -0
  40. avos_cli/services/citation_validator.py +134 -0
  41. avos_cli/services/context_budget_service.py +104 -0
  42. avos_cli/services/diff_resolver.py +398 -0
  43. avos_cli/services/diff_summary_service.py +141 -0
  44. avos_cli/services/git_client.py +351 -0
  45. avos_cli/services/github_client.py +443 -0
  46. avos_cli/services/llm_client.py +312 -0
  47. avos_cli/services/memory_client.py +323 -0
  48. avos_cli/services/query_fallback_formatter.py +108 -0
  49. avos_cli/services/reply_output_service.py +341 -0
  50. avos_cli/services/sanitization_service.py +218 -0
  51. avos_cli/utils/__init__.py +1 -0
  52. avos_cli/utils/dotenv_load.py +50 -0
  53. avos_cli/utils/hashing.py +22 -0
  54. avos_cli/utils/logger.py +77 -0
  55. avos_cli/utils/output.py +232 -0
  56. avos_cli/utils/sanitization_diagnostics.py +81 -0
  57. avos_cli/utils/time_helpers.py +56 -0
  58. git_aware_coding_agent-1.0.0.dist-info/METADATA +390 -0
  59. git_aware_coding_agent-1.0.0.dist-info/RECORD +62 -0
  60. git_aware_coding_agent-1.0.0.dist-info/WHEEL +4 -0
  61. git_aware_coding_agent-1.0.0.dist-info/entry_points.txt +2 -0
  62. git_aware_coding_agent-1.0.0.dist-info/licenses/LICENSE +201 -0
avos_cli/cli/main.py ADDED
@@ -0,0 +1,504 @@
1
+ """CLI entry point for the avos command.
2
+
3
+ Thin layer: parses arguments, resolves credentials from the environment,
4
+ connected repo config (with env overlay), and delegates to orchestrators.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING
12
+
13
+ import typer
14
+
15
+ from avos_cli import __version__
16
+ from avos_cli.exceptions import AuthError
17
+ from avos_cli.utils.dotenv_load import load_layers
18
+ from avos_cli.utils.output import print_error
19
+
20
+ if TYPE_CHECKING:
21
+ from avos_cli.services.diff_summary_service import DiffSummaryService
22
+ from avos_cli.services.github_client import GitHubClient
23
+ from avos_cli.services.reply_output_service import ReplyOutputService
24
+
25
+ # Load .env: cwd, then repository root (beside avos_cli) with override so
26
+ # GITHUB_TOKEN in project root wins, then ~/.avos/.env without overriding.
27
+ load_layers()
28
+
29
+ app = typer.Typer(
30
+ name="avos",
31
+ help="Developer memory CLI for repositories.",
32
+ no_args_is_help=False,
33
+ add_completion=False,
34
+ )
35
+
36
+
37
+ def _version_callback(value: bool) -> None:
38
+ """Print version and exit."""
39
+ if value:
40
+ typer.echo(f"avos {__version__}")
41
+ raise typer.Exit()
42
+
43
+
44
+ def _first_env(*keys: str) -> str:
45
+ """Return the first non-empty env value for any of the given keys."""
46
+ for k in keys:
47
+ v = os.environ.get(k, "")
48
+ if v and isinstance(v, str):
49
+ return v.strip()
50
+ return ""
51
+
52
+
53
+ def _github_client_or_exit(repo_root: Path) -> GitHubClient:
54
+ """Resolve GitHub client from connected config (overlay) and env; exit if missing."""
55
+ from avos_cli.services.github_client import github_client_for_repo
56
+
57
+ try:
58
+ return github_client_for_repo(repo_root)
59
+ except AuthError:
60
+ print_error(
61
+ "[AUTH_ERROR] GitHub token is required. Set GITHUB_TOKEN or use a layered .env; "
62
+ "it is not written to .avos/config.json."
63
+ )
64
+ raise typer.Exit(1) from None
65
+
66
+
67
+ def _get_reply_model_config() -> tuple[str, str, str]:
68
+ """Get REPLY_MODEL configuration from environment.
69
+
70
+ Returns:
71
+ Tuple of (model, url, api_key). Empty strings if not configured.
72
+ """
73
+ model = _first_env("REPLY_MODEL", "reply_model")
74
+ url = _first_env("REPLY_MODEL_URL", "reply_model_URL", "reply_model_url")
75
+ api_key = _first_env("REPLY_MODEL_API_KEY", "reply_model_API_KEY", "reply_model_api_key")
76
+ return model, url, api_key
77
+
78
+
79
+ def _make_reply_service() -> ReplyOutputService | None:
80
+ """Build ReplyOutputService from env if REPLY_MODEL, REPLY_MODEL_URL, REPLY_MODEL_API_KEY are set."""
81
+ model, url, api_key = _get_reply_model_config()
82
+ if model and url and api_key:
83
+ from avos_cli.services.reply_output_service import ReplyOutputService
84
+ return ReplyOutputService(api_key=api_key, api_url=url, model=model)
85
+ return None
86
+
87
+
88
+ def _make_diff_summary_service() -> DiffSummaryService | None:
89
+ """Build DiffSummaryService from env if REPLY_MODEL config is available."""
90
+ model, url, api_key = _get_reply_model_config()
91
+ if model and url and api_key:
92
+ from avos_cli.services.diff_summary_service import DiffSummaryService
93
+
94
+ return DiffSummaryService(api_key=api_key, api_url=url, model=model)
95
+ return None
96
+
97
+
98
+ def _github_client_optional(repo_root: Path) -> GitHubClient | None:
99
+ """Resolve GitHub client from connected config (overlay) and env; return None if missing."""
100
+ from avos_cli.services.github_client import github_client_for_repo
101
+
102
+ try:
103
+ return github_client_for_repo(repo_root)
104
+ except AuthError:
105
+ return None
106
+
107
+
108
+ @app.callback(invoke_without_command=True)
109
+ def main(
110
+ ctx: typer.Context,
111
+ version: bool | None = typer.Option(
112
+ None,
113
+ "--version",
114
+ "-v",
115
+ help="Show version and exit.",
116
+ callback=_version_callback,
117
+ is_eager=True,
118
+ ),
119
+ verbose: bool = typer.Option(
120
+ False,
121
+ "--verbose",
122
+ help="Enable verbose debug output.",
123
+ ),
124
+ json_output: bool = typer.Option(
125
+ False,
126
+ "--json",
127
+ help="Emit machine-readable JSON output.",
128
+ ),
129
+ ) -> None:
130
+ """AVOS CLI - Developer memory for repositories."""
131
+ if ctx.invoked_subcommand is None:
132
+ help_text = ctx.get_help()
133
+ if "--version" not in help_text:
134
+ help_text = f"{help_text.rstrip()}\n\n--version -v Show version and exit.\n"
135
+ typer.echo(help_text)
136
+ raise typer.Exit(0)
137
+
138
+ ctx.ensure_object(dict)
139
+ ctx.obj["verbose"] = verbose
140
+ ctx.obj["json"] = json_output
141
+
142
+
143
+ @app.command()
144
+ def connect(
145
+ ctx: typer.Context,
146
+ repo: str | None = typer.Argument(
147
+ None,
148
+ help=(
149
+ "Repository slug in 'org/repo' format. "
150
+ "Omit to detect from git remote origin (GitHub HTTPS or SSH URL)."
151
+ ),
152
+ ),
153
+ ) -> None:
154
+ """Connect a repository to Avos Memory."""
155
+ from avos_cli.commands.connect import ConnectOrchestrator
156
+ from avos_cli.config.manager import find_repo_root
157
+ from avos_cli.exceptions import RepositoryContextError
158
+ from avos_cli.services.git_client import GitClient
159
+ from avos_cli.services.memory_client import AvosMemoryClient
160
+
161
+ json_output = ctx.obj.get("json", False)
162
+
163
+ api_key = os.environ.get("AVOS_API_KEY", "")
164
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
165
+
166
+ if not api_key:
167
+ print_error("[AUTH_ERROR] AVOS_API_KEY environment variable is required.")
168
+ raise typer.Exit(1)
169
+
170
+ try:
171
+ repo_root = find_repo_root(Path.cwd())
172
+ except RepositoryContextError as e:
173
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
174
+ raise typer.Exit(1) from e
175
+
176
+ gh_client = _github_client_or_exit(repo_root)
177
+
178
+ orchestrator = ConnectOrchestrator(
179
+ git_client=GitClient(),
180
+ github_client=gh_client,
181
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
182
+ repo_root=repo_root,
183
+ )
184
+ code = orchestrator.run(repo, json_output=json_output)
185
+ raise typer.Exit(code)
186
+
187
+
188
+ def _parse_since_days(value: str) -> int:
189
+ """Parse a '--since Nd' value like '90d' into integer days."""
190
+ cleaned = value.strip().lower()
191
+ if cleaned.endswith("d"):
192
+ cleaned = cleaned[:-1]
193
+ try:
194
+ days = int(cleaned)
195
+ if days <= 0:
196
+ raise typer.BadParameter("--since must be a positive number of days.")
197
+ return days
198
+ except ValueError as e:
199
+ raise typer.BadParameter(f"Invalid --since value: '{value}'. Expected format: '90d' or '90'.") from e
200
+
201
+
202
+ @app.command()
203
+ def ingest(
204
+ ctx: typer.Context,
205
+ repo: str = typer.Argument(..., help="Repository slug in 'org/repo' format."),
206
+ since: str = typer.Option("90d", "--since", help="Time window, e.g. '90d' for 90 days."),
207
+ ) -> None:
208
+ """Ingest repository history into Avos Memory."""
209
+ from avos_cli.commands.ingest import IngestOrchestrator
210
+ from avos_cli.config.hash_store import IngestHashStore
211
+ from avos_cli.config.lock import IngestLockManager
212
+ from avos_cli.config.manager import find_repo_root
213
+ from avos_cli.exceptions import RepositoryContextError
214
+ from avos_cli.services.git_client import GitClient
215
+ from avos_cli.services.memory_client import AvosMemoryClient
216
+
217
+ json_output = ctx.obj.get("json", False)
218
+ since_days = _parse_since_days(since)
219
+
220
+ api_key = os.environ.get("AVOS_API_KEY", "")
221
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
222
+
223
+ if not api_key:
224
+ print_error("[AUTH_ERROR] AVOS_API_KEY environment variable is required.")
225
+ raise typer.Exit(1)
226
+
227
+ try:
228
+ repo_root = find_repo_root(Path.cwd())
229
+ except RepositoryContextError as e:
230
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
231
+ raise typer.Exit(1) from e
232
+
233
+ gh_client = _github_client_or_exit(repo_root)
234
+
235
+ avos_dir = repo_root / ".avos"
236
+ hash_store = IngestHashStore(avos_dir)
237
+ hash_store.load()
238
+
239
+ orchestrator = IngestOrchestrator(
240
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
241
+ github_client=gh_client,
242
+ git_client=GitClient(),
243
+ hash_store=hash_store,
244
+ lock_manager=IngestLockManager(avos_dir),
245
+ repo_root=repo_root,
246
+ )
247
+ code = orchestrator.run(repo, since_days=since_days, json_output=json_output)
248
+ raise typer.Exit(code)
249
+
250
+
251
+ @app.command(name="ingest-pr")
252
+ def ingest_pr(
253
+ ctx: typer.Context,
254
+ repo: str = typer.Argument(..., help="Repository slug in 'org/repo' format."),
255
+ pr_number: int = typer.Argument(..., help="PR number to ingest."),
256
+ ) -> None:
257
+ """Ingest a single PR into Avos Memory."""
258
+ from avos_cli.commands.ingest_pr import IngestPROrchestrator
259
+ from avos_cli.config.hash_store import IngestHashStore
260
+ from avos_cli.config.manager import find_repo_root
261
+ from avos_cli.exceptions import RepositoryContextError
262
+ from avos_cli.services.memory_client import AvosMemoryClient
263
+
264
+ json_output = ctx.obj.get("json", False)
265
+
266
+ api_key = os.environ.get("AVOS_API_KEY", "")
267
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
268
+
269
+ if not api_key:
270
+ print_error("[AUTH_ERROR] AVOS_API_KEY environment variable is required.")
271
+ raise typer.Exit(1)
272
+
273
+ try:
274
+ repo_root = find_repo_root(Path.cwd())
275
+ except RepositoryContextError as e:
276
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
277
+ raise typer.Exit(1) from e
278
+
279
+ gh_client = _github_client_or_exit(repo_root)
280
+
281
+ avos_dir = repo_root / ".avos"
282
+ hash_store = IngestHashStore(avos_dir)
283
+ hash_store.load()
284
+
285
+ orchestrator = IngestPROrchestrator(
286
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
287
+ github_client=gh_client,
288
+ hash_store=hash_store,
289
+ repo_root=repo_root,
290
+ )
291
+ code = orchestrator.run(repo, pr_number, json_output=json_output)
292
+ raise typer.Exit(code)
293
+
294
+
295
+ @app.command()
296
+ def ask(
297
+ ctx: typer.Context,
298
+ question: str = typer.Argument(..., help="Natural language question about the repository."),
299
+ ) -> None:
300
+ """Ask a question about the repository and get an evidence-backed answer."""
301
+ from avos_cli.commands.ask import AskOrchestrator
302
+ from avos_cli.config.manager import find_repo_root, load_config
303
+ from avos_cli.exceptions import (
304
+ ConfigurationNotInitializedError,
305
+ RepositoryContextError,
306
+ )
307
+ from avos_cli.services.llm_client import LLMClient
308
+ from avos_cli.services.memory_client import AvosMemoryClient
309
+
310
+ json_output = ctx.obj.get("json", False)
311
+
312
+ api_key = os.environ.get("AVOS_API_KEY", "")
313
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
314
+
315
+ if not api_key:
316
+ print_error("[AUTH_ERROR] AVOS_API_KEY environment variable is required.")
317
+ raise typer.Exit(1)
318
+
319
+ try:
320
+ repo_root = find_repo_root(Path.cwd())
321
+ except RepositoryContextError as e:
322
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
323
+ raise typer.Exit(1) from e
324
+
325
+ try:
326
+ config = load_config(repo_root)
327
+ except ConfigurationNotInitializedError as e:
328
+ print_error("[AUTH_ERROR] Repository not connected. Run 'avos connect org/repo' first.")
329
+ raise typer.Exit(1) from e
330
+
331
+ provider = config.llm.provider.lower()
332
+ if provider == "openai":
333
+ llm_api_key = os.environ.get("OPENAI_API_KEY", "")
334
+ if not llm_api_key:
335
+ print_error("[AUTH_ERROR] OPENAI_API_KEY environment variable is required for OpenAI.")
336
+ raise typer.Exit(1)
337
+ else:
338
+ llm_api_key = os.environ.get("ANTHROPIC_API_KEY", "")
339
+ if not llm_api_key:
340
+ print_error("[AUTH_ERROR] ANTHROPIC_API_KEY environment variable is required for LLM synthesis.")
341
+ raise typer.Exit(1)
342
+
343
+ reply_service = _make_reply_service()
344
+ github_client = _github_client_optional(repo_root)
345
+ diff_summary_service = _make_diff_summary_service()
346
+ orchestrator = AskOrchestrator(
347
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
348
+ llm_client=LLMClient(api_key=llm_api_key, provider=provider),
349
+ repo_root=repo_root,
350
+ reply_service=reply_service,
351
+ github_client=github_client,
352
+ diff_summary_service=diff_summary_service,
353
+ )
354
+ code = orchestrator.run("_/_", question, json_output=json_output)
355
+ raise typer.Exit(code)
356
+
357
+
358
+ @app.command()
359
+ def history(
360
+ ctx: typer.Context,
361
+ subject: str = typer.Argument(..., help="Subject or topic for chronological history."),
362
+ ) -> None:
363
+ """Get a chronological history of a subject in the repository."""
364
+ from avos_cli.commands.history import HistoryOrchestrator
365
+ from avos_cli.config.manager import find_repo_root, load_config
366
+ from avos_cli.exceptions import (
367
+ ConfigurationNotInitializedError,
368
+ RepositoryContextError,
369
+ )
370
+ from avos_cli.services.llm_client import LLMClient
371
+ from avos_cli.services.memory_client import AvosMemoryClient
372
+
373
+ json_output = ctx.obj.get("json", False)
374
+
375
+ api_key = os.environ.get("AVOS_API_KEY", "")
376
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
377
+
378
+ if not api_key:
379
+ print_error("[AUTH_ERROR] AVOS_API_KEY environment variable is required.")
380
+ raise typer.Exit(1)
381
+
382
+ try:
383
+ repo_root = find_repo_root(Path.cwd())
384
+ except RepositoryContextError as e:
385
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
386
+ raise typer.Exit(1) from e
387
+
388
+ try:
389
+ config = load_config(repo_root)
390
+ except ConfigurationNotInitializedError as e:
391
+ print_error("[AUTH_ERROR] Repository not connected. Run 'avos connect org/repo' first.")
392
+ raise typer.Exit(1) from e
393
+
394
+ provider = config.llm.provider.lower()
395
+ if provider == "openai":
396
+ llm_api_key = os.environ.get("OPENAI_API_KEY", "")
397
+ if not llm_api_key:
398
+ print_error("[AUTH_ERROR] OPENAI_API_KEY environment variable is required for OpenAI.")
399
+ raise typer.Exit(1)
400
+ else:
401
+ llm_api_key = os.environ.get("ANTHROPIC_API_KEY", "")
402
+ if not llm_api_key:
403
+ print_error("[AUTH_ERROR] ANTHROPIC_API_KEY environment variable is required for LLM synthesis.")
404
+ raise typer.Exit(1)
405
+
406
+ reply_service = _make_reply_service()
407
+ github_client = _github_client_optional(repo_root)
408
+ diff_summary_service = _make_diff_summary_service()
409
+ orchestrator = HistoryOrchestrator(
410
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
411
+ llm_client=LLMClient(api_key=llm_api_key, provider=provider),
412
+ repo_root=repo_root,
413
+ reply_service=reply_service,
414
+ github_client=github_client,
415
+ diff_summary_service=diff_summary_service,
416
+ )
417
+ code = orchestrator.run("_/_", subject, json_output=json_output)
418
+ raise typer.Exit(code)
419
+
420
+
421
+ @app.command(name="hook-install")
422
+ def hook_install(
423
+ force: bool = typer.Option(
424
+ False, "--force", "-f", help="Overwrite existing pre-push hook."
425
+ ),
426
+ ) -> None:
427
+ """Install git hook for automatic commit sync on push."""
428
+ from avos_cli.commands.hook_install import HookInstallOrchestrator
429
+ from avos_cli.config.manager import find_repo_root
430
+ from avos_cli.exceptions import RepositoryContextError
431
+ from avos_cli.services.git_client import GitClient
432
+
433
+ try:
434
+ repo_root = find_repo_root(Path.cwd())
435
+ except RepositoryContextError as e:
436
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
437
+ raise typer.Exit(1) from e
438
+
439
+ orchestrator = HookInstallOrchestrator(
440
+ git_client=GitClient(),
441
+ repo_root=repo_root,
442
+ )
443
+ code = orchestrator.run(force=force)
444
+ raise typer.Exit(code)
445
+
446
+
447
+ @app.command(name="hook-uninstall")
448
+ def hook_uninstall() -> None:
449
+ """Remove the avos pre-push git hook."""
450
+ from avos_cli.commands.hook_install import HookUninstallOrchestrator
451
+ from avos_cli.config.manager import find_repo_root
452
+ from avos_cli.exceptions import RepositoryContextError
453
+
454
+ try:
455
+ repo_root = find_repo_root(Path.cwd())
456
+ except RepositoryContextError as e:
457
+ print_error(f"[REPOSITORY_CONTEXT_ERROR] {e}")
458
+ raise typer.Exit(1) from e
459
+
460
+ orchestrator = HookUninstallOrchestrator(repo_root=repo_root)
461
+ code = orchestrator.run()
462
+ raise typer.Exit(code)
463
+
464
+
465
+ @app.command(name="hook-sync", hidden=True)
466
+ def hook_sync(
467
+ old_sha: str = typer.Argument(..., help="Base commit SHA (remote has this)."),
468
+ new_sha: str = typer.Argument(..., help="Target commit SHA (pushing this)."),
469
+ ) -> None:
470
+ """Sync commits to Avos Memory (called by pre-push hook)."""
471
+ from avos_cli.commands.hook_sync import HookSyncOrchestrator
472
+ from avos_cli.config.hash_store import IngestHashStore
473
+ from avos_cli.config.manager import find_repo_root
474
+ from avos_cli.exceptions import RepositoryContextError
475
+ from avos_cli.services.git_client import GitClient
476
+ from avos_cli.services.memory_client import AvosMemoryClient
477
+
478
+ api_key = os.environ.get("AVOS_API_KEY", "")
479
+ api_url = os.environ.get("AVOS_API_URL", "https://api.avos.ai")
480
+
481
+ if not api_key:
482
+ return
483
+
484
+ try:
485
+ repo_root = find_repo_root(Path.cwd())
486
+ except RepositoryContextError:
487
+ return
488
+
489
+ avos_dir = repo_root / ".avos"
490
+ hash_store = IngestHashStore(avos_dir)
491
+ hash_store.load()
492
+
493
+ orchestrator = HookSyncOrchestrator(
494
+ memory_client=AvosMemoryClient(api_key=api_key, api_url=api_url),
495
+ git_client=GitClient(),
496
+ hash_store=hash_store,
497
+ repo_root=repo_root,
498
+ )
499
+ code = orchestrator.run(old_sha, new_sha)
500
+ raise typer.Exit(code)
501
+
502
+
503
+ if __name__ == "__main__":
504
+ app()
@@ -0,0 +1 @@
1
+