nao-core 0.0.29__py3-none-any.whl → 0.0.31__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 (73) hide show
  1. nao_core/__init__.py +1 -1
  2. nao_core/bin/fastapi/main.py +21 -2
  3. nao_core/bin/fastapi/test_main.py +156 -0
  4. nao_core/bin/migrations-postgres/0004_input_and_output_tokens.sql +8 -0
  5. nao_core/bin/migrations-postgres/0005_add_project_tables.sql +39 -0
  6. nao_core/bin/migrations-postgres/meta/0004_snapshot.json +847 -0
  7. nao_core/bin/migrations-postgres/meta/0005_snapshot.json +1129 -0
  8. nao_core/bin/migrations-postgres/meta/_journal.json +14 -0
  9. nao_core/bin/migrations-sqlite/0004_input_and_output_tokens.sql +8 -0
  10. nao_core/bin/migrations-sqlite/0005_add_project_tables.sql +38 -0
  11. nao_core/bin/migrations-sqlite/meta/0004_snapshot.json +819 -0
  12. nao_core/bin/migrations-sqlite/meta/0005_snapshot.json +1086 -0
  13. nao_core/bin/migrations-sqlite/meta/_journal.json +14 -0
  14. nao_core/bin/nao-chat-server +0 -0
  15. nao_core/bin/public/assets/{code-block-F6WJLWQG-EQr6mTlQ.js → code-block-F6WJLWQG-TAi8koem.js} +3 -3
  16. nao_core/bin/public/assets/index-BfHcd9Xz.css +1 -0
  17. nao_core/bin/public/assets/index-Mzo9bkag.js +557 -0
  18. nao_core/bin/public/index.html +2 -2
  19. nao_core/commands/chat.py +11 -10
  20. nao_core/commands/debug.py +10 -6
  21. nao_core/commands/init.py +66 -27
  22. nao_core/commands/sync/__init__.py +40 -21
  23. nao_core/commands/sync/accessors.py +219 -141
  24. nao_core/commands/sync/cleanup.py +133 -0
  25. nao_core/commands/sync/providers/__init__.py +30 -0
  26. nao_core/commands/sync/providers/base.py +87 -0
  27. nao_core/commands/sync/providers/databases/__init__.py +17 -0
  28. nao_core/commands/sync/providers/databases/bigquery.py +78 -0
  29. nao_core/commands/sync/providers/databases/databricks.py +79 -0
  30. nao_core/commands/sync/providers/databases/duckdb.py +83 -0
  31. nao_core/commands/sync/providers/databases/postgres.py +78 -0
  32. nao_core/commands/sync/providers/databases/provider.py +123 -0
  33. nao_core/commands/sync/providers/databases/snowflake.py +78 -0
  34. nao_core/commands/sync/providers/repositories/__init__.py +5 -0
  35. nao_core/commands/sync/{repositories.py → providers/repositories/provider.py} +43 -20
  36. nao_core/config/__init__.py +16 -1
  37. nao_core/config/base.py +23 -4
  38. nao_core/config/databases/__init__.py +37 -11
  39. nao_core/config/databases/base.py +7 -0
  40. nao_core/config/databases/bigquery.py +29 -1
  41. nao_core/config/databases/databricks.py +69 -0
  42. nao_core/config/databases/duckdb.py +33 -0
  43. nao_core/config/databases/postgres.py +78 -0
  44. nao_core/config/databases/snowflake.py +115 -0
  45. nao_core/config/exceptions.py +7 -0
  46. nao_core/templates/__init__.py +12 -0
  47. nao_core/templates/defaults/databases/columns.md.j2 +23 -0
  48. nao_core/templates/defaults/databases/description.md.j2 +32 -0
  49. nao_core/templates/defaults/databases/preview.md.j2 +22 -0
  50. nao_core/templates/defaults/databases/profiling.md.j2 +34 -0
  51. nao_core/templates/engine.py +133 -0
  52. {nao_core-0.0.29.dist-info → nao_core-0.0.31.dist-info}/METADATA +9 -4
  53. nao_core-0.0.31.dist-info/RECORD +86 -0
  54. nao_core/bin/public/assets/_chat-layout-BTlqRUE5.js +0 -1
  55. nao_core/bin/public/assets/_chat-layout.index-DOARokp1.js +0 -1
  56. nao_core/bin/public/assets/agentProvider-C6dGIy-H.js +0 -1
  57. nao_core/bin/public/assets/button-By_1dzVx.js +0 -1
  58. nao_core/bin/public/assets/folder-DnRS5rg3.js +0 -1
  59. nao_core/bin/public/assets/index-CElAN2SH.css +0 -1
  60. nao_core/bin/public/assets/index-ZTHASguQ.js +0 -59
  61. nao_core/bin/public/assets/input-CUQA5tsi.js +0 -1
  62. nao_core/bin/public/assets/login-BUQDum3t.js +0 -1
  63. nao_core/bin/public/assets/mermaid-FSSLJTFX-Dc6ZvCPw.js +0 -427
  64. nao_core/bin/public/assets/sidebar-bgEk7Xg8.js +0 -1
  65. nao_core/bin/public/assets/signinForm-CGAhnAkv.js +0 -1
  66. nao_core/bin/public/assets/signup-D2n11La3.js +0 -1
  67. nao_core/bin/public/assets/user-CYl8Tly2.js +0 -1
  68. nao_core/bin/public/assets/utils-DzJYey0s.js +0 -1
  69. nao_core/commands/sync/databases.py +0 -132
  70. nao_core-0.0.29.dist-info/RECORD +0 -69
  71. {nao_core-0.0.29.dist-info → nao_core-0.0.31.dist-info}/WHEEL +0 -0
  72. {nao_core-0.0.29.dist-info → nao_core-0.0.31.dist-info}/entry_points.txt +0 -0
  73. {nao_core-0.0.29.dist-info → nao_core-0.0.31.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,17 @@
1
+ """Database syncing functionality for generating markdown documentation from database schemas."""
2
+
3
+ from .bigquery import sync_bigquery
4
+ from .databricks import sync_databricks
5
+ from .duckdb import sync_duckdb
6
+ from .postgres import sync_postgres
7
+ from .provider import DatabaseSyncProvider
8
+ from .snowflake import sync_snowflake
9
+
10
+ __all__ = [
11
+ "DatabaseSyncProvider",
12
+ "sync_bigquery",
13
+ "sync_databricks",
14
+ "sync_duckdb",
15
+ "sync_postgres",
16
+ "sync_snowflake",
17
+ ]
@@ -0,0 +1,78 @@
1
+ from pathlib import Path
2
+
3
+ from rich.progress import Progress
4
+
5
+ from nao_core.commands.sync.accessors import DataAccessor
6
+ from nao_core.commands.sync.cleanup import DatabaseSyncState
7
+
8
+
9
+ def sync_bigquery(
10
+ db_config,
11
+ base_path: Path,
12
+ progress: Progress,
13
+ accessors: list[DataAccessor],
14
+ ) -> DatabaseSyncState:
15
+ """Sync BigQuery database schema to markdown files.
16
+
17
+ Args:
18
+ db_config: The database configuration
19
+ base_path: Base output path
20
+ progress: Rich progress instance
21
+ accessors: List of data accessors to run
22
+
23
+ Returns:
24
+ DatabaseSyncState with sync results and tracked paths
25
+ """
26
+ conn = db_config.connect()
27
+ db_path = base_path / "type=bigquery" / f"database={db_config.project_id}"
28
+ state = DatabaseSyncState(db_path=db_path)
29
+
30
+ if db_config.dataset_id:
31
+ datasets = [db_config.dataset_id]
32
+ else:
33
+ datasets = conn.list_databases()
34
+
35
+ dataset_task = progress.add_task(
36
+ f"[dim]{db_config.name}[/dim]",
37
+ total=len(datasets),
38
+ )
39
+
40
+ for dataset in datasets:
41
+ try:
42
+ all_tables = conn.list_tables(database=dataset)
43
+ except Exception:
44
+ progress.update(dataset_task, advance=1)
45
+ continue
46
+
47
+ # Filter tables based on include/exclude patterns
48
+ tables = [t for t in all_tables if db_config.matches_pattern(dataset, t)]
49
+
50
+ # Skip dataset if no tables match
51
+ if not tables:
52
+ progress.update(dataset_task, advance=1)
53
+ continue
54
+
55
+ dataset_path = db_path / f"schema={dataset}"
56
+ dataset_path.mkdir(parents=True, exist_ok=True)
57
+ state.add_schema(dataset)
58
+
59
+ table_task = progress.add_task(
60
+ f" [cyan]{dataset}[/cyan]",
61
+ total=len(tables),
62
+ )
63
+
64
+ for table in tables:
65
+ table_path = dataset_path / f"table={table}"
66
+ table_path.mkdir(parents=True, exist_ok=True)
67
+
68
+ for accessor in accessors:
69
+ content = accessor.generate(conn, dataset, table)
70
+ output_file = table_path / accessor.filename
71
+ output_file.write_text(content)
72
+
73
+ state.add_table(dataset, table)
74
+ progress.update(table_task, advance=1)
75
+
76
+ progress.update(dataset_task, advance=1)
77
+
78
+ return state
@@ -0,0 +1,79 @@
1
+ from pathlib import Path
2
+
3
+ from rich.progress import Progress
4
+
5
+ from nao_core.commands.sync.accessors import DataAccessor
6
+ from nao_core.commands.sync.cleanup import DatabaseSyncState
7
+
8
+
9
+ def sync_databricks(
10
+ db_config,
11
+ base_path: Path,
12
+ progress: Progress,
13
+ accessors: list[DataAccessor],
14
+ ) -> DatabaseSyncState:
15
+ """Sync Databricks database schema to markdown files.
16
+
17
+ Args:
18
+ db_config: The database configuration
19
+ base_path: Base output path
20
+ progress: Rich progress instance
21
+ accessors: List of data accessors to run
22
+
23
+ Returns:
24
+ DatabaseSyncState with sync results and tracked paths
25
+ """
26
+ conn = db_config.connect()
27
+ catalog = db_config.catalog or "main"
28
+ db_path = base_path / "type=databricks" / f"database={catalog}"
29
+ state = DatabaseSyncState(db_path=db_path)
30
+
31
+ if db_config.schema:
32
+ schemas = [db_config.schema]
33
+ else:
34
+ schemas = conn.list_databases()
35
+
36
+ schema_task = progress.add_task(
37
+ f"[dim]{db_config.name}[/dim]",
38
+ total=len(schemas),
39
+ )
40
+
41
+ for schema in schemas:
42
+ try:
43
+ all_tables = conn.list_tables(database=schema)
44
+ except Exception:
45
+ progress.update(schema_task, advance=1)
46
+ continue
47
+
48
+ # Filter tables based on include/exclude patterns
49
+ tables = [t for t in all_tables if db_config.matches_pattern(schema, t)]
50
+
51
+ # Skip schema if no tables match
52
+ if not tables:
53
+ progress.update(schema_task, advance=1)
54
+ continue
55
+
56
+ schema_path = db_path / f"schema={schema}"
57
+ schema_path.mkdir(parents=True, exist_ok=True)
58
+ state.add_schema(schema)
59
+
60
+ table_task = progress.add_task(
61
+ f" [cyan]{schema}[/cyan]",
62
+ total=len(tables),
63
+ )
64
+
65
+ for table in tables:
66
+ table_path = schema_path / f"table={table}"
67
+ table_path.mkdir(parents=True, exist_ok=True)
68
+
69
+ for accessor in accessors:
70
+ content = accessor.generate(conn, schema, table)
71
+ output_file = table_path / accessor.filename
72
+ output_file.write_text(content)
73
+
74
+ state.add_table(schema, table)
75
+ progress.update(table_task, advance=1)
76
+
77
+ progress.update(schema_task, advance=1)
78
+
79
+ return state
@@ -0,0 +1,83 @@
1
+ from pathlib import Path
2
+
3
+ from rich.progress import Progress
4
+
5
+ from nao_core.commands.sync.accessors import DataAccessor
6
+ from nao_core.commands.sync.cleanup import DatabaseSyncState
7
+
8
+
9
+ def sync_duckdb(
10
+ db_config,
11
+ base_path: Path,
12
+ progress: Progress,
13
+ accessors: list[DataAccessor],
14
+ ) -> DatabaseSyncState:
15
+ """Sync DuckDB database schema to markdown files.
16
+
17
+ Args:
18
+ db_config: The database configuration
19
+ base_path: Base output path
20
+ progress: Rich progress instance
21
+ accessors: List of data accessors to run
22
+
23
+ Returns:
24
+ DatabaseSyncState with sync results and tracked paths
25
+ """
26
+ conn = db_config.connect()
27
+
28
+ # Derive database name from path
29
+ if db_config.path == ":memory:":
30
+ db_name = "memory"
31
+ else:
32
+ db_name = Path(db_config.path).stem
33
+
34
+ db_path = base_path / "type=duckdb" / f"database={db_name}"
35
+ state = DatabaseSyncState(db_path=db_path)
36
+
37
+ # List all schemas in DuckDB
38
+ schemas = conn.list_databases()
39
+
40
+ schema_task = progress.add_task(
41
+ f"[dim]{db_config.name}[/dim]",
42
+ total=len(schemas),
43
+ )
44
+
45
+ for schema in schemas:
46
+ try:
47
+ all_tables = conn.list_tables(database=schema)
48
+ except Exception:
49
+ progress.update(schema_task, advance=1)
50
+ continue
51
+
52
+ # Filter tables based on include/exclude patterns
53
+ tables = [t for t in all_tables if db_config.matches_pattern(schema, t)]
54
+
55
+ # Skip schema if no tables match
56
+ if not tables:
57
+ progress.update(schema_task, advance=1)
58
+ continue
59
+
60
+ schema_path = db_path / f"schema={schema}"
61
+ schema_path.mkdir(parents=True, exist_ok=True)
62
+ state.add_schema(schema)
63
+
64
+ table_task = progress.add_task(
65
+ f" [cyan]{schema}[/cyan]",
66
+ total=len(tables),
67
+ )
68
+
69
+ for table in tables:
70
+ table_path = schema_path / f"table={table}"
71
+ table_path.mkdir(parents=True, exist_ok=True)
72
+
73
+ for accessor in accessors:
74
+ content = accessor.generate(conn, schema, table)
75
+ output_file = table_path / accessor.filename
76
+ output_file.write_text(content)
77
+
78
+ state.add_table(schema, table)
79
+ progress.update(table_task, advance=1)
80
+
81
+ progress.update(schema_task, advance=1)
82
+
83
+ return state
@@ -0,0 +1,78 @@
1
+ from pathlib import Path
2
+
3
+ from rich.progress import Progress
4
+
5
+ from nao_core.commands.sync.accessors import DataAccessor
6
+ from nao_core.commands.sync.cleanup import DatabaseSyncState
7
+
8
+
9
+ def sync_postgres(
10
+ db_config,
11
+ base_path: Path,
12
+ progress: Progress,
13
+ accessors: list[DataAccessor],
14
+ ) -> DatabaseSyncState:
15
+ """Sync PostgreSQL database schema to markdown files.
16
+
17
+ Args:
18
+ db_config: The database configuration
19
+ base_path: Base output path
20
+ progress: Rich progress instance
21
+ accessors: List of data accessors to run
22
+
23
+ Returns:
24
+ DatabaseSyncState with sync results and tracked paths
25
+ """
26
+ conn = db_config.connect()
27
+ db_path = base_path / "type=postgres" / f"database={db_config.database}"
28
+ state = DatabaseSyncState(db_path=db_path)
29
+
30
+ if db_config.schema_name:
31
+ schemas = [db_config.schema_name]
32
+ else:
33
+ schemas = conn.list_databases()
34
+
35
+ schema_task = progress.add_task(
36
+ f"[dim]{db_config.name}[/dim]",
37
+ total=len(schemas),
38
+ )
39
+
40
+ for schema in schemas:
41
+ try:
42
+ all_tables = conn.list_tables(database=schema)
43
+ except Exception:
44
+ progress.update(schema_task, advance=1)
45
+ continue
46
+
47
+ # Filter tables based on include/exclude patterns
48
+ tables = [t for t in all_tables if db_config.matches_pattern(schema, t)]
49
+
50
+ # Skip schema if no tables match
51
+ if not tables:
52
+ progress.update(schema_task, advance=1)
53
+ continue
54
+
55
+ schema_path = db_path / f"schema={schema}"
56
+ schema_path.mkdir(parents=True, exist_ok=True)
57
+ state.add_schema(schema)
58
+
59
+ table_task = progress.add_task(
60
+ f" [cyan]{schema}[/cyan]",
61
+ total=len(tables),
62
+ )
63
+
64
+ for table in tables:
65
+ table_path = schema_path / f"table={table}"
66
+ table_path.mkdir(parents=True, exist_ok=True)
67
+
68
+ for accessor in accessors:
69
+ content = accessor.generate(conn, schema, table)
70
+ output_file = table_path / accessor.filename
71
+ output_file.write_text(content)
72
+
73
+ state.add_table(schema, table)
74
+ progress.update(table_task, advance=1)
75
+
76
+ progress.update(schema_task, advance=1)
77
+
78
+ return state
@@ -0,0 +1,123 @@
1
+ """Database sync provider implementation."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from rich.console import Console
7
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
8
+
9
+ from nao_core.commands.sync.accessors import DataAccessor
10
+ from nao_core.commands.sync.cleanup import DatabaseSyncState, cleanup_stale_paths
11
+ from nao_core.commands.sync.registry import get_accessors
12
+ from nao_core.config import AnyDatabaseConfig, NaoConfig
13
+
14
+ from ..base import SyncProvider, SyncResult
15
+ from .bigquery import sync_bigquery
16
+ from .databricks import sync_databricks
17
+ from .duckdb import sync_duckdb
18
+ from .postgres import sync_postgres
19
+ from .snowflake import sync_snowflake
20
+
21
+ console = Console()
22
+
23
+ # Registry mapping database types to their sync functions
24
+ DATABASE_SYNC_FUNCTIONS = {
25
+ "bigquery": sync_bigquery,
26
+ "duckdb": sync_duckdb,
27
+ "databricks": sync_databricks,
28
+ "snowflake": sync_snowflake,
29
+ "postgres": sync_postgres,
30
+ }
31
+
32
+
33
+ class DatabaseSyncProvider(SyncProvider):
34
+ """Provider for syncing database schemas to markdown documentation."""
35
+
36
+ @property
37
+ def name(self) -> str:
38
+ return "Databases"
39
+
40
+ @property
41
+ def emoji(self) -> str:
42
+ return "🗄️"
43
+
44
+ @property
45
+ def default_output_dir(self) -> str:
46
+ return "databases"
47
+
48
+ def get_items(self, config: NaoConfig) -> list[AnyDatabaseConfig]:
49
+ return config.databases
50
+
51
+ def sync(self, items: list[Any], output_path: Path, project_path: Path | None = None) -> SyncResult:
52
+ """Sync all configured databases.
53
+
54
+ Args:
55
+ items: List of database configurations
56
+ output_path: Base path where database schemas are stored
57
+ project_path: Path to the nao project root (for template resolution)
58
+
59
+ Returns:
60
+ SyncResult with datasets and tables synced
61
+ """
62
+ if not items:
63
+ console.print("\n[dim]No databases configured[/dim]")
64
+ return SyncResult(provider_name=self.name, items_synced=0)
65
+
66
+ # Set project path for template resolution
67
+ DataAccessor.set_project_path(project_path)
68
+
69
+ total_datasets = 0
70
+ total_tables = 0
71
+ total_removed = 0
72
+ sync_states: list[DatabaseSyncState] = []
73
+
74
+ console.print(f"\n[bold cyan]{self.emoji} Syncing {self.name}[/bold cyan]")
75
+ console.print(f"[dim]Location:[/dim] {output_path.absolute()}\n")
76
+
77
+ with Progress(
78
+ SpinnerColumn(style="dim"),
79
+ TextColumn("[progress.description]{task.description}"),
80
+ BarColumn(bar_width=30, style="dim", complete_style="cyan", finished_style="green"),
81
+ TaskProgressColumn(),
82
+ console=console,
83
+ transient=False,
84
+ ) as progress:
85
+ for db in items:
86
+ # Get accessors from database config
87
+ db_accessors = get_accessors(db.accessors)
88
+ accessor_names = [a.filename.replace(".md", "") for a in db_accessors]
89
+
90
+ try:
91
+ console.print(f"[dim]{db.name} accessors:[/dim] {', '.join(accessor_names)}")
92
+
93
+ sync_fn = DATABASE_SYNC_FUNCTIONS.get(db.type)
94
+ if sync_fn:
95
+ state = sync_fn(db, output_path, progress, db_accessors)
96
+ sync_states.append(state)
97
+ total_datasets += state.schemas_synced
98
+ total_tables += state.tables_synced
99
+ else:
100
+ console.print(f"[yellow]⚠ Unsupported database type: {db.type}[/yellow]")
101
+ except Exception as e:
102
+ console.print(f"[bold red]✗[/bold red] Failed to sync {db.name}: {e}")
103
+
104
+ # Clean up stale files after all syncs complete
105
+ for state in sync_states:
106
+ removed = cleanup_stale_paths(state, verbose=True)
107
+ total_removed += removed
108
+
109
+ # Build summary
110
+ summary = f"{total_tables} tables across {total_datasets} datasets"
111
+ if total_removed > 0:
112
+ summary += f", {total_removed} stale removed"
113
+
114
+ return SyncResult(
115
+ provider_name=self.name,
116
+ items_synced=total_tables,
117
+ details={
118
+ "datasets": total_datasets,
119
+ "tables": total_tables,
120
+ "removed": total_removed,
121
+ },
122
+ summary=summary,
123
+ )
@@ -0,0 +1,78 @@
1
+ from pathlib import Path
2
+
3
+ from rich.progress import Progress
4
+
5
+ from nao_core.commands.sync.accessors import DataAccessor
6
+ from nao_core.commands.sync.cleanup import DatabaseSyncState
7
+
8
+
9
+ def sync_snowflake(
10
+ db_config,
11
+ base_path: Path,
12
+ progress: Progress,
13
+ accessors: list[DataAccessor],
14
+ ) -> DatabaseSyncState:
15
+ """Sync Snowflake database schema to markdown files.
16
+
17
+ Args:
18
+ db_config: The database configuration
19
+ base_path: Base output path
20
+ progress: Rich progress instance
21
+ accessors: List of data accessors to run
22
+
23
+ Returns:
24
+ DatabaseSyncState with sync results and tracked paths
25
+ """
26
+ conn = db_config.connect()
27
+ db_path = base_path / "type=snowflake" / f"database={db_config.database}"
28
+ state = DatabaseSyncState(db_path=db_path)
29
+
30
+ if db_config.schema:
31
+ schemas = [db_config.schema]
32
+ else:
33
+ schemas = conn.list_databases()
34
+
35
+ schema_task = progress.add_task(
36
+ f"[dim]{db_config.name}[/dim]",
37
+ total=len(schemas),
38
+ )
39
+
40
+ for schema in schemas:
41
+ try:
42
+ all_tables = conn.list_tables(database=schema)
43
+ except Exception:
44
+ progress.update(schema_task, advance=1)
45
+ continue
46
+
47
+ # Filter tables based on include/exclude patterns
48
+ tables = [t for t in all_tables if db_config.matches_pattern(schema, t)]
49
+
50
+ # Skip schema if no tables match
51
+ if not tables:
52
+ progress.update(schema_task, advance=1)
53
+ continue
54
+
55
+ schema_path = db_path / f"schema={schema}"
56
+ schema_path.mkdir(parents=True, exist_ok=True)
57
+ state.add_schema(schema)
58
+
59
+ table_task = progress.add_task(
60
+ f" [cyan]{schema}[/cyan]",
61
+ total=len(tables),
62
+ )
63
+
64
+ for table in tables:
65
+ table_path = schema_path / f"table={table}"
66
+ table_path.mkdir(parents=True, exist_ok=True)
67
+
68
+ for accessor in accessors:
69
+ content = accessor.generate(conn, schema, table)
70
+ output_file = table_path / accessor.filename
71
+ output_file.write_text(content)
72
+
73
+ state.add_table(schema, table)
74
+ progress.update(table_task, advance=1)
75
+
76
+ progress.update(schema_task, advance=1)
77
+
78
+ return state
@@ -0,0 +1,5 @@
1
+ """Repository syncing functionality for cloning and pulling git repositories."""
2
+
3
+ from .provider import RepositorySyncProvider
4
+
5
+ __all__ = ["RepositorySyncProvider"]
@@ -1,12 +1,16 @@
1
- """Repository syncing functionality for cloning and pulling git repositories."""
1
+ """Repository sync provider implementation."""
2
2
 
3
3
  import subprocess
4
4
  from pathlib import Path
5
+ from typing import Any
5
6
 
6
7
  from rich.console import Console
7
8
 
9
+ from nao_core.config import NaoConfig
8
10
  from nao_core.config.repos import RepoConfig
9
11
 
12
+ from ..base import SyncProvider, SyncResult
13
+
10
14
  console = Console()
11
15
 
12
16
 
@@ -76,28 +80,47 @@ def clone_or_pull_repo(repo: RepoConfig, base_path: Path) -> bool:
76
80
  return False
77
81
 
78
82
 
79
- def sync_repositories(repos: list[RepoConfig], base_path: Path) -> int:
80
- """Sync all configured repositories.
83
+ class RepositorySyncProvider(SyncProvider):
84
+ """Provider for syncing git repositories."""
81
85
 
82
- Args:
83
- repos: List of repository configurations
84
- base_path: Base path where repositories are stored
86
+ @property
87
+ def name(self) -> str:
88
+ return "Repositories"
85
89
 
86
- Returns:
87
- Number of successfully synced repositories
88
- """
89
- if not repos:
90
- return 0
90
+ @property
91
+ def emoji(self) -> str:
92
+ return "📦"
93
+
94
+ @property
95
+ def default_output_dir(self) -> str:
96
+ return "repos"
97
+
98
+ def get_items(self, config: NaoConfig) -> list[RepoConfig]:
99
+ return config.repos
100
+
101
+ def sync(self, items: list[Any], output_path: Path, project_path: Path | None = None) -> SyncResult:
102
+ """Sync all configured repositories.
103
+
104
+ Args:
105
+ items: List of repository configurations
106
+ output_path: Base path where repositories are stored
107
+ project_path: Path to the nao project root (unused for repos)
108
+
109
+ Returns:
110
+ SyncResult with number of successfully synced repositories
111
+ """
112
+ if not items:
113
+ return SyncResult(provider_name=self.name, items_synced=0)
91
114
 
92
- base_path.mkdir(parents=True, exist_ok=True)
93
- success_count = 0
115
+ output_path.mkdir(parents=True, exist_ok=True)
116
+ success_count = 0
94
117
 
95
- console.print("\n[bold cyan]📦 Syncing Repositories[/bold cyan]")
96
- console.print(f"[dim]Location:[/dim] {base_path.absolute()}\n")
118
+ console.print(f"\n[bold cyan]{self.emoji} Syncing {self.name}[/bold cyan]")
119
+ console.print(f"[dim]Location:[/dim] {output_path.absolute()}\n")
97
120
 
98
- for repo in repos:
99
- if clone_or_pull_repo(repo, base_path):
100
- success_count += 1
101
- console.print(f" [green]✓[/green] {repo.name}")
121
+ for repo in items:
122
+ if clone_or_pull_repo(repo, output_path):
123
+ success_count += 1
124
+ console.print(f" [green]✓[/green] {repo.name}")
102
125
 
103
- return success_count
126
+ return SyncResult(provider_name=self.name, items_synced=success_count)
@@ -1,5 +1,15 @@
1
1
  from .base import NaoConfig
2
- from .databases import AccessorType, AnyDatabaseConfig, BigQueryConfig, DatabaseType
2
+ from .databases import (
3
+ AccessorType,
4
+ AnyDatabaseConfig,
5
+ BigQueryConfig,
6
+ DatabaseType,
7
+ DatabricksConfig,
8
+ DuckDBConfig,
9
+ PostgresConfig,
10
+ SnowflakeConfig,
11
+ )
12
+ from .exceptions import InitError
3
13
  from .llm import LLMConfig, LLMProvider
4
14
  from .slack import SlackConfig
5
15
 
@@ -8,8 +18,13 @@ __all__ = [
8
18
  "AccessorType",
9
19
  "AnyDatabaseConfig",
10
20
  "BigQueryConfig",
21
+ "DuckDBConfig",
22
+ "DatabricksConfig",
23
+ "SnowflakeConfig",
24
+ "PostgresConfig",
11
25
  "DatabaseType",
12
26
  "LLMConfig",
13
27
  "LLMProvider",
14
28
  "SlackConfig",
29
+ "InitError",
15
30
  ]