odcli 0.1.3__tar.gz → 0.1.5__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.
@@ -1,11 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: odcli
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: A small CLI for reading and writing notes in a local Obsidian vault.
5
- Author: Chang LeHung
6
- Project-URL: Homepage, https://github.com/Chang-LeHung/obsidian-cli
7
- Project-URL: Repository, https://github.com/Chang-LeHung/obsidian-cli
8
- Project-URL: Issues, https://github.com/Chang-LeHung/obsidian-cli/issues
5
+ Author: odcli contributors
9
6
  Keywords: obsidian,cli,markdown,notes,vault
10
7
  Classifier: Development Status :: 3 - Alpha
11
8
  Classifier: Intended Audience :: Developers
@@ -35,11 +32,12 @@ It works directly on Markdown files inside the vault, so it does not depend on p
35
32
  - Append content to a note
36
33
  - Full-text search across the vault
37
34
  - Auto-discover the default vault from Obsidian config or common macOS and Windows locations
35
+ - Install odcli helper skills into Codex or Claude Code skill directories
38
36
 
39
37
  ## Using uv
40
38
 
41
39
  ```bash
42
- cd /Users/huchang/agents/obsidian_cli
40
+ cd path/to/obsidian_cli
43
41
  uv sync
44
42
  uv run odcli --help
45
43
  ```
@@ -47,14 +45,14 @@ uv run odcli --help
47
45
  Run tests:
48
46
 
49
47
  ```bash
50
- cd /Users/huchang/agents/obsidian_cli
48
+ cd path/to/obsidian_cli
51
49
  uv run python -m unittest discover -s tests
52
50
  ```
53
51
 
54
52
  Build distributions:
55
53
 
56
54
  ```bash
57
- cd /Users/huchang/agents/obsidian_cli
55
+ cd path/to/obsidian_cli
58
56
  uv build
59
57
  ```
60
58
 
@@ -64,14 +62,14 @@ After installation, both `odcli` and `obsidian-cli` are available as command nam
64
62
  ## Run Locally
65
63
 
66
64
  ```bash
67
- cd /Users/huchang/agents/obsidian_cli
65
+ cd path/to/obsidian_cli
68
66
  ./odcli --help
69
67
  ```
70
68
 
71
69
  The compatibility entry point is still available:
72
70
 
73
71
  ```bash
74
- cd /Users/huchang/agents/obsidian_cli
72
+ cd path/to/obsidian_cli
75
73
  ./obsidian-cli --help
76
74
  ```
77
75
 
@@ -101,7 +99,7 @@ Built-in default locations:
101
99
  Example:
102
100
 
103
101
  ```bash
104
- export OBSIDIAN_VAULT="/Users/your-name/Documents/MyVault"
102
+ export OBSIDIAN_VAULT="/path/to/MyVault"
105
103
  ./odcli check
106
104
  ./odcli list
107
105
  ./odcli read Inbox/today.md
@@ -189,9 +187,27 @@ Arguments:
189
187
  - `query`
190
188
  - `--case-sensitive`
191
189
 
190
+ ### `plugin install`
191
+
192
+ Install odcli helper skills for local coding tools.
193
+
194
+ Targets:
195
+
196
+ - `codex-skill`: installs to `~/.codex/skills/odcli/SKILL.md`
197
+ - `claude-skill`: installs to `~/.claude/skills/odcli/SKILL.md`
198
+ - `all-skills`: installs both
199
+
200
+ Examples:
201
+
202
+ ```bash
203
+ odcli plugin install codex-skill
204
+ odcli plugin install claude-skill
205
+ odcli plugin install all-skills
206
+ ```
207
+
192
208
  ## Testing
193
209
 
194
210
  ```bash
195
- cd /Users/huchang/agents/obsidian_cli
211
+ cd path/to/obsidian_cli
196
212
  uv run python -m unittest discover -s tests
197
213
  ```
@@ -14,11 +14,12 @@ 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
 
20
21
  ```bash
21
- cd /Users/huchang/agents/obsidian_cli
22
+ cd path/to/obsidian_cli
22
23
  uv sync
23
24
  uv run odcli --help
24
25
  ```
@@ -26,14 +27,14 @@ uv run odcli --help
26
27
  Run tests:
27
28
 
28
29
  ```bash
29
- cd /Users/huchang/agents/obsidian_cli
30
+ cd path/to/obsidian_cli
30
31
  uv run python -m unittest discover -s tests
31
32
  ```
32
33
 
33
34
  Build distributions:
34
35
 
35
36
  ```bash
36
- cd /Users/huchang/agents/obsidian_cli
37
+ cd path/to/obsidian_cli
37
38
  uv build
38
39
  ```
39
40
 
@@ -43,14 +44,14 @@ After installation, both `odcli` and `obsidian-cli` are available as command nam
43
44
  ## Run Locally
44
45
 
45
46
  ```bash
46
- cd /Users/huchang/agents/obsidian_cli
47
+ cd path/to/obsidian_cli
47
48
  ./odcli --help
48
49
  ```
49
50
 
50
51
  The compatibility entry point is still available:
51
52
 
52
53
  ```bash
53
- cd /Users/huchang/agents/obsidian_cli
54
+ cd path/to/obsidian_cli
54
55
  ./obsidian-cli --help
55
56
  ```
56
57
 
@@ -80,7 +81,7 @@ Built-in default locations:
80
81
  Example:
81
82
 
82
83
  ```bash
83
- export OBSIDIAN_VAULT="/Users/your-name/Documents/MyVault"
84
+ export OBSIDIAN_VAULT="/path/to/MyVault"
84
85
  ./odcli check
85
86
  ./odcli list
86
87
  ./odcli read Inbox/today.md
@@ -168,9 +169,27 @@ 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
174
- cd /Users/huchang/agents/obsidian_cli
193
+ cd path/to/obsidian_cli
175
194
  uv run python -m unittest discover -s tests
176
195
  ```
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "odcli"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "A small CLI for reading and writing notes in a local Obsidian vault."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  authors = [
12
- { name = "Chang LeHung" }
12
+ { name = "odcli contributors" }
13
13
  ]
14
14
  dependencies = []
15
15
  keywords = ["obsidian", "cli", "markdown", "notes", "vault"]
@@ -25,11 +25,6 @@ classifiers = [
25
25
  "Environment :: Console",
26
26
  ]
27
27
 
28
- [project.urls]
29
- Homepage = "https://github.com/Chang-LeHung/obsidian-cli"
30
- Repository = "https://github.com/Chang-LeHung/obsidian-cli"
31
- Issues = "https://github.com/Chang-LeHung/obsidian-cli/issues"
32
-
33
28
  [project.scripts]
34
29
  odcli = "obsidian_cli.cli:main"
35
30
  obsidian-cli = "obsidian_cli.cli:main"
@@ -1,3 +1,3 @@
1
1
  __all__ = ["__version__"]
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.1.5"
@@ -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,11 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: odcli
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: A small CLI for reading and writing notes in a local Obsidian vault.
5
- Author: Chang LeHung
6
- Project-URL: Homepage, https://github.com/Chang-LeHung/obsidian-cli
7
- Project-URL: Repository, https://github.com/Chang-LeHung/obsidian-cli
8
- Project-URL: Issues, https://github.com/Chang-LeHung/obsidian-cli/issues
5
+ Author: odcli contributors
9
6
  Keywords: obsidian,cli,markdown,notes,vault
10
7
  Classifier: Development Status :: 3 - Alpha
11
8
  Classifier: Intended Audience :: Developers
@@ -35,11 +32,12 @@ It works directly on Markdown files inside the vault, so it does not depend on p
35
32
  - Append content to a note
36
33
  - Full-text search across the vault
37
34
  - Auto-discover the default vault from Obsidian config or common macOS and Windows locations
35
+ - Install odcli helper skills into Codex or Claude Code skill directories
38
36
 
39
37
  ## Using uv
40
38
 
41
39
  ```bash
42
- cd /Users/huchang/agents/obsidian_cli
40
+ cd path/to/obsidian_cli
43
41
  uv sync
44
42
  uv run odcli --help
45
43
  ```
@@ -47,14 +45,14 @@ uv run odcli --help
47
45
  Run tests:
48
46
 
49
47
  ```bash
50
- cd /Users/huchang/agents/obsidian_cli
48
+ cd path/to/obsidian_cli
51
49
  uv run python -m unittest discover -s tests
52
50
  ```
53
51
 
54
52
  Build distributions:
55
53
 
56
54
  ```bash
57
- cd /Users/huchang/agents/obsidian_cli
55
+ cd path/to/obsidian_cli
58
56
  uv build
59
57
  ```
60
58
 
@@ -64,14 +62,14 @@ After installation, both `odcli` and `obsidian-cli` are available as command nam
64
62
  ## Run Locally
65
63
 
66
64
  ```bash
67
- cd /Users/huchang/agents/obsidian_cli
65
+ cd path/to/obsidian_cli
68
66
  ./odcli --help
69
67
  ```
70
68
 
71
69
  The compatibility entry point is still available:
72
70
 
73
71
  ```bash
74
- cd /Users/huchang/agents/obsidian_cli
72
+ cd path/to/obsidian_cli
75
73
  ./obsidian-cli --help
76
74
  ```
77
75
 
@@ -101,7 +99,7 @@ Built-in default locations:
101
99
  Example:
102
100
 
103
101
  ```bash
104
- export OBSIDIAN_VAULT="/Users/your-name/Documents/MyVault"
102
+ export OBSIDIAN_VAULT="/path/to/MyVault"
105
103
  ./odcli check
106
104
  ./odcli list
107
105
  ./odcli read Inbox/today.md
@@ -189,9 +187,27 @@ Arguments:
189
187
  - `query`
190
188
  - `--case-sensitive`
191
189
 
190
+ ### `plugin install`
191
+
192
+ Install odcli helper skills for local coding tools.
193
+
194
+ Targets:
195
+
196
+ - `codex-skill`: installs to `~/.codex/skills/odcli/SKILL.md`
197
+ - `claude-skill`: installs to `~/.claude/skills/odcli/SKILL.md`
198
+ - `all-skills`: installs both
199
+
200
+ Examples:
201
+
202
+ ```bash
203
+ odcli plugin install codex-skill
204
+ odcli plugin install claude-skill
205
+ odcli plugin install all-skills
206
+ ```
207
+
192
208
  ## Testing
193
209
 
194
210
  ```bash
195
- cd /Users/huchang/agents/obsidian_cli
211
+ cd path/to/obsidian_cli
196
212
  uv run python -m unittest discover -s tests
197
213
  ```
@@ -5,6 +5,7 @@ src/obsidian_cli/__main__.py
5
5
  src/obsidian_cli/cli.py
6
6
  src/obsidian_cli/commands.py
7
7
  src/obsidian_cli/discovery.py
8
+ src/obsidian_cli/plugins.py
8
9
  src/obsidian_cli/vault.py
9
10
  src/odcli.egg-info/PKG-INFO
10
11
  src/odcli.egg-info/SOURCES.txt
@@ -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