odcli 0.1.3__tar.gz → 0.1.4__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.
- {odcli-0.1.3 → odcli-0.1.4}/PKG-INFO +20 -1
- {odcli-0.1.3 → odcli-0.1.4}/README.md +19 -0
- {odcli-0.1.3 → odcli-0.1.4}/pyproject.toml +1 -1
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/__init__.py +1 -1
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/__main__.py +1 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/cli.py +28 -0
- odcli-0.1.4/src/obsidian_cli/plugins.py +84 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/odcli.egg-info/PKG-INFO +20 -1
- {odcli-0.1.3 → odcli-0.1.4}/src/odcli.egg-info/SOURCES.txt +1 -0
- {odcli-0.1.3 → odcli-0.1.4}/tests/test_cli.py +28 -0
- {odcli-0.1.3 → odcli-0.1.4}/setup.cfg +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/commands.py +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/discovery.py +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/obsidian_cli/vault.py +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/odcli.egg-info/dependency_links.txt +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/odcli.egg-info/entry_points.txt +0 -0
- {odcli-0.1.3 → odcli-0.1.4}/src/odcli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odcli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A small CLI for reading and writing notes in a local Obsidian vault.
|
|
5
5
|
Author: Chang LeHung
|
|
6
6
|
Project-URL: Homepage, https://github.com/Chang-LeHung/obsidian-cli
|
|
@@ -35,6 +35,7 @@ It works directly on Markdown files inside the vault, so it does not depend on p
|
|
|
35
35
|
- Append content to a note
|
|
36
36
|
- Full-text search across the vault
|
|
37
37
|
- Auto-discover the default vault from Obsidian config or common macOS and Windows locations
|
|
38
|
+
- Install odcli helper skills into Codex or Claude Code skill directories
|
|
38
39
|
|
|
39
40
|
## Using uv
|
|
40
41
|
|
|
@@ -189,6 +190,24 @@ Arguments:
|
|
|
189
190
|
- `query`
|
|
190
191
|
- `--case-sensitive`
|
|
191
192
|
|
|
193
|
+
### `plugin install`
|
|
194
|
+
|
|
195
|
+
Install odcli helper skills for local coding tools.
|
|
196
|
+
|
|
197
|
+
Targets:
|
|
198
|
+
|
|
199
|
+
- `codex-skill`: installs to `~/.codex/skills/odcli/SKILL.md`
|
|
200
|
+
- `claude-skill`: installs to `~/.claude/skills/odcli/SKILL.md`
|
|
201
|
+
- `all-skills`: installs both
|
|
202
|
+
|
|
203
|
+
Examples:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
odcli plugin install codex-skill
|
|
207
|
+
odcli plugin install claude-skill
|
|
208
|
+
odcli plugin install all-skills
|
|
209
|
+
```
|
|
210
|
+
|
|
192
211
|
## Testing
|
|
193
212
|
|
|
194
213
|
```bash
|
|
@@ -14,6 +14,7 @@ It works directly on Markdown files inside the vault, so it does not depend on p
|
|
|
14
14
|
- Append content to a note
|
|
15
15
|
- Full-text search across the vault
|
|
16
16
|
- Auto-discover the default vault from Obsidian config or common macOS and Windows locations
|
|
17
|
+
- Install odcli helper skills into Codex or Claude Code skill directories
|
|
17
18
|
|
|
18
19
|
## Using uv
|
|
19
20
|
|
|
@@ -168,6 +169,24 @@ Arguments:
|
|
|
168
169
|
- `query`
|
|
169
170
|
- `--case-sensitive`
|
|
170
171
|
|
|
172
|
+
### `plugin install`
|
|
173
|
+
|
|
174
|
+
Install odcli helper skills for local coding tools.
|
|
175
|
+
|
|
176
|
+
Targets:
|
|
177
|
+
|
|
178
|
+
- `codex-skill`: installs to `~/.codex/skills/odcli/SKILL.md`
|
|
179
|
+
- `claude-skill`: installs to `~/.claude/skills/odcli/SKILL.md`
|
|
180
|
+
- `all-skills`: installs both
|
|
181
|
+
|
|
182
|
+
Examples:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
odcli plugin install codex-skill
|
|
186
|
+
odcli plugin install claude-skill
|
|
187
|
+
odcli plugin install all-skills
|
|
188
|
+
```
|
|
189
|
+
|
|
171
190
|
## Testing
|
|
172
191
|
|
|
173
192
|
```bash
|
|
@@ -5,18 +5,23 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
from obsidian_cli.commands import CommandRunner
|
|
7
7
|
from obsidian_cli.discovery import VaultLocator
|
|
8
|
+
from obsidian_cli.plugins import SkillInstaller
|
|
8
9
|
from obsidian_cli.vault import ObsidianVault, VaultError
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class ObsidianCLI:
|
|
12
13
|
def __init__(self, vault_locator: VaultLocator | None = None) -> None:
|
|
13
14
|
self._vault_locator = vault_locator or VaultLocator()
|
|
15
|
+
self._skill_installer = SkillInstaller()
|
|
14
16
|
self._parser = self._build_parser()
|
|
15
17
|
|
|
16
18
|
def run(self, argv: list[str] | None = None) -> int:
|
|
17
19
|
args = self._parser.parse_args(argv)
|
|
18
20
|
|
|
19
21
|
try:
|
|
22
|
+
if args.command == "plugin":
|
|
23
|
+
return self._run_plugin_command(args)
|
|
24
|
+
|
|
20
25
|
runner = CommandRunner(
|
|
21
26
|
ObsidianVault(self._vault_locator.resolve(args.vault))
|
|
22
27
|
)
|
|
@@ -145,8 +150,31 @@ class ObsidianCLI:
|
|
|
145
150
|
help="Use case-sensitive matching.",
|
|
146
151
|
)
|
|
147
152
|
|
|
153
|
+
plugin_parser = subparsers.add_parser(
|
|
154
|
+
"plugin", help="Install odcli helper skills for supported coding tools."
|
|
155
|
+
)
|
|
156
|
+
plugin_subparsers = plugin_parser.add_subparsers(
|
|
157
|
+
dest="plugin_command", required=True
|
|
158
|
+
)
|
|
159
|
+
plugin_install_parser = plugin_subparsers.add_parser(
|
|
160
|
+
"install", help="Install an odcli skill into a supported tool directory."
|
|
161
|
+
)
|
|
162
|
+
plugin_install_parser.add_argument(
|
|
163
|
+
"target",
|
|
164
|
+
choices=["codex-skill", "claude-skill", "all-skills"],
|
|
165
|
+
help="Installation target.",
|
|
166
|
+
)
|
|
167
|
+
|
|
148
168
|
return parser
|
|
149
169
|
|
|
170
|
+
def _run_plugin_command(self, args: argparse.Namespace) -> int:
|
|
171
|
+
if args.plugin_command == "install":
|
|
172
|
+
for result in self._skill_installer.install(args.target):
|
|
173
|
+
print(f"{result.target}: {result.path}")
|
|
174
|
+
return 0
|
|
175
|
+
self._parser.error(f"unsupported plugin command: {args.plugin_command}")
|
|
176
|
+
return 2
|
|
177
|
+
|
|
150
178
|
@staticmethod
|
|
151
179
|
def _read_content_arg(content: str | None, use_stdin: bool) -> str:
|
|
152
180
|
if content is not None and use_stdin:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
SKILL_BODY = """# odcli
|
|
8
|
+
|
|
9
|
+
Use `odcli` when you need to read or write notes inside a local Obsidian vault.
|
|
10
|
+
|
|
11
|
+
## What this skill does
|
|
12
|
+
|
|
13
|
+
- Reads notes from a local Obsidian vault
|
|
14
|
+
- Writes or appends Markdown notes
|
|
15
|
+
- Replaces specific line ranges in a note
|
|
16
|
+
- Searches across Markdown notes in the vault
|
|
17
|
+
- Works with either `odcli` or `obsidian-cli`
|
|
18
|
+
|
|
19
|
+
## Preferred workflow
|
|
20
|
+
|
|
21
|
+
1. Check whether `OBSIDIAN_VAULT` is set.
|
|
22
|
+
2. If it is not set, let `odcli` auto-discover the vault from local Obsidian config or common default locations.
|
|
23
|
+
3. Use `odcli` for direct note operations instead of editing vault files manually.
|
|
24
|
+
|
|
25
|
+
## Common commands
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
odcli check
|
|
29
|
+
odcli list
|
|
30
|
+
odcli read Inbox/today.md
|
|
31
|
+
odcli read-lines Inbox/today.md 3 8
|
|
32
|
+
odcli write Inbox/today.md --content "# Today"
|
|
33
|
+
odcli write-lines Inbox/today.md 3 4 --content "- replaced\\n- lines\\n"
|
|
34
|
+
odcli append Inbox/today.md --content "\\n- new item"
|
|
35
|
+
odcli search "project alpha"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- `--vault /path/to/vault` overrides auto-discovery.
|
|
41
|
+
- `OBSIDIAN_VAULT` is used when set.
|
|
42
|
+
- Line numbers for `read-lines` and `write-lines` are 1-based and inclusive.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class InstallResult:
|
|
48
|
+
target: str
|
|
49
|
+
path: Path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SkillInstaller:
|
|
53
|
+
def __init__(self, home: Path | None = None) -> None:
|
|
54
|
+
self._home = (home or Path.home()).expanduser()
|
|
55
|
+
|
|
56
|
+
def install(self, target: str) -> list[InstallResult]:
|
|
57
|
+
if target == "codex-skill":
|
|
58
|
+
return [
|
|
59
|
+
self._install_skill(
|
|
60
|
+
"codex-skill", self._home / ".codex" / "skills" / "odcli"
|
|
61
|
+
)
|
|
62
|
+
]
|
|
63
|
+
if target == "claude-skill":
|
|
64
|
+
return [
|
|
65
|
+
self._install_skill(
|
|
66
|
+
"claude-skill", self._home / ".claude" / "skills" / "odcli"
|
|
67
|
+
)
|
|
68
|
+
]
|
|
69
|
+
if target == "all-skills":
|
|
70
|
+
return [
|
|
71
|
+
self._install_skill(
|
|
72
|
+
"codex-skill", self._home / ".codex" / "skills" / "odcli"
|
|
73
|
+
),
|
|
74
|
+
self._install_skill(
|
|
75
|
+
"claude-skill", self._home / ".claude" / "skills" / "odcli"
|
|
76
|
+
),
|
|
77
|
+
]
|
|
78
|
+
raise ValueError(f"unsupported plugin install target: {target}")
|
|
79
|
+
|
|
80
|
+
def _install_skill(self, target: str, skill_dir: Path) -> InstallResult:
|
|
81
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
skill_file = skill_dir / "SKILL.md"
|
|
83
|
+
skill_file.write_text(SKILL_BODY, encoding="utf-8")
|
|
84
|
+
return InstallResult(target=target, path=skill_file)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odcli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A small CLI for reading and writing notes in a local Obsidian vault.
|
|
5
5
|
Author: Chang LeHung
|
|
6
6
|
Project-URL: Homepage, https://github.com/Chang-LeHung/obsidian-cli
|
|
@@ -35,6 +35,7 @@ It works directly on Markdown files inside the vault, so it does not depend on p
|
|
|
35
35
|
- Append content to a note
|
|
36
36
|
- Full-text search across the vault
|
|
37
37
|
- Auto-discover the default vault from Obsidian config or common macOS and Windows locations
|
|
38
|
+
- Install odcli helper skills into Codex or Claude Code skill directories
|
|
38
39
|
|
|
39
40
|
## Using uv
|
|
40
41
|
|
|
@@ -189,6 +190,24 @@ Arguments:
|
|
|
189
190
|
- `query`
|
|
190
191
|
- `--case-sensitive`
|
|
191
192
|
|
|
193
|
+
### `plugin install`
|
|
194
|
+
|
|
195
|
+
Install odcli helper skills for local coding tools.
|
|
196
|
+
|
|
197
|
+
Targets:
|
|
198
|
+
|
|
199
|
+
- `codex-skill`: installs to `~/.codex/skills/odcli/SKILL.md`
|
|
200
|
+
- `claude-skill`: installs to `~/.claude/skills/odcli/SKILL.md`
|
|
201
|
+
- `all-skills`: installs both
|
|
202
|
+
|
|
203
|
+
Examples:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
odcli plugin install codex-skill
|
|
207
|
+
odcli plugin install claude-skill
|
|
208
|
+
odcli plugin install all-skills
|
|
209
|
+
```
|
|
210
|
+
|
|
192
211
|
## Testing
|
|
193
212
|
|
|
194
213
|
```bash
|
|
@@ -12,6 +12,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
12
12
|
|
|
13
13
|
from obsidian_cli.cli import ObsidianCLI, main
|
|
14
14
|
from obsidian_cli.discovery import VaultCandidate, VaultLocator
|
|
15
|
+
from obsidian_cli.plugins import SkillInstaller
|
|
15
16
|
from obsidian_cli.vault import ObsidianVault, VaultError
|
|
16
17
|
|
|
17
18
|
|
|
@@ -132,6 +133,33 @@ class VaultTests(unittest.TestCase):
|
|
|
132
133
|
exit_code = cli.run(["check"])
|
|
133
134
|
self.assertEqual(exit_code, 0)
|
|
134
135
|
|
|
136
|
+
def test_skill_installer_installs_codex_skill(self) -> None:
|
|
137
|
+
installer = SkillInstaller(home=self.vault_root)
|
|
138
|
+
results = installer.install("codex-skill")
|
|
139
|
+
self.assertEqual(len(results), 1)
|
|
140
|
+
self.assertTrue(
|
|
141
|
+
(self.vault_root / ".codex" / "skills" / "odcli" / "SKILL.md").is_file()
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def test_skill_installer_installs_claude_skill(self) -> None:
|
|
145
|
+
installer = SkillInstaller(home=self.vault_root)
|
|
146
|
+
results = installer.install("claude-skill")
|
|
147
|
+
self.assertEqual(len(results), 1)
|
|
148
|
+
self.assertTrue(
|
|
149
|
+
(self.vault_root / ".claude" / "skills" / "odcli" / "SKILL.md").is_file()
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def test_cli_plugin_install_all_skills(self) -> None:
|
|
153
|
+
cli = ObsidianCLI(vault_locator=VaultLocator(env={}, home=self.vault_root))
|
|
154
|
+
cli._skill_installer = SkillInstaller(home=self.vault_root)
|
|
155
|
+
stdout = StringIO()
|
|
156
|
+
with redirect_stdout(stdout), redirect_stderr(StringIO()):
|
|
157
|
+
exit_code = cli.run(["plugin", "install", "all-skills"])
|
|
158
|
+
self.assertEqual(exit_code, 0)
|
|
159
|
+
output = stdout.getvalue()
|
|
160
|
+
self.assertIn("codex-skill:", output)
|
|
161
|
+
self.assertIn("claude-skill:", output)
|
|
162
|
+
|
|
135
163
|
|
|
136
164
|
if __name__ == "__main__":
|
|
137
165
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|