chronicle-dev 0.1.0__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.
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chronicle-dev
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI-native development memory — markdown RAG for every AI coding tool
|
|
5
|
+
Project-URL: Homepage, https://github.com/ypollak2/chronicle
|
|
6
|
+
Project-URL: Repository, https://github.com/ypollak2/chronicle
|
|
7
|
+
Project-URL: Issues, https://github.com/ypollak2/chronicle/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: ai,claude,codex,cursor,developer-tools,llm,rag
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# chronicle-dev (Python)
|
|
22
|
+
|
|
23
|
+
> AI-native development memory — markdown RAG for every AI coding tool
|
|
24
|
+
|
|
25
|
+
This is the Python wrapper for [Chronicle](https://github.com/ypollak2/chronicle).
|
|
26
|
+
It delegates all work to the Node.js CLI (`chronicle-dev` on npm).
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Python ≥ 3.9
|
|
31
|
+
- Node.js ≥ 20 ([install](https://nodejs.org))
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install chronicle-dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
Identical to the npm version:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
chronicle init # scan last 6 months of git history
|
|
45
|
+
chronicle inject | claude # pipe context into Claude
|
|
46
|
+
chronicle inject | codex # or Codex, Gemini CLI, Aider...
|
|
47
|
+
chronicle hooks install # passive capture on every commit
|
|
48
|
+
chronicle deepen --depth=1year # scan further back
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## How it works
|
|
52
|
+
|
|
53
|
+
`pip install chronicle-dev` installs a `chronicle` entry point that:
|
|
54
|
+
1. Checks Node ≥ 20 is available
|
|
55
|
+
2. Delegates to `chronicle` (if globally installed) or `npx chronicle-dev`
|
|
56
|
+
|
|
57
|
+
The Node package is the source of truth. This wrapper exists so Python developers
|
|
58
|
+
can install Chronicle without switching to npm.
|
|
59
|
+
|
|
60
|
+
## Full docs
|
|
61
|
+
|
|
62
|
+
See [github.com/ypollak2/chronicle](https://github.com/ypollak2/chronicle)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# chronicle-dev (Python)
|
|
2
|
+
|
|
3
|
+
> AI-native development memory — markdown RAG for every AI coding tool
|
|
4
|
+
|
|
5
|
+
This is the Python wrapper for [Chronicle](https://github.com/ypollak2/chronicle).
|
|
6
|
+
It delegates all work to the Node.js CLI (`chronicle-dev` on npm).
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Python ≥ 3.9
|
|
11
|
+
- Node.js ≥ 20 ([install](https://nodejs.org))
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install chronicle-dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Identical to the npm version:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
chronicle init # scan last 6 months of git history
|
|
25
|
+
chronicle inject | claude # pipe context into Claude
|
|
26
|
+
chronicle inject | codex # or Codex, Gemini CLI, Aider...
|
|
27
|
+
chronicle hooks install # passive capture on every commit
|
|
28
|
+
chronicle deepen --depth=1year # scan further back
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## How it works
|
|
32
|
+
|
|
33
|
+
`pip install chronicle-dev` installs a `chronicle` entry point that:
|
|
34
|
+
1. Checks Node ≥ 20 is available
|
|
35
|
+
2. Delegates to `chronicle` (if globally installed) or `npx chronicle-dev`
|
|
36
|
+
|
|
37
|
+
The Node package is the source of truth. This wrapper exists so Python developers
|
|
38
|
+
can install Chronicle without switching to npm.
|
|
39
|
+
|
|
40
|
+
## Full docs
|
|
41
|
+
|
|
42
|
+
See [github.com/ypollak2/chronicle](https://github.com/ypollak2/chronicle)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Chronicle — AI-native development memory.
|
|
3
|
+
|
|
4
|
+
This package is a thin wrapper around the Node.js chronicle-dev CLI.
|
|
5
|
+
For the full API, use the CLI directly or via subprocess.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
import subprocess
|
|
9
|
+
result = subprocess.run(["chronicle", "inject"], capture_output=True, text=True)
|
|
10
|
+
context = result.stdout
|
|
11
|
+
"""
|
|
12
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Subprocess wrapper — delegates all work to the Node binary (chronicle-dev).
|
|
3
|
+
Requires Node ≥ 20 on PATH. The Node package is the source of truth.
|
|
4
|
+
"""
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _node_available() -> bool:
|
|
11
|
+
node = shutil.which("node")
|
|
12
|
+
if not node:
|
|
13
|
+
return False
|
|
14
|
+
try:
|
|
15
|
+
out = subprocess.check_output(["node", "--version"], text=True).strip()
|
|
16
|
+
major = int(out.lstrip("v").split(".")[0])
|
|
17
|
+
return major >= 20
|
|
18
|
+
except Exception:
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _chronicle_binary() -> list[str]:
|
|
23
|
+
"""Return the command list that invokes the Node chronicle CLI.
|
|
24
|
+
|
|
25
|
+
Never calls 'chronicle' directly — that would call this Python wrapper
|
|
26
|
+
recursively. Always delegates to the Node package via npm/npx.
|
|
27
|
+
"""
|
|
28
|
+
# If the user has `npm install -g chronicle-dev`, the Node binary lands
|
|
29
|
+
# at a path like /usr/local/bin/chronicle — but so does this Python script.
|
|
30
|
+
# We disambiguate by checking for the npm global bin explicitly.
|
|
31
|
+
npm = shutil.which("npm")
|
|
32
|
+
if npm:
|
|
33
|
+
try:
|
|
34
|
+
prefix = subprocess.check_output(
|
|
35
|
+
[npm, "root", "-g"], text=True, stderr=subprocess.DEVNULL
|
|
36
|
+
).strip()
|
|
37
|
+
# npm global bin is one level up from global node_modules
|
|
38
|
+
import os
|
|
39
|
+
global_bin = os.path.join(os.path.dirname(prefix), "bin", "chronicle")
|
|
40
|
+
# Only use it if it's a Node script (not this Python script)
|
|
41
|
+
if os.path.exists(global_bin):
|
|
42
|
+
with open(global_bin) as f:
|
|
43
|
+
first_line = f.readline()
|
|
44
|
+
if "node" in first_line and "python" not in first_line:
|
|
45
|
+
return [global_bin]
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# Fallback: npx installs on-demand from npm registry
|
|
50
|
+
return ["npx", "--yes", "chronicle-dev"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main() -> None:
|
|
54
|
+
if not _node_available():
|
|
55
|
+
print(
|
|
56
|
+
"Chronicle requires Node.js ≥ 20.\n"
|
|
57
|
+
"Install it from https://nodejs.org or via `brew install node`.",
|
|
58
|
+
file=sys.stderr,
|
|
59
|
+
)
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
|
|
62
|
+
cmd = _chronicle_binary() + sys.argv[1:]
|
|
63
|
+
result = subprocess.run(cmd)
|
|
64
|
+
sys.exit(result.returncode)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "chronicle-dev"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "AI-native development memory — markdown RAG for every AI coding tool"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
keywords = ["ai", "llm", "rag", "developer-tools", "claude", "codex", "cursor"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# No runtime dependencies — Node does all the work
|
|
23
|
+
dependencies = []
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["pytest>=7.0"]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
chronicle = "chronicle._cli:main"
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/ypollak2/chronicle"
|
|
33
|
+
Repository = "https://github.com/ypollak2/chronicle"
|
|
34
|
+
Issues = "https://github.com/ypollak2/chronicle/issues"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["chronicle"]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Tests for the Chronicle Python wrapper."""
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_node_available_when_node_on_path():
|
|
9
|
+
"""Node must be detectable via shutil.which."""
|
|
10
|
+
import shutil
|
|
11
|
+
assert shutil.which("node") is not None, "Node not found — required for Chronicle"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_node_version_is_20_plus():
|
|
15
|
+
"""Node version must be >= 20."""
|
|
16
|
+
result = subprocess.run(["node", "--version"], capture_output=True, text=True)
|
|
17
|
+
assert result.returncode == 0
|
|
18
|
+
version_str = result.stdout.strip().lstrip("v")
|
|
19
|
+
major = int(version_str.split(".")[0])
|
|
20
|
+
assert major >= 20, f"Node {version_str} < 20"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_main_exits_nonzero_without_node(monkeypatch):
|
|
24
|
+
"""main() exits with code 1 when Node is not available."""
|
|
25
|
+
from chronicle._cli import _node_available
|
|
26
|
+
with patch("chronicle._cli._node_available", return_value=False):
|
|
27
|
+
with pytest.raises(SystemExit) as exc:
|
|
28
|
+
from chronicle._cli import main
|
|
29
|
+
main()
|
|
30
|
+
assert exc.value.code == 1
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_chronicle_binary_prefers_node_global():
|
|
34
|
+
"""_chronicle_binary returns a list starting with a string."""
|
|
35
|
+
from chronicle._cli import _chronicle_binary
|
|
36
|
+
cmd = _chronicle_binary()
|
|
37
|
+
assert isinstance(cmd, list)
|
|
38
|
+
assert len(cmd) >= 1
|
|
39
|
+
assert isinstance(cmd[0], str)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_chronicle_binary_fallback_is_npx():
|
|
43
|
+
"""Falls back to npx when no global Node binary found."""
|
|
44
|
+
import shutil
|
|
45
|
+
with patch("shutil.which", return_value=None):
|
|
46
|
+
from importlib import reload
|
|
47
|
+
import chronicle._cli as cli_module
|
|
48
|
+
# Simulate npm not found → must fall back to npx
|
|
49
|
+
with patch("subprocess.check_output", side_effect=Exception("no npm")):
|
|
50
|
+
cmd = cli_module._chronicle_binary()
|
|
51
|
+
assert cmd[0] in ("npx", "chronicle"), f"Unexpected fallback: {cmd}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_package_version_defined():
|
|
55
|
+
"""Package version must be defined."""
|
|
56
|
+
import chronicle
|
|
57
|
+
assert hasattr(chronicle, "__version__")
|
|
58
|
+
assert chronicle.__version__ == "0.1.0"
|