lestash 1.48.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.
- lestash-1.48.3/.gitignore +20 -0
- lestash-1.48.3/PKG-INFO +194 -0
- lestash-1.48.3/README.md +176 -0
- lestash-1.48.3/pyproject.toml +29 -0
- lestash-1.48.3/src/lestash/__init__.py +3 -0
- lestash-1.48.3/src/lestash/__main__.py +6 -0
- lestash-1.48.3/src/lestash/cli/__init__.py +1 -0
- lestash-1.48.3/src/lestash/cli/config.py +85 -0
- lestash-1.48.3/src/lestash/cli/db.py +525 -0
- lestash-1.48.3/src/lestash/cli/embeddings.py +51 -0
- lestash-1.48.3/src/lestash/cli/google.py +199 -0
- lestash-1.48.3/src/lestash/cli/items.py +385 -0
- lestash-1.48.3/src/lestash/cli/main.py +61 -0
- lestash-1.48.3/src/lestash/cli/media.py +220 -0
- lestash-1.48.3/src/lestash/cli/profiles.py +96 -0
- lestash-1.48.3/src/lestash/cli/sources.py +273 -0
- lestash-1.48.3/src/lestash/core/__init__.py +10 -0
- lestash-1.48.3/src/lestash/core/config.py +108 -0
- lestash-1.48.3/src/lestash/core/database.py +785 -0
- lestash-1.48.3/src/lestash/core/db_admin.py +568 -0
- lestash-1.48.3/src/lestash/core/embeddings.py +182 -0
- lestash-1.48.3/src/lestash/core/enrichment.py +157 -0
- lestash-1.48.3/src/lestash/core/exceptions.py +21 -0
- lestash-1.48.3/src/lestash/core/google_auth.py +236 -0
- lestash-1.48.3/src/lestash/core/google_drive.py +299 -0
- lestash-1.48.3/src/lestash/core/logging.py +254 -0
- lestash-1.48.3/src/lestash/core/text_extract.py +57 -0
- lestash-1.48.3/src/lestash/models/__init__.py +7 -0
- lestash-1.48.3/src/lestash/models/item.py +81 -0
- lestash-1.48.3/src/lestash/models/source.py +26 -0
- lestash-1.48.3/src/lestash/models/tag.py +10 -0
- lestash-1.48.3/src/lestash/plugins/__init__.py +6 -0
- lestash-1.48.3/src/lestash/plugins/base.py +55 -0
- lestash-1.48.3/src/lestash/plugins/loader.py +32 -0
- lestash-1.48.3/tests/__init__.py +1 -0
- lestash-1.48.3/tests/conftest.py +18 -0
- lestash-1.48.3/tests/test_db_admin.py +357 -0
- lestash-1.48.3/tests/test_history.py +265 -0
- lestash-1.48.3/tests/test_items_display.py +246 -0
- lestash-1.48.3/tests/test_post_cache.py +336 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Tool caches
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
|
|
17
|
+
# Tauri app
|
|
18
|
+
app/node_modules/
|
|
19
|
+
app/src-tauri/target/
|
|
20
|
+
app/src-tauri/gen/schemas/
|
lestash-1.48.3/PKG-INFO
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lestash
|
|
3
|
+
Version: 1.48.3
|
|
4
|
+
Summary: Personal knowledge base CLI - aggregate content from multiple sources
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: docling>=2.84.0
|
|
7
|
+
Requires-Dist: google-api-python-client>=2.0.0
|
|
8
|
+
Requires-Dist: google-auth-httplib2>=0.2.0
|
|
9
|
+
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
10
|
+
Requires-Dist: pydantic-settings>=2.1.0
|
|
11
|
+
Requires-Dist: pydantic>=2.5.0
|
|
12
|
+
Requires-Dist: rich>=13.7.0
|
|
13
|
+
Requires-Dist: sentence-transformers>=3.0.0
|
|
14
|
+
Requires-Dist: sqlite-vec>=0.1.6
|
|
15
|
+
Requires-Dist: toml>=0.10.2
|
|
16
|
+
Requires-Dist: typer>=0.12.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# lestash
|
|
20
|
+
|
|
21
|
+
[](https://pypi.org/project/lestash/)
|
|
22
|
+
[](https://github.com/thompsonson/lestash)
|
|
23
|
+
|
|
24
|
+
Core CLI and plugin system for Le Stash - a personal knowledge base that aggregates content from multiple sources into a unified, searchable database.
|
|
25
|
+
|
|
26
|
+
> **Note**: For project overview, features, and quick start guide, see the [GitHub repository](https://github.com/thompsonson/lestash).
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv add lestash
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install the full workspace from the repository root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv sync --all-packages
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## CLI Commands
|
|
41
|
+
|
|
42
|
+
### Items
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# List all items (with date/time and resolved author names)
|
|
46
|
+
lestash items list
|
|
47
|
+
|
|
48
|
+
# Filter by source
|
|
49
|
+
lestash items list --source linkedin
|
|
50
|
+
|
|
51
|
+
# Search items
|
|
52
|
+
lestash items search "query"
|
|
53
|
+
|
|
54
|
+
# Show item details (includes reaction/comment target URLs)
|
|
55
|
+
lestash items show <id>
|
|
56
|
+
|
|
57
|
+
# Export items to JSON
|
|
58
|
+
lestash items export --output backup.json
|
|
59
|
+
|
|
60
|
+
# Create a Micro.blog draft from an item
|
|
61
|
+
lestash items draft <id> --output ~/blog/content/drafts/
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Profiles
|
|
65
|
+
|
|
66
|
+
Map person URNs (e.g., LinkedIn) to human-readable names and profile URLs:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Add a profile mapping
|
|
70
|
+
lestash profiles add "urn:li:person:abc123" --name "John Doe" --url "https://linkedin.com/in/johndoe"
|
|
71
|
+
|
|
72
|
+
# List all profiles
|
|
73
|
+
lestash profiles list
|
|
74
|
+
|
|
75
|
+
# Show a specific profile
|
|
76
|
+
lestash profiles show "urn:li:person:abc123"
|
|
77
|
+
|
|
78
|
+
# Remove a profile
|
|
79
|
+
lestash profiles remove "urn:li:person:abc123"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
When profiles are configured, `items list` and `items search` display names instead of URNs, and `items show` includes the profile URL.
|
|
83
|
+
|
|
84
|
+
### Sources
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# List configured sources
|
|
88
|
+
lestash sources list
|
|
89
|
+
|
|
90
|
+
# Sync all sources
|
|
91
|
+
lestash sources sync
|
|
92
|
+
|
|
93
|
+
# View sync history
|
|
94
|
+
lestash sources history
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Configuration
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Show current config
|
|
101
|
+
lestash config show
|
|
102
|
+
|
|
103
|
+
# Initialize config file
|
|
104
|
+
lestash config init
|
|
105
|
+
|
|
106
|
+
# Set a config value
|
|
107
|
+
lestash config set logging.level DEBUG
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Plugin Architecture
|
|
111
|
+
|
|
112
|
+
Le Stash uses entry points for plugin discovery. Plugins implement the `SourcePlugin` base class:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from lestash.plugins.base import SourcePlugin
|
|
116
|
+
from lestash.models.item import ItemCreate
|
|
117
|
+
|
|
118
|
+
class MySource(SourcePlugin):
|
|
119
|
+
name = "my-source"
|
|
120
|
+
description = "My custom data source"
|
|
121
|
+
|
|
122
|
+
def get_commands(self) -> typer.Typer:
|
|
123
|
+
"""Return Typer app with source-specific commands."""
|
|
124
|
+
app = typer.Typer()
|
|
125
|
+
# Add commands...
|
|
126
|
+
return app
|
|
127
|
+
|
|
128
|
+
def sync(self, config: dict) -> Iterator[ItemCreate]:
|
|
129
|
+
"""Fetch items from the source."""
|
|
130
|
+
yield ItemCreate(
|
|
131
|
+
source_type="my-source",
|
|
132
|
+
source_id="unique-id",
|
|
133
|
+
content="Item content",
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Register the plugin in `pyproject.toml`:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
[project.entry-points."lestash.sources"]
|
|
141
|
+
my-source = "my_package:MySource"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Data Model
|
|
145
|
+
|
|
146
|
+
### Item
|
|
147
|
+
|
|
148
|
+
The core unit of content:
|
|
149
|
+
|
|
150
|
+
| Field | Type | Description |
|
|
151
|
+
|-------|------|-------------|
|
|
152
|
+
| `id` | int | Auto-generated primary key |
|
|
153
|
+
| `source_type` | str | Plugin identifier (e.g., "arxiv", "linkedin") |
|
|
154
|
+
| `source_id` | str | Unique ID within the source |
|
|
155
|
+
| `url` | str | Optional URL to original content |
|
|
156
|
+
| `title` | str | Optional title |
|
|
157
|
+
| `content` | str | Main text content |
|
|
158
|
+
| `author` | str | Optional author |
|
|
159
|
+
| `created_at` | datetime | When the content was created |
|
|
160
|
+
| `is_own_content` | bool | Whether user authored this |
|
|
161
|
+
| `metadata` | dict | Source-specific data (JSON) |
|
|
162
|
+
| `parent_id` | int | Optional FK to parent item (for grouping children) |
|
|
163
|
+
|
|
164
|
+
### Database
|
|
165
|
+
|
|
166
|
+
SQLite database with:
|
|
167
|
+
|
|
168
|
+
- **items** - Main content table with unique constraint on (source_type, source_id) and optional `parent_id` for hierarchical grouping
|
|
169
|
+
- **items_fts** - Full-text search virtual table (FTS5)
|
|
170
|
+
- **vec_items** - Vector embeddings for semantic search (sqlite-vec)
|
|
171
|
+
- **item_history** - Audit trail of content changes
|
|
172
|
+
- **person_profiles** - URN to name/URL mapping for display
|
|
173
|
+
- **post_cache** - Cached metadata for posts referenced by reactions/comments
|
|
174
|
+
- **tags** / **item_tags** - Tagging system
|
|
175
|
+
- **collections** / **collection_items** - Cross-source item grouping
|
|
176
|
+
- **sources** - Plugin configuration storage
|
|
177
|
+
- **sync_log** - Sync operation history
|
|
178
|
+
|
|
179
|
+
## Configuration
|
|
180
|
+
|
|
181
|
+
Default location: `~/.config/lestash/config.toml`
|
|
182
|
+
|
|
183
|
+
```toml
|
|
184
|
+
[general]
|
|
185
|
+
database_path = "~/.config/lestash/lestash.db"
|
|
186
|
+
|
|
187
|
+
[logging]
|
|
188
|
+
level = "INFO"
|
|
189
|
+
file_path = "~/.config/lestash/logs/lestash.log"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](../../LICENSE)
|
lestash-1.48.3/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# lestash
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/lestash/)
|
|
4
|
+
[](https://github.com/thompsonson/lestash)
|
|
5
|
+
|
|
6
|
+
Core CLI and plugin system for Le Stash - a personal knowledge base that aggregates content from multiple sources into a unified, searchable database.
|
|
7
|
+
|
|
8
|
+
> **Note**: For project overview, features, and quick start guide, see the [GitHub repository](https://github.com/thompsonson/lestash).
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv add lestash
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or install the full workspace from the repository root:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync --all-packages
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## CLI Commands
|
|
23
|
+
|
|
24
|
+
### Items
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# List all items (with date/time and resolved author names)
|
|
28
|
+
lestash items list
|
|
29
|
+
|
|
30
|
+
# Filter by source
|
|
31
|
+
lestash items list --source linkedin
|
|
32
|
+
|
|
33
|
+
# Search items
|
|
34
|
+
lestash items search "query"
|
|
35
|
+
|
|
36
|
+
# Show item details (includes reaction/comment target URLs)
|
|
37
|
+
lestash items show <id>
|
|
38
|
+
|
|
39
|
+
# Export items to JSON
|
|
40
|
+
lestash items export --output backup.json
|
|
41
|
+
|
|
42
|
+
# Create a Micro.blog draft from an item
|
|
43
|
+
lestash items draft <id> --output ~/blog/content/drafts/
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Profiles
|
|
47
|
+
|
|
48
|
+
Map person URNs (e.g., LinkedIn) to human-readable names and profile URLs:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Add a profile mapping
|
|
52
|
+
lestash profiles add "urn:li:person:abc123" --name "John Doe" --url "https://linkedin.com/in/johndoe"
|
|
53
|
+
|
|
54
|
+
# List all profiles
|
|
55
|
+
lestash profiles list
|
|
56
|
+
|
|
57
|
+
# Show a specific profile
|
|
58
|
+
lestash profiles show "urn:li:person:abc123"
|
|
59
|
+
|
|
60
|
+
# Remove a profile
|
|
61
|
+
lestash profiles remove "urn:li:person:abc123"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
When profiles are configured, `items list` and `items search` display names instead of URNs, and `items show` includes the profile URL.
|
|
65
|
+
|
|
66
|
+
### Sources
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# List configured sources
|
|
70
|
+
lestash sources list
|
|
71
|
+
|
|
72
|
+
# Sync all sources
|
|
73
|
+
lestash sources sync
|
|
74
|
+
|
|
75
|
+
# View sync history
|
|
76
|
+
lestash sources history
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Configuration
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Show current config
|
|
83
|
+
lestash config show
|
|
84
|
+
|
|
85
|
+
# Initialize config file
|
|
86
|
+
lestash config init
|
|
87
|
+
|
|
88
|
+
# Set a config value
|
|
89
|
+
lestash config set logging.level DEBUG
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Plugin Architecture
|
|
93
|
+
|
|
94
|
+
Le Stash uses entry points for plugin discovery. Plugins implement the `SourcePlugin` base class:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from lestash.plugins.base import SourcePlugin
|
|
98
|
+
from lestash.models.item import ItemCreate
|
|
99
|
+
|
|
100
|
+
class MySource(SourcePlugin):
|
|
101
|
+
name = "my-source"
|
|
102
|
+
description = "My custom data source"
|
|
103
|
+
|
|
104
|
+
def get_commands(self) -> typer.Typer:
|
|
105
|
+
"""Return Typer app with source-specific commands."""
|
|
106
|
+
app = typer.Typer()
|
|
107
|
+
# Add commands...
|
|
108
|
+
return app
|
|
109
|
+
|
|
110
|
+
def sync(self, config: dict) -> Iterator[ItemCreate]:
|
|
111
|
+
"""Fetch items from the source."""
|
|
112
|
+
yield ItemCreate(
|
|
113
|
+
source_type="my-source",
|
|
114
|
+
source_id="unique-id",
|
|
115
|
+
content="Item content",
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Register the plugin in `pyproject.toml`:
|
|
120
|
+
|
|
121
|
+
```toml
|
|
122
|
+
[project.entry-points."lestash.sources"]
|
|
123
|
+
my-source = "my_package:MySource"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Data Model
|
|
127
|
+
|
|
128
|
+
### Item
|
|
129
|
+
|
|
130
|
+
The core unit of content:
|
|
131
|
+
|
|
132
|
+
| Field | Type | Description |
|
|
133
|
+
|-------|------|-------------|
|
|
134
|
+
| `id` | int | Auto-generated primary key |
|
|
135
|
+
| `source_type` | str | Plugin identifier (e.g., "arxiv", "linkedin") |
|
|
136
|
+
| `source_id` | str | Unique ID within the source |
|
|
137
|
+
| `url` | str | Optional URL to original content |
|
|
138
|
+
| `title` | str | Optional title |
|
|
139
|
+
| `content` | str | Main text content |
|
|
140
|
+
| `author` | str | Optional author |
|
|
141
|
+
| `created_at` | datetime | When the content was created |
|
|
142
|
+
| `is_own_content` | bool | Whether user authored this |
|
|
143
|
+
| `metadata` | dict | Source-specific data (JSON) |
|
|
144
|
+
| `parent_id` | int | Optional FK to parent item (for grouping children) |
|
|
145
|
+
|
|
146
|
+
### Database
|
|
147
|
+
|
|
148
|
+
SQLite database with:
|
|
149
|
+
|
|
150
|
+
- **items** - Main content table with unique constraint on (source_type, source_id) and optional `parent_id` for hierarchical grouping
|
|
151
|
+
- **items_fts** - Full-text search virtual table (FTS5)
|
|
152
|
+
- **vec_items** - Vector embeddings for semantic search (sqlite-vec)
|
|
153
|
+
- **item_history** - Audit trail of content changes
|
|
154
|
+
- **person_profiles** - URN to name/URL mapping for display
|
|
155
|
+
- **post_cache** - Cached metadata for posts referenced by reactions/comments
|
|
156
|
+
- **tags** / **item_tags** - Tagging system
|
|
157
|
+
- **collections** / **collection_items** - Cross-source item grouping
|
|
158
|
+
- **sources** - Plugin configuration storage
|
|
159
|
+
- **sync_log** - Sync operation history
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
Default location: `~/.config/lestash/config.toml`
|
|
164
|
+
|
|
165
|
+
```toml
|
|
166
|
+
[general]
|
|
167
|
+
database_path = "~/.config/lestash/lestash.db"
|
|
168
|
+
|
|
169
|
+
[logging]
|
|
170
|
+
level = "INFO"
|
|
171
|
+
file_path = "~/.config/lestash/logs/lestash.log"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lestash"
|
|
3
|
+
version = "1.48.3"
|
|
4
|
+
description = "Personal knowledge base CLI - aggregate content from multiple sources"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"typer>=0.12.0",
|
|
9
|
+
"pydantic>=2.5.0",
|
|
10
|
+
"pydantic-settings>=2.1.0",
|
|
11
|
+
"rich>=13.7.0",
|
|
12
|
+
"toml>=0.10.2",
|
|
13
|
+
"google-api-python-client>=2.0.0",
|
|
14
|
+
"google-auth-oauthlib>=1.0.0",
|
|
15
|
+
"google-auth-httplib2>=0.2.0",
|
|
16
|
+
"sqlite-vec>=0.1.6",
|
|
17
|
+
"sentence-transformers>=3.0.0",
|
|
18
|
+
"docling>=2.84.0",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
lestash = "lestash.cli.main:app"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/lestash"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI module for Le Stash."""
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Config commands for Le Stash CLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from lestash.core.config import Config, get_config_path, init_config
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(help="Manage configuration.")
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("show")
|
|
15
|
+
def show_config() -> None:
|
|
16
|
+
"""Show current configuration."""
|
|
17
|
+
config_path = get_config_path()
|
|
18
|
+
|
|
19
|
+
if not config_path.exists():
|
|
20
|
+
console.print(f"[dim]No config file at {config_path}[/dim]")
|
|
21
|
+
console.print("[dim]Run 'lestash config init' to create one.[/dim]")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
config = Config.load()
|
|
25
|
+
console.print(f"[bold]Config file:[/bold] {config_path}")
|
|
26
|
+
console.print()
|
|
27
|
+
|
|
28
|
+
data = config.model_dump()
|
|
29
|
+
for section, values in data.items():
|
|
30
|
+
console.print(f"[bold][{section}][/bold]")
|
|
31
|
+
if isinstance(values, dict):
|
|
32
|
+
for key, value in values.items():
|
|
33
|
+
console.print(f" {key} = {value}")
|
|
34
|
+
else:
|
|
35
|
+
console.print(f" {values}")
|
|
36
|
+
console.print()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.command("init")
|
|
40
|
+
def init_config_cmd(
|
|
41
|
+
force: Annotated[bool, typer.Option("--force", "-f", help="Overwrite existing config")] = False,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Initialize configuration file with defaults."""
|
|
44
|
+
config_path = get_config_path()
|
|
45
|
+
|
|
46
|
+
if config_path.exists() and not force:
|
|
47
|
+
console.print(f"[yellow]Config already exists at {config_path}[/yellow]")
|
|
48
|
+
console.print("[dim]Use --force to overwrite.[/dim]")
|
|
49
|
+
raise typer.Exit(1)
|
|
50
|
+
|
|
51
|
+
init_config()
|
|
52
|
+
console.print(f"[green]Created config at {config_path}[/green]")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("set")
|
|
56
|
+
def set_config(
|
|
57
|
+
key: Annotated[str, typer.Argument(help="Config key (e.g., general.database_path)")],
|
|
58
|
+
value: Annotated[str, typer.Argument(help="Value to set")],
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Set a configuration value."""
|
|
61
|
+
config = Config.load()
|
|
62
|
+
|
|
63
|
+
parts = key.split(".")
|
|
64
|
+
if len(parts) != 2:
|
|
65
|
+
console.print("[red]Key must be in format 'section.key'[/red]")
|
|
66
|
+
raise typer.Exit(1)
|
|
67
|
+
|
|
68
|
+
section, setting = parts
|
|
69
|
+
|
|
70
|
+
# Get current config as dict
|
|
71
|
+
data = config.model_dump()
|
|
72
|
+
|
|
73
|
+
if section not in data:
|
|
74
|
+
data[section] = {}
|
|
75
|
+
|
|
76
|
+
data[section][setting] = value
|
|
77
|
+
|
|
78
|
+
# Save updated config
|
|
79
|
+
try:
|
|
80
|
+
updated_config = Config(**data)
|
|
81
|
+
updated_config.save()
|
|
82
|
+
console.print(f"[green]Set {key} = {value}[/green]")
|
|
83
|
+
except Exception as e:
|
|
84
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
85
|
+
raise typer.Exit(1) from None
|