taken-cli 0.1.0rc1__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.
Files changed (29) hide show
  1. taken_cli-0.1.0rc1/PKG-INFO +123 -0
  2. taken_cli-0.1.0rc1/README.md +108 -0
  3. taken_cli-0.1.0rc1/pyproject.toml +119 -0
  4. taken_cli-0.1.0rc1/src/taken/__init__.py +0 -0
  5. taken_cli-0.1.0rc1/src/taken/commands/__init__.py +0 -0
  6. taken_cli-0.1.0rc1/src/taken/commands/add.py +211 -0
  7. taken_cli-0.1.0rc1/src/taken/commands/init.py +167 -0
  8. taken_cli-0.1.0rc1/src/taken/commands/install.py +224 -0
  9. taken_cli-0.1.0rc1/src/taken/commands/list.py +57 -0
  10. taken_cli-0.1.0rc1/src/taken/commands/remove.py +124 -0
  11. taken_cli-0.1.0rc1/src/taken/commands/save.py +164 -0
  12. taken_cli-0.1.0rc1/src/taken/commands/update.py +286 -0
  13. taken_cli-0.1.0rc1/src/taken/commands/use.py +153 -0
  14. taken_cli-0.1.0rc1/src/taken/core/__init__.py +0 -0
  15. taken_cli-0.1.0rc1/src/taken/core/config.py +64 -0
  16. taken_cli-0.1.0rc1/src/taken/core/editor.py +14 -0
  17. taken_cli-0.1.0rc1/src/taken/core/github.py +158 -0
  18. taken_cli-0.1.0rc1/src/taken/core/hashing.py +21 -0
  19. taken_cli-0.1.0rc1/src/taken/core/paths.py +4 -0
  20. taken_cli-0.1.0rc1/src/taken/core/project.py +68 -0
  21. taken_cli-0.1.0rc1/src/taken/core/registry.py +111 -0
  22. taken_cli-0.1.0rc1/src/taken/core/skills.py +116 -0
  23. taken_cli-0.1.0rc1/src/taken/main.py +34 -0
  24. taken_cli-0.1.0rc1/src/taken/models/__init__.py +0 -0
  25. taken_cli-0.1.0rc1/src/taken/models/config.py +32 -0
  26. taken_cli-0.1.0rc1/src/taken/models/project.py +14 -0
  27. taken_cli-0.1.0rc1/src/taken/models/registry.py +137 -0
  28. taken_cli-0.1.0rc1/src/taken/utils/__init__.py +0 -0
  29. taken_cli-0.1.0rc1/src/taken/utils/console.py +8 -0
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.3
2
+ Name: taken-cli
3
+ Version: 0.1.0rc1
4
+ Summary: Personal dotfile-style CLI for managing AI agent skills
5
+ Author: Pradyoth
6
+ Author-email: Pradyoth <pradyoth.dev@gmail.com>
7
+ License: MIT
8
+ Requires-Dist: inquirerpy>=0.3.4
9
+ Requires-Dist: pydantic>=2.13.3
10
+ Requires-Dist: rich>=15.0.0
11
+ Requires-Dist: ruamel-yaml>=0.19.1
12
+ Requires-Dist: typer>=0.24.2
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+
16
+ # taken
17
+
18
+ > *"A very particular set of skills, acquired over a very long career."*
19
+
20
+ **taken** is a personal dotfile-style CLI for managing AI agent skills (`SKILL.md` files). Think [chezmoi](https://www.chezmoi.io/), but for agent skills — your skills live in `~/.taken/`, are git-backed, and can be linked into any project on demand.
21
+
22
+ Fully compatible with the [skills.sh](https://skills.sh) ecosystem (Claude Code, Cursor, OpenAI Codex, and 40+ agents).
23
+
24
+ ---
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ uv tool install taken
30
+ ```
31
+
32
+ ```bash
33
+ pip install taken
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Quick Start
39
+
40
+ **1. Initialize**
41
+ ```bash
42
+ taken init
43
+ ```
44
+ Sets up `~/.taken/`, prompts for your namespace, creates config and registry.
45
+
46
+ **2. Create a personal skill**
47
+ ```bash
48
+ taken add my-skill
49
+ ```
50
+ Scaffolds `~/.taken/skills/<you>/my-skill/SKILL.md` and opens it in your editor.
51
+
52
+ **3. Adopt an existing skill from a project**
53
+ ```bash
54
+ taken add ./agents/my-skill/
55
+ ```
56
+ Copies the skill folder into `~/.taken/` and registers it, detecting provenance from skills.sh lock files automatically.
57
+
58
+ **4. Install a skill from GitHub**
59
+ ```bash
60
+ taken install vercel-labs/agent-skills
61
+ taken install vercel-labs/agent-skills/react-best-practices
62
+ taken install https://github.com/vercel-labs/agent-skills
63
+ ```
64
+ Interactive fuzzy picker when multiple skills are found. Use `--skill` to install specific ones non-interactively, `--ref` to pin to a branch/tag/SHA, `--pin` to lock the version.
65
+
66
+ **5. Use a skill in a project**
67
+ ```bash
68
+ taken use
69
+ ```
70
+ Fuzzy-picks from your registry and copies the skill into `.agents/skills/` in the current project. Records state in `.taken.yaml` (committed to git — no taken dependency needed for teammates).
71
+
72
+ **6. Push edits back to registry**
73
+ ```bash
74
+ taken save
75
+ ```
76
+ If you refined a skill while working in a project, `save` promotes those edits back to `~/.taken/` so they propagate to future projects.
77
+
78
+ **7. Update project skills**
79
+ ```bash
80
+ taken update
81
+ ```
82
+ Re-copies the latest registry version into the project. Warns before overwriting if local edits are detected.
83
+
84
+ ---
85
+
86
+ ## Commands
87
+
88
+ | Command | Description |
89
+ |---|---|
90
+ | `taken init` | First-time setup — creates `~/.taken/`, prompts for namespace |
91
+ | `taken add <skill-name>` | Create a new personal skill, opens in editor |
92
+ | `taken add <path>` | Adopt an existing skill folder into taken management |
93
+ | `taken install <source>` | Install a skill from GitHub |
94
+ | `taken use [namespace/skill]` | Copy a skill from registry into the current project |
95
+ | `taken save [namespace/skill]` | Push project edits back to registry |
96
+ | `taken update [namespace/skill]` | Re-copy latest registry version into project |
97
+ | `taken list` | Show all skills in the registry |
98
+ | `taken remove <namespace/skill>` | Remove a skill from registry |
99
+
100
+ ---
101
+
102
+ ## How It Works
103
+
104
+ Skills live in `~/.taken/skills/<namespace>/<skill-name>/`. The registry at `~/.taken/registry.yaml` is the single source of truth for provenance — tracking whether a skill was created personally, adopted from a project, or installed from GitHub.
105
+
106
+ When you run `taken use`, the skill is copied (never symlinked) into `.agents/skills/` and tracked in `.taken.yaml` at the project root. This file is committed to git, making the project self-contained — teammates don't need taken installed to use the skills.
107
+
108
+ ---
109
+
110
+ ## Storage Layout
111
+
112
+ ```
113
+ ~/.taken/
114
+ config.yaml # username, preferences
115
+ registry.yaml # all skills + metadata
116
+ skills/
117
+ you/
118
+ my-skill/
119
+ SKILL.md
120
+ vercel-labs/
121
+ react-best-practices/
122
+ SKILL.md
123
+ ```
@@ -0,0 +1,108 @@
1
+ # taken
2
+
3
+ > *"A very particular set of skills, acquired over a very long career."*
4
+
5
+ **taken** is a personal dotfile-style CLI for managing AI agent skills (`SKILL.md` files). Think [chezmoi](https://www.chezmoi.io/), but for agent skills — your skills live in `~/.taken/`, are git-backed, and can be linked into any project on demand.
6
+
7
+ Fully compatible with the [skills.sh](https://skills.sh) ecosystem (Claude Code, Cursor, OpenAI Codex, and 40+ agents).
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ uv tool install taken
15
+ ```
16
+
17
+ ```bash
18
+ pip install taken
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick Start
24
+
25
+ **1. Initialize**
26
+ ```bash
27
+ taken init
28
+ ```
29
+ Sets up `~/.taken/`, prompts for your namespace, creates config and registry.
30
+
31
+ **2. Create a personal skill**
32
+ ```bash
33
+ taken add my-skill
34
+ ```
35
+ Scaffolds `~/.taken/skills/<you>/my-skill/SKILL.md` and opens it in your editor.
36
+
37
+ **3. Adopt an existing skill from a project**
38
+ ```bash
39
+ taken add ./agents/my-skill/
40
+ ```
41
+ Copies the skill folder into `~/.taken/` and registers it, detecting provenance from skills.sh lock files automatically.
42
+
43
+ **4. Install a skill from GitHub**
44
+ ```bash
45
+ taken install vercel-labs/agent-skills
46
+ taken install vercel-labs/agent-skills/react-best-practices
47
+ taken install https://github.com/vercel-labs/agent-skills
48
+ ```
49
+ Interactive fuzzy picker when multiple skills are found. Use `--skill` to install specific ones non-interactively, `--ref` to pin to a branch/tag/SHA, `--pin` to lock the version.
50
+
51
+ **5. Use a skill in a project**
52
+ ```bash
53
+ taken use
54
+ ```
55
+ Fuzzy-picks from your registry and copies the skill into `.agents/skills/` in the current project. Records state in `.taken.yaml` (committed to git — no taken dependency needed for teammates).
56
+
57
+ **6. Push edits back to registry**
58
+ ```bash
59
+ taken save
60
+ ```
61
+ If you refined a skill while working in a project, `save` promotes those edits back to `~/.taken/` so they propagate to future projects.
62
+
63
+ **7. Update project skills**
64
+ ```bash
65
+ taken update
66
+ ```
67
+ Re-copies the latest registry version into the project. Warns before overwriting if local edits are detected.
68
+
69
+ ---
70
+
71
+ ## Commands
72
+
73
+ | Command | Description |
74
+ |---|---|
75
+ | `taken init` | First-time setup — creates `~/.taken/`, prompts for namespace |
76
+ | `taken add <skill-name>` | Create a new personal skill, opens in editor |
77
+ | `taken add <path>` | Adopt an existing skill folder into taken management |
78
+ | `taken install <source>` | Install a skill from GitHub |
79
+ | `taken use [namespace/skill]` | Copy a skill from registry into the current project |
80
+ | `taken save [namespace/skill]` | Push project edits back to registry |
81
+ | `taken update [namespace/skill]` | Re-copy latest registry version into project |
82
+ | `taken list` | Show all skills in the registry |
83
+ | `taken remove <namespace/skill>` | Remove a skill from registry |
84
+
85
+ ---
86
+
87
+ ## How It Works
88
+
89
+ Skills live in `~/.taken/skills/<namespace>/<skill-name>/`. The registry at `~/.taken/registry.yaml` is the single source of truth for provenance — tracking whether a skill was created personally, adopted from a project, or installed from GitHub.
90
+
91
+ When you run `taken use`, the skill is copied (never symlinked) into `.agents/skills/` and tracked in `.taken.yaml` at the project root. This file is committed to git, making the project self-contained — teammates don't need taken installed to use the skills.
92
+
93
+ ---
94
+
95
+ ## Storage Layout
96
+
97
+ ```
98
+ ~/.taken/
99
+ config.yaml # username, preferences
100
+ registry.yaml # all skills + metadata
101
+ skills/
102
+ you/
103
+ my-skill/
104
+ SKILL.md
105
+ vercel-labs/
106
+ react-best-practices/
107
+ SKILL.md
108
+ ```
@@ -0,0 +1,119 @@
1
+ [project]
2
+ name = "taken-cli"
3
+ version = "0.1.0rc1"
4
+ description = "Personal dotfile-style CLI for managing AI agent skills"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [
8
+ { name = "Pradyoth", email = "pradyoth.dev@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.11"
11
+ dependencies = [
12
+ "inquirerpy>=0.3.4",
13
+ "pydantic>=2.13.3",
14
+ "rich>=15.0.0",
15
+ "ruamel-yaml>=0.19.1",
16
+ "typer>=0.24.2",
17
+ ]
18
+
19
+ [project.scripts]
20
+ taken = "taken.main:main"
21
+
22
+ [build-system]
23
+ requires = ["uv_build>=0.10.9,<0.11.0"]
24
+ build-backend = "uv_build"
25
+
26
+ [tool.uv.build-backend]
27
+ module-name = "taken"
28
+
29
+ [dependency-groups]
30
+ dev = [
31
+ "pre-commit>=4.6.0",
32
+ "pyright>=1.1.409",
33
+ "ruff>=0.15.12",
34
+ ]
35
+ test = [
36
+ "anyio>=4.0",
37
+ "polyfactory>=3.3.0",
38
+ "pytest>=9.0.3",
39
+ ]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
43
+ anyio_mode = "asyncio"
44
+
45
+ [tool.pyright]
46
+ include = ["src", "tests"]
47
+ venvPath = "."
48
+ venv = ".venv"
49
+ pythonVersion = "3.14"
50
+ typeCheckingMode = "strict"
51
+ reportMissingImports = true
52
+ useLibraryCodeForTypes = true
53
+ reportInvalidTypeForm = true
54
+
55
+ [tool.ruff]
56
+ line-length = 120
57
+ target-version = "py311"
58
+
59
+ # Allow autofix behavior
60
+ fix = true
61
+ unsafe-fixes = false
62
+
63
+ # Exclude files and directories
64
+ exclude = [
65
+ ".bzr",
66
+ ".direnv",
67
+ ".eggs",
68
+ ".git",
69
+ ".git-rewrite",
70
+ ".hg",
71
+ ".mypy_cache",
72
+ ".nox",
73
+ ".pants.d",
74
+ ".pytype",
75
+ ".ruff_cache",
76
+ ".svn",
77
+ ".tox",
78
+ ".venv",
79
+ "__pypackages__",
80
+ "_build",
81
+ "buck-out",
82
+ "build",
83
+ "dist",
84
+ "node_modules",
85
+ "venv",
86
+ "migrations",
87
+ ".agents",
88
+ ]
89
+
90
+ [tool.ruff.lint]
91
+ select = [
92
+ "E", # pycodestyle errors
93
+ "F", # pyflakes
94
+ "UP", # pyupgrade
95
+ "B", # flake8-bugbear
96
+ "SIM", # flake8-simplify
97
+ "I", # isort
98
+ "RUF", # Ruff-specific rules
99
+ "C90", # McCabe complexity
100
+ "ARG", # unused arguments
101
+ ]
102
+
103
+ # Ignore specific rules
104
+ ignore = [
105
+ "E501", # Line too long (handled by formatter)
106
+ ]
107
+
108
+ [tool.flake8]
109
+ ignore = ["E501"]
110
+ max-line-length = 120
111
+
112
+ [tool.ruff.lint.pydocstyle]
113
+ convention = "google" # Use Google-style docstrings
114
+
115
+ [tool.ruff.format]
116
+ quote-style = "double"
117
+ indent-style = "space"
118
+ skip-magic-trailing-comma = false
119
+ line-ending = "auto"
File without changes
File without changes
@@ -0,0 +1,211 @@
1
+ import re
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+
5
+ import typer
6
+ from rich.panel import Panel
7
+
8
+ from taken.core import paths
9
+ from taken.core.config import is_config_exists, read_config
10
+ from taken.core.editor import open_in_editor
11
+ from taken.core.registry import read_registry, write_registry
12
+ from taken.core.skills import (
13
+ adopt_skill,
14
+ is_path_argument,
15
+ lookup_lock_entry,
16
+ scaffold_skill,
17
+ )
18
+ from taken.models.config import TakenConfig
19
+ from taken.models.registry import RegistryEntry, SkillSource
20
+ from taken.utils.console import console, err_console
21
+
22
+ _VALID_NAME = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_-]*$")
23
+
24
+
25
+ def add(
26
+ skill_or_path: str = typer.Argument(..., help="Skill name to create, or path to existing skill folder to adopt"),
27
+ ) -> None:
28
+ """Add a new personal skill or adopt an existing skill into taken management."""
29
+ if not is_config_exists(paths.TAKEN_HOME):
30
+ err_console.print(
31
+ Panel(
32
+ "Taken is not initialized. Run [bold]taken init[/bold] to get started.",
33
+ title="[red]Not Initialized[/red]",
34
+ border_style="red",
35
+ )
36
+ )
37
+ raise typer.Exit(code=1)
38
+
39
+ config = read_config(paths.TAKEN_HOME)
40
+
41
+ if is_path_argument(skill_or_path):
42
+ _adopt_mode(skill_or_path, config)
43
+ else:
44
+ _create_mode(skill_or_path, config)
45
+
46
+
47
+ def _create_mode(name: str, config: TakenConfig) -> None:
48
+ if "/" in name:
49
+ err_console.print(
50
+ Panel(
51
+ f"Skill name must not contain a namespace. Got: [bold]{name}[/bold]\n"
52
+ f"Your namespace [bold]{config.username}[/bold] is added automatically.\n"
53
+ "Example: [bold]taken add my-skill[/bold]",
54
+ title="[red]Invalid Skill Name[/red]",
55
+ border_style="red",
56
+ )
57
+ )
58
+ raise typer.Exit(code=1)
59
+
60
+ if not _VALID_NAME.match(name):
61
+ err_console.print(
62
+ Panel(
63
+ f"[bold]{name}[/bold] is not a valid skill name.\n"
64
+ "Use lowercase letters, numbers, hyphens, and underscores only.\n"
65
+ "Must start with a letter or number.",
66
+ title="[red]Invalid Skill Name[/red]",
67
+ border_style="red",
68
+ )
69
+ )
70
+ raise typer.Exit(code=1)
71
+
72
+ registry = read_registry(paths.TAKEN_HOME)
73
+ full_name = f"{config.username}/{name}"
74
+
75
+ if registry.exists(full_name):
76
+ err_console.print(
77
+ Panel(
78
+ f"Skill [bold]{full_name}[/bold] is already in your registry.\nUse [bold]taken list[/bold] to see it.",
79
+ title="[red]Already Exists[/red]",
80
+ border_style="red",
81
+ )
82
+ )
83
+ raise typer.Exit(code=1)
84
+
85
+ try:
86
+ skill_md = scaffold_skill(config.username, name, paths.TAKEN_HOME)
87
+ except FileExistsError:
88
+ err_console.print(
89
+ Panel(
90
+ f"Skill directory already exists on disk but is not in the registry.\n"
91
+ f"Path: [dim]{paths.TAKEN_HOME}/skills/{config.username}/{name}[/dim]\n"
92
+ "Run [bold]taken doctor[/bold] to repair registry drift.",
93
+ title="[red]Directory Conflict[/red]",
94
+ border_style="red",
95
+ )
96
+ )
97
+ raise typer.Exit(code=1) from None
98
+
99
+ now = datetime.now()
100
+ entry = RegistryEntry(
101
+ namespace=config.username,
102
+ name=name,
103
+ source=SkillSource.PERSONAL,
104
+ version="1",
105
+ created_at=now,
106
+ updated_at=now,
107
+ )
108
+ registry.add(entry)
109
+ write_registry(registry, paths.TAKEN_HOME)
110
+
111
+ console.print(
112
+ Panel(
113
+ f"[green]✓[/green] Created [bold]{full_name}[/bold]\n"
114
+ f"[green]✓[/green] Registered as [bold]personal[/bold] skill\n\n"
115
+ f"[dim]Opening editor…[/dim]",
116
+ title="[green]Skill Created[/green]",
117
+ border_style="green",
118
+ padding=(1, 2),
119
+ )
120
+ )
121
+
122
+ open_in_editor(skill_md)
123
+
124
+
125
+ def _adopt_mode(path_str: str, config: TakenConfig) -> None:
126
+ source_dir = Path(path_str).resolve()
127
+
128
+ if not source_dir.is_dir():
129
+ err_console.print(
130
+ Panel(
131
+ f"[bold]{path_str}[/bold] is not a directory.",
132
+ title="[red]Invalid Path[/red]",
133
+ border_style="red",
134
+ )
135
+ )
136
+ raise typer.Exit(code=1)
137
+
138
+ name = source_dir.name
139
+ lock_entry = lookup_lock_entry(name, Path.cwd())
140
+
141
+ if lock_entry is not None:
142
+ namespace = lock_entry.source.split("/")[0]
143
+ source = SkillSource.NPX
144
+ repo = lock_entry.source
145
+ version = lock_entry.ref
146
+ source_url = lock_entry.source_url
147
+ skill_path = lock_entry.skill_path
148
+ skill_folder_hash = lock_entry.skill_folder_hash
149
+ else:
150
+ namespace = config.username
151
+ source = SkillSource.PERSONAL
152
+ repo = None
153
+ version = None
154
+ source_url = None
155
+ skill_path = None
156
+ skill_folder_hash = None
157
+
158
+ registry = read_registry(paths.TAKEN_HOME)
159
+ full_name = f"{namespace}/{name}"
160
+
161
+ if registry.exists(full_name):
162
+ err_console.print(
163
+ Panel(
164
+ f"Skill [bold]{full_name}[/bold] is already in your registry.\nUse [bold]taken list[/bold] to see it.",
165
+ title="[red]Already Exists[/red]",
166
+ border_style="red",
167
+ )
168
+ )
169
+ raise typer.Exit(code=1)
170
+
171
+ try:
172
+ adopt_skill(source_dir, namespace, name, paths.TAKEN_HOME)
173
+ except FileExistsError:
174
+ err_console.print(
175
+ Panel(
176
+ f"Skill directory already exists at [dim]{paths.TAKEN_HOME}/skills/{namespace}/{name}[/dim]\n"
177
+ "Run [bold]taken doctor[/bold] to repair registry drift.",
178
+ title="[red]Directory Conflict[/red]",
179
+ border_style="red",
180
+ )
181
+ )
182
+ raise typer.Exit(code=1) from None
183
+
184
+ now = datetime.now()
185
+ entry = RegistryEntry(
186
+ namespace=namespace,
187
+ name=name,
188
+ source=source,
189
+ repo=repo,
190
+ version=version,
191
+ installed_at=now if source == SkillSource.NPX else None,
192
+ created_at=now if source == SkillSource.PERSONAL else None,
193
+ updated_at=now,
194
+ source_url=source_url,
195
+ skill_path=skill_path,
196
+ skill_folder_hash=skill_folder_hash,
197
+ )
198
+ registry.add(entry)
199
+ write_registry(registry, paths.TAKEN_HOME)
200
+
201
+ source_detail = f" ({repo})" if repo else ""
202
+ console.print(
203
+ Panel(
204
+ f"[green]✓[/green] Adopted [bold]{name}[/bold] → [bold]{full_name}[/bold]\n"
205
+ f"[green]✓[/green] Source: [bold]{source.value}[/bold]{source_detail}\n\n"
206
+ f"[dim]Copied to ~/.taken/skills/{namespace}/{name}/[/dim]",
207
+ title="[green]Skill Adopted[/green]",
208
+ border_style="green",
209
+ padding=(1, 2),
210
+ )
211
+ )