ai-config-cli 0.1.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.
- ai_config/__init__.py +3 -0
- ai_config/__main__.py +6 -0
- ai_config/adapters/__init__.py +1 -0
- ai_config/adapters/claude.py +353 -0
- ai_config/cli.py +729 -0
- ai_config/cli_render.py +525 -0
- ai_config/cli_theme.py +44 -0
- ai_config/config.py +260 -0
- ai_config/init.py +763 -0
- ai_config/operations.py +357 -0
- ai_config/scaffold.py +87 -0
- ai_config/settings.py +63 -0
- ai_config/types.py +143 -0
- ai_config/validators/__init__.py +149 -0
- ai_config/validators/base.py +48 -0
- ai_config/validators/component/__init__.py +1 -0
- ai_config/validators/component/hook.py +366 -0
- ai_config/validators/component/mcp.py +230 -0
- ai_config/validators/component/skill.py +411 -0
- ai_config/validators/context.py +69 -0
- ai_config/validators/marketplace/__init__.py +1 -0
- ai_config/validators/marketplace/validators.py +433 -0
- ai_config/validators/plugin/__init__.py +1 -0
- ai_config/validators/plugin/validators.py +336 -0
- ai_config/validators/target/__init__.py +1 -0
- ai_config/validators/target/claude.py +154 -0
- ai_config/watch.py +279 -0
- ai_config_cli-0.1.0.dist-info/METADATA +235 -0
- ai_config_cli-0.1.0.dist-info/RECORD +32 -0
- ai_config_cli-0.1.0.dist-info/WHEEL +4 -0
- ai_config_cli-0.1.0.dist-info/entry_points.txt +2 -0
- ai_config_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
ai_config/watch.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""File watching for auto-sync on changes."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from threading import Event, Timer
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
|
10
|
+
from watchdog.observers import Observer
|
|
11
|
+
|
|
12
|
+
from ai_config.types import AIConfig, PluginSource
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FileChange:
|
|
17
|
+
"""Represents a detected file change."""
|
|
18
|
+
|
|
19
|
+
path: Path
|
|
20
|
+
change_type: Literal["config", "plugin_directory"]
|
|
21
|
+
event_type: str # "created", "modified", "deleted"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class WatchConfig:
|
|
26
|
+
"""Configuration for file watching."""
|
|
27
|
+
|
|
28
|
+
config_path: Path
|
|
29
|
+
plugin_directories: list[Path]
|
|
30
|
+
debounce_seconds: float = 1.5
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Patterns to ignore when watching files
|
|
34
|
+
IGNORE_PATTERNS = frozenset(
|
|
35
|
+
{
|
|
36
|
+
".swp", # Vim swap files
|
|
37
|
+
".swo", # Vim swap overflow
|
|
38
|
+
".swn", # Vim swap
|
|
39
|
+
"~", # Backup files
|
|
40
|
+
".tmp", # Temp files
|
|
41
|
+
".bak", # Backup files
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
IGNORE_DIRS = frozenset(
|
|
46
|
+
{
|
|
47
|
+
".git",
|
|
48
|
+
"__pycache__",
|
|
49
|
+
".pytest_cache",
|
|
50
|
+
"node_modules",
|
|
51
|
+
".venv",
|
|
52
|
+
"venv",
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def collect_watch_paths(config: AIConfig, config_path: Path) -> WatchConfig:
|
|
58
|
+
"""Extract paths to watch from config.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
config: The loaded AIConfig.
|
|
62
|
+
config_path: Path to the config file itself.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
WatchConfig with paths to monitor.
|
|
66
|
+
"""
|
|
67
|
+
plugin_directories: list[Path] = []
|
|
68
|
+
|
|
69
|
+
for target in config.targets:
|
|
70
|
+
if target.type == "claude":
|
|
71
|
+
for marketplace in target.config.marketplaces.values():
|
|
72
|
+
if marketplace.source == PluginSource.LOCAL:
|
|
73
|
+
plugin_directories.append(Path(marketplace.path))
|
|
74
|
+
|
|
75
|
+
return WatchConfig(
|
|
76
|
+
config_path=config_path,
|
|
77
|
+
plugin_directories=plugin_directories,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _should_ignore_path(path: Path) -> bool:
|
|
82
|
+
"""Check if a path should be ignored.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
path: Path to check.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
True if the path should be ignored.
|
|
89
|
+
"""
|
|
90
|
+
# Check file suffixes/patterns
|
|
91
|
+
name = path.name
|
|
92
|
+
for pattern in IGNORE_PATTERNS:
|
|
93
|
+
if name.endswith(pattern):
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
# Check for hidden files starting with .
|
|
97
|
+
if name.startswith(".") and not name.startswith(".."):
|
|
98
|
+
# Allow normal dotfiles but check for swap pattern
|
|
99
|
+
if name.endswith(".swp") or name.endswith(".swo"):
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
# Check if any parent is an ignored directory
|
|
103
|
+
for part in path.parts:
|
|
104
|
+
if part in IGNORE_DIRS:
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ChangeCollector(FileSystemEventHandler):
|
|
111
|
+
"""Collects file changes and debounces callback invocation."""
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self,
|
|
115
|
+
config_path: Path,
|
|
116
|
+
plugin_directories: list[Path],
|
|
117
|
+
debounce_seconds: float,
|
|
118
|
+
on_changes: Callable[[list[FileChange]], None],
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Initialize the collector.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
config_path: Path to the config file.
|
|
124
|
+
plugin_directories: List of plugin directories to monitor.
|
|
125
|
+
debounce_seconds: Seconds to wait before firing callback.
|
|
126
|
+
on_changes: Callback to invoke with collected changes.
|
|
127
|
+
"""
|
|
128
|
+
super().__init__()
|
|
129
|
+
self._config_path = config_path.resolve()
|
|
130
|
+
self._plugin_directories = [d.resolve() for d in plugin_directories]
|
|
131
|
+
self._debounce_seconds = debounce_seconds
|
|
132
|
+
self._on_changes = on_changes
|
|
133
|
+
self._pending_changes: dict[Path, FileChange] = {}
|
|
134
|
+
self._debounce_timer: Timer | None = None
|
|
135
|
+
|
|
136
|
+
def _classify_change(self, path: Path) -> Literal["config", "plugin_directory"] | None:
|
|
137
|
+
"""Classify a file change by type.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
path: Path of the changed file.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Change type or None if not a watched path.
|
|
144
|
+
"""
|
|
145
|
+
resolved = path.resolve()
|
|
146
|
+
|
|
147
|
+
if resolved == self._config_path:
|
|
148
|
+
return "config"
|
|
149
|
+
|
|
150
|
+
for plugin_dir in self._plugin_directories:
|
|
151
|
+
try:
|
|
152
|
+
resolved.relative_to(plugin_dir)
|
|
153
|
+
return "plugin_directory"
|
|
154
|
+
except ValueError:
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
def _handle_event(self, event: FileSystemEvent) -> None:
|
|
160
|
+
"""Handle a file system event.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
event: The watchdog event.
|
|
164
|
+
"""
|
|
165
|
+
if event.is_directory:
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
src_path = event.src_path
|
|
169
|
+
if isinstance(src_path, bytes):
|
|
170
|
+
src_path = src_path.decode()
|
|
171
|
+
path = Path(src_path)
|
|
172
|
+
|
|
173
|
+
if _should_ignore_path(path):
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
change_type = self._classify_change(path)
|
|
177
|
+
if change_type is None:
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
# Add to pending changes (deduplicate by path)
|
|
181
|
+
self._pending_changes[path] = FileChange(
|
|
182
|
+
path=path,
|
|
183
|
+
change_type=change_type,
|
|
184
|
+
event_type=event.event_type,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Reset debounce timer
|
|
188
|
+
if self._debounce_timer is not None:
|
|
189
|
+
self._debounce_timer.cancel()
|
|
190
|
+
|
|
191
|
+
self._debounce_timer = Timer(self._debounce_seconds, self._fire_callback)
|
|
192
|
+
self._debounce_timer.daemon = True
|
|
193
|
+
self._debounce_timer.start()
|
|
194
|
+
|
|
195
|
+
def _fire_callback(self) -> None:
|
|
196
|
+
"""Fire the callback with collected changes."""
|
|
197
|
+
if not self._pending_changes:
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
changes = list(self._pending_changes.values())
|
|
201
|
+
self._pending_changes.clear()
|
|
202
|
+
self._debounce_timer = None
|
|
203
|
+
|
|
204
|
+
self._on_changes(changes)
|
|
205
|
+
|
|
206
|
+
def on_created(self, event: FileSystemEvent) -> None:
|
|
207
|
+
"""Handle file created event."""
|
|
208
|
+
self._handle_event(event)
|
|
209
|
+
|
|
210
|
+
def on_modified(self, event: FileSystemEvent) -> None:
|
|
211
|
+
"""Handle file modified event."""
|
|
212
|
+
self._handle_event(event)
|
|
213
|
+
|
|
214
|
+
def on_deleted(self, event: FileSystemEvent) -> None:
|
|
215
|
+
"""Handle file deleted event."""
|
|
216
|
+
self._handle_event(event)
|
|
217
|
+
|
|
218
|
+
def on_moved(self, event: FileSystemEvent) -> None:
|
|
219
|
+
"""Handle file moved event."""
|
|
220
|
+
self._handle_event(event)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass
|
|
224
|
+
class WatchResult:
|
|
225
|
+
"""Result of a watch operation."""
|
|
226
|
+
|
|
227
|
+
config_changes: int = 0
|
|
228
|
+
plugin_changes: int = 0
|
|
229
|
+
errors: list[str] = field(default_factory=list)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def run_watch_loop(
|
|
233
|
+
watch_config: WatchConfig,
|
|
234
|
+
on_changes: Callable[[list[FileChange]], None],
|
|
235
|
+
stop_event: Event,
|
|
236
|
+
debounce_seconds: float = 1.5,
|
|
237
|
+
) -> None:
|
|
238
|
+
"""Run the watch loop until stop_event is set.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
watch_config: Configuration for what to watch.
|
|
242
|
+
on_changes: Callback to invoke when changes are detected.
|
|
243
|
+
stop_event: Event to signal the loop should stop.
|
|
244
|
+
debounce_seconds: Seconds to wait before syncing after changes.
|
|
245
|
+
"""
|
|
246
|
+
collector = ChangeCollector(
|
|
247
|
+
config_path=watch_config.config_path,
|
|
248
|
+
plugin_directories=watch_config.plugin_directories,
|
|
249
|
+
debounce_seconds=debounce_seconds,
|
|
250
|
+
on_changes=on_changes,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
observer = Observer()
|
|
254
|
+
|
|
255
|
+
# Watch config file's directory
|
|
256
|
+
if watch_config.config_path.parent.exists():
|
|
257
|
+
observer.schedule(
|
|
258
|
+
collector,
|
|
259
|
+
str(watch_config.config_path.parent),
|
|
260
|
+
recursive=False,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Watch plugin directories recursively
|
|
264
|
+
for plugin_dir in watch_config.plugin_directories:
|
|
265
|
+
if plugin_dir.exists():
|
|
266
|
+
observer.schedule(
|
|
267
|
+
collector,
|
|
268
|
+
str(plugin_dir),
|
|
269
|
+
recursive=True,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
observer.start()
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
while not stop_event.is_set():
|
|
276
|
+
stop_event.wait(timeout=0.5)
|
|
277
|
+
finally:
|
|
278
|
+
observer.stop()
|
|
279
|
+
observer.join()
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-config-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Declarative plugin manager for Claude Code
|
|
5
|
+
Project-URL: Homepage, https://github.com/safurrier/ai-config
|
|
6
|
+
Project-URL: Documentation, https://safurrier.github.io/ai-config/
|
|
7
|
+
Project-URL: Repository, https://github.com/safurrier/ai-config
|
|
8
|
+
Project-URL: Issues, https://github.com/safurrier/ai-config/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/safurrier/ai-config/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Alex Furrier <afurrier@gmail.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: ai,claude,claude-code,configuration,plugin
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Requires-Dist: click>=8.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: questionary>=2.0
|
|
31
|
+
Requires-Dist: requests>=2.28
|
|
32
|
+
Requires-Dist: rich>=13.0
|
|
33
|
+
Requires-Dist: watchdog>=3.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8.1.1; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ty>=0.0.2; extra == 'dev'
|
|
41
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: types-requests>=2.28; extra == 'dev'
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: mkdocs-material>=9.6.14; extra == 'docs'
|
|
45
|
+
Requires-Dist: mkdocstrings[python]>=0.26.1; extra == 'docs'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# ai-config
|
|
49
|
+
|
|
50
|
+
Declarative plugin manager for Claude Code.
|
|
51
|
+
|
|
52
|
+
(Future: Codex CLI and OpenCode support planned once plugins become more standardized for sharing ai-context)
|
|
53
|
+
|
|
54
|
+
## Why this exists
|
|
55
|
+
|
|
56
|
+
Claude Code plugins are useful. They let you extend Claude with custom skills, hooks, and MCP servers. The problem is managing them.
|
|
57
|
+
|
|
58
|
+
Without ai-config, you're running `claude plugin install` and `claude plugin marketplace add` commands by hand across machines. There's no config file. No way to version control your setup. No way to share it.
|
|
59
|
+
|
|
60
|
+
ai-config fixes that. You write a YAML file describing what plugins you want, and it handles the rest.
|
|
61
|
+
|
|
62
|
+
Or more simply, run `ai-config init` and it writes the config for you.
|
|
63
|
+
|
|
64
|
+
## What this isn't
|
|
65
|
+
|
|
66
|
+
This README does not have:
|
|
67
|
+
|
|
68
|
+
- 14 shields.io badges declaring build status, coverage, npm downloads, discord members, twitter followers, and mass-to-charge ratio
|
|
69
|
+
- A mass of emojis to make it look "friendly" and "approachable"
|
|
70
|
+
- Claims about revolutionizing your development workflow
|
|
71
|
+
- Integration with 47 different tools (we integrate with one)
|
|
72
|
+
- A "Quick Start" that's actually 73 steps
|
|
73
|
+
- Screenshots of a dashboard that doesn't exist
|
|
74
|
+
- A "Powered by AI" badge despite just being a for-loop
|
|
75
|
+
|
|
76
|
+
It's a config file and some commands. That's it.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
> **Alpha software**: This project is in active development. APIs and config formats may change between versions.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install ai-config-cli
|
|
84
|
+
# or
|
|
85
|
+
uv tool install ai-config-cli
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This installs `ai-config` globally. Run `ai-config --help` to verify.
|
|
89
|
+
|
|
90
|
+
### From source (latest)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv tool install git+https://github.com/safurrier/ai-config
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### For development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/safurrier/ai-config.git
|
|
100
|
+
cd ai-config
|
|
101
|
+
just setup # Install dependencies
|
|
102
|
+
just check # Run lint, type check, tests
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Quick Start
|
|
106
|
+
|
|
107
|
+
**1. Create your config**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ai-config init
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Interactive wizard walks you through adding marketplaces and plugins. Creates `.ai-config/config.yaml`.
|
|
114
|
+
|
|
115
|
+
**2. Sync to install plugins**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
ai-config sync
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Installs/uninstalls plugins to match your config. Run this after editing `config.yaml`.
|
|
122
|
+
|
|
123
|
+
If plugins seem stale or out of date:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ai-config sync --fresh
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**3. Iterate with watch (plugin development)**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
ai-config watch
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Auto-syncs when you edit config or plugin files. Press Ctrl+C to stop.
|
|
136
|
+
|
|
137
|
+
**Note:** Claude Code loads plugins at session start. After changes sync, restart Claude Code to apply them. Use `claude --resume` to continue your previous session.
|
|
138
|
+
|
|
139
|
+
**4. Troubleshoot with doctor**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
ai-config doctor
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Validates marketplaces, plugins, skills, hooks, and MCP servers. Shows fix hints for any issues.
|
|
146
|
+
|
|
147
|
+
## What it does
|
|
148
|
+
|
|
149
|
+
**Declarative config** - Define your plugins in `.ai-config/config.yaml`:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
version: 1
|
|
153
|
+
targets:
|
|
154
|
+
- type: claude
|
|
155
|
+
config:
|
|
156
|
+
marketplaces:
|
|
157
|
+
claude-code-tutorial:
|
|
158
|
+
source: github
|
|
159
|
+
repo: safurrier/claude-code-tutorial
|
|
160
|
+
plugins:
|
|
161
|
+
- id: claude-code-tutorial@claude-code-tutorial
|
|
162
|
+
scope: user
|
|
163
|
+
enabled: true
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Interactive setup** - Don't want to write YAML? Run the wizard:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
ai-config init
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
It walks you through adding marketplaces and plugins with arrow-key navigation.
|
|
173
|
+
|
|
174
|
+
**Sync** - Make reality match your config:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
ai-config sync
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Validation** - Find problems before they bite you:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
ai-config doctor
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Checks that marketplaces exist, plugins are installed, skills are valid, hooks work.
|
|
187
|
+
|
|
188
|
+
## Commands
|
|
189
|
+
|
|
190
|
+
| Command | What it does |
|
|
191
|
+
|---------|--------------|
|
|
192
|
+
| `init` | Interactive config generator |
|
|
193
|
+
| `sync` | Install/uninstall plugins to match config |
|
|
194
|
+
| `status` | Show what's currently installed |
|
|
195
|
+
| `watch` | Auto-sync on file changes (plugin development) |
|
|
196
|
+
| `update` | Update plugins to latest versions |
|
|
197
|
+
| `doctor` | Validate setup and show fix hints |
|
|
198
|
+
| `plugin create` | Scaffold a new plugin |
|
|
199
|
+
| `cache clear` | Clear the plugin cache |
|
|
200
|
+
|
|
201
|
+
## Config file locations
|
|
202
|
+
|
|
203
|
+
ai-config looks for config in this order:
|
|
204
|
+
|
|
205
|
+
1. `.ai-config/config.yaml` (project-local)
|
|
206
|
+
2. `~/.ai-config/config.yaml` (global)
|
|
207
|
+
|
|
208
|
+
You can also pass `-c /path/to/config.yaml` to any command.
|
|
209
|
+
|
|
210
|
+
## Scopes
|
|
211
|
+
|
|
212
|
+
Plugins can be installed in different scopes:
|
|
213
|
+
|
|
214
|
+
- **user** - Available everywhere (`~/.claude/plugins/`)
|
|
215
|
+
- **project** - Only in the current project (`.claude/plugins/`)
|
|
216
|
+
|
|
217
|
+
## Troubleshooting
|
|
218
|
+
|
|
219
|
+
**Plugin installed but not showing up in / commands**
|
|
220
|
+
|
|
221
|
+
Clear cache and re-sync:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
ai-config sync --fresh
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Something's broken and Claude Code won't help**
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
ai-config doctor --verbose
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
ai_config/__init__.py,sha256=gjK4b53Q3FicdMkCfFxlMfb1UGppWUPA2eQlgPU4upU,88
|
|
2
|
+
ai_config/__main__.py,sha256=GmxRKMTWWibho0WpkCjFMil8jZBSToDFf-Otenax0Xs,114
|
|
3
|
+
ai_config/cli.py,sha256=aMt_h9B5GJSB08qvO_SkzC3V3-GzI1Y346-if3Z0cuE,23865
|
|
4
|
+
ai_config/cli_render.py,sha256=gJ3Lnb-4_ONXNIz2sRjVDhdDGIi833WGC_WnTFX0xKQ,18658
|
|
5
|
+
ai_config/cli_theme.py,sha256=AU3EdLAvyWtxwFtQYNhowRCBJerlZfU89cFgwDRq5wo,1094
|
|
6
|
+
ai_config/config.py,sha256=mqol3YKXiipkBqdJV1cBBvlhMFu4PRjOsbw5ypiChZs,8686
|
|
7
|
+
ai_config/init.py,sha256=uJl7Egh13iJlG6_ciuYIRbA3GwOa339bUp5NZO1BNjY,22517
|
|
8
|
+
ai_config/operations.py,sha256=om967tGLhywcz_RFoDc0nXjzRp-O8NMmVffd32YWJj8,10907
|
|
9
|
+
ai_config/scaffold.py,sha256=rP-U2rwEe67WXp26-9LzoalRP_S8MjntrXO5D7xONSw,1836
|
|
10
|
+
ai_config/settings.py,sha256=uwqVpz_YkTqaFI8Qtq7UIWx7KF1l2Nd86CFD8P0B0As,1642
|
|
11
|
+
ai_config/types.py,sha256=Rn9QNdWvymH28jLi_mLn_e_rnVKWoMxwZ7tgNWFKzdM,4022
|
|
12
|
+
ai_config/watch.py,sha256=fYCZTc__jzEpj38633wF62wT7J5DFmYrid_n1h_7NlI,7711
|
|
13
|
+
ai_config/adapters/__init__.py,sha256=qHNJ4e7OVzkreIS5Cu7KtehPVGiWmrWMPUqZ97NiKIM,46
|
|
14
|
+
ai_config/adapters/claude.py,sha256=baWdzMFVj94mO10jUCm6AojcKxT5svqYtNh00ADPcYU,9496
|
|
15
|
+
ai_config/validators/__init__.py,sha256=nCk8_PKQ1_RBd_CaWLB5BHEug_JOb3OlLJ3dyldfqEQ,4404
|
|
16
|
+
ai_config/validators/base.py,sha256=AfOxBJOjVSrw8uHsUZTlz4ZdTGmtFvkxgOm77HXo8rI,1225
|
|
17
|
+
ai_config/validators/context.py,sha256=q1IxgX501e3GoiLhJAxJYQQlOFlxlMKlSYmfNKJeKM8,2642
|
|
18
|
+
ai_config/validators/component/__init__.py,sha256=nPod-3X9oLl1mUfz_kyfmUzIxogZTG2BWGpLhClmrFw,64
|
|
19
|
+
ai_config/validators/component/hook.py,sha256=h7bCoQYD03WAsspNCIoX-iz5Mb7rxexjmc0kxnL0UT4,13188
|
|
20
|
+
ai_config/validators/component/mcp.py,sha256=Oq9X-K9EDO4bTUGE70lEW4EIn30mUsq65Fi4L4Vhvaw,8613
|
|
21
|
+
ai_config/validators/component/skill.py,sha256=o5uAaWVljpGEw0Xx85d7tovt7gYrRZ9AwK_w96PO-EM,12647
|
|
22
|
+
ai_config/validators/marketplace/__init__.py,sha256=eg1JthnlrnEQeG8Daghyn4zZOKLea3y5xYuqfUxHv7M,44
|
|
23
|
+
ai_config/validators/marketplace/validators.py,sha256=uOmO8yl4q10QvIt4kNhUyb4V3qWhhhqbI-WfxSkbDC4,16353
|
|
24
|
+
ai_config/validators/plugin/__init__.py,sha256=xT8IoczuJrhorek6naYwZNESDaQkeOJ_nijozAOz4Wo,39
|
|
25
|
+
ai_config/validators/plugin/validators.py,sha256=dP4T-JhuC41CDcllvgRwlVTkwHdKf106aR-bpk5pzFs,12170
|
|
26
|
+
ai_config/validators/target/__init__.py,sha256=A_57pCwACG1oFd80UlGUN1iWRHlYqzceBz5aCYYtTKA,39
|
|
27
|
+
ai_config/validators/target/claude.py,sha256=rYs9gqQRcqtnN-jv4EA5HXIrpIP-DEq6lVqAJGk9b78,5014
|
|
28
|
+
ai_config_cli-0.1.0.dist-info/METADATA,sha256=YkMC1WvFkCl1YRPkVF6vKbdkHH_TLAZkZi6ji805Ql8,6603
|
|
29
|
+
ai_config_cli-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
30
|
+
ai_config_cli-0.1.0.dist-info/entry_points.txt,sha256=Bw4iKZy9_HtrJplb26AWXbVOQYugpMre11wg31GhnB0,49
|
|
31
|
+
ai_config_cli-0.1.0.dist-info/licenses/LICENSE,sha256=YubrN8gJ2atD-zJmLwr1OJa2pejx1ZwArylzHBEYAb8,1069
|
|
32
|
+
ai_config_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Alex Furrier
|
|
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.
|