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.
- taken_cli-0.1.0rc1/PKG-INFO +123 -0
- taken_cli-0.1.0rc1/README.md +108 -0
- taken_cli-0.1.0rc1/pyproject.toml +119 -0
- taken_cli-0.1.0rc1/src/taken/__init__.py +0 -0
- taken_cli-0.1.0rc1/src/taken/commands/__init__.py +0 -0
- taken_cli-0.1.0rc1/src/taken/commands/add.py +211 -0
- taken_cli-0.1.0rc1/src/taken/commands/init.py +167 -0
- taken_cli-0.1.0rc1/src/taken/commands/install.py +224 -0
- taken_cli-0.1.0rc1/src/taken/commands/list.py +57 -0
- taken_cli-0.1.0rc1/src/taken/commands/remove.py +124 -0
- taken_cli-0.1.0rc1/src/taken/commands/save.py +164 -0
- taken_cli-0.1.0rc1/src/taken/commands/update.py +286 -0
- taken_cli-0.1.0rc1/src/taken/commands/use.py +153 -0
- taken_cli-0.1.0rc1/src/taken/core/__init__.py +0 -0
- taken_cli-0.1.0rc1/src/taken/core/config.py +64 -0
- taken_cli-0.1.0rc1/src/taken/core/editor.py +14 -0
- taken_cli-0.1.0rc1/src/taken/core/github.py +158 -0
- taken_cli-0.1.0rc1/src/taken/core/hashing.py +21 -0
- taken_cli-0.1.0rc1/src/taken/core/paths.py +4 -0
- taken_cli-0.1.0rc1/src/taken/core/project.py +68 -0
- taken_cli-0.1.0rc1/src/taken/core/registry.py +111 -0
- taken_cli-0.1.0rc1/src/taken/core/skills.py +116 -0
- taken_cli-0.1.0rc1/src/taken/main.py +34 -0
- taken_cli-0.1.0rc1/src/taken/models/__init__.py +0 -0
- taken_cli-0.1.0rc1/src/taken/models/config.py +32 -0
- taken_cli-0.1.0rc1/src/taken/models/project.py +14 -0
- taken_cli-0.1.0rc1/src/taken/models/registry.py +137 -0
- taken_cli-0.1.0rc1/src/taken/utils/__init__.py +0 -0
- 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
|
+
)
|