piagentsync 0.0.1rc0__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.
- piagentsync-0.0.1rc0/.env.example +8 -0
- piagentsync-0.0.1rc0/.github/scripts/generate_changelog.py +203 -0
- piagentsync-0.0.1rc0/.github/workflows/ci.yml +20 -0
- piagentsync-0.0.1rc0/.github/workflows/publish.yml +20 -0
- piagentsync-0.0.1rc0/.github/workflows/tag.yml +66 -0
- piagentsync-0.0.1rc0/.gitignore +55 -0
- piagentsync-0.0.1rc0/.python-version +1 -0
- piagentsync-0.0.1rc0/CHANGELOG.md +43 -0
- piagentsync-0.0.1rc0/LICENSE +21 -0
- piagentsync-0.0.1rc0/PKG-INFO +161 -0
- piagentsync-0.0.1rc0/README.md +147 -0
- piagentsync-0.0.1rc0/pyproject.toml +29 -0
- piagentsync-0.0.1rc0/src/piagentsync/__init__.py +1 -0
- piagentsync-0.0.1rc0/src/piagentsync/cli.py +272 -0
- piagentsync-0.0.1rc0/src/piagentsync/config.py +38 -0
- piagentsync-0.0.1rc0/src/piagentsync/errors.py +33 -0
- piagentsync-0.0.1rc0/src/piagentsync/manifest.py +30 -0
- piagentsync-0.0.1rc0/src/piagentsync/models.py +82 -0
- piagentsync-0.0.1rc0/src/piagentsync/sync.py +116 -0
- piagentsync-0.0.1rc0/tests/conftest.py +105 -0
- piagentsync-0.0.1rc0/tests/test_cli.py +278 -0
- piagentsync-0.0.1rc0/tests/test_config.py +58 -0
- piagentsync-0.0.1rc0/tests/test_manifest.py +87 -0
- piagentsync-0.0.1rc0/tests/test_models.py +97 -0
- piagentsync-0.0.1rc0/tests/test_sync.py +234 -0
- piagentsync-0.0.1rc0/uv.lock +469 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate changelog entry from conventional commits and compute next version.
|
|
3
|
+
|
|
4
|
+
This script:
|
|
5
|
+
1. Finds the latest stable semantic version tag (vX.Y.Z)
|
|
6
|
+
2. Computes the next version based on the BUMP input (MAJOR/MINOR/PATCH/RC)
|
|
7
|
+
3. Gathers conventional commit messages since that tag
|
|
8
|
+
4. Categorizes and formats them into a changelog entry
|
|
9
|
+
5. Prepends the entry to CHANGELOG.md
|
|
10
|
+
6. Outputs GITHUB_OUTPUT variables: new_version and tag_name
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run_git(*args):
|
|
22
|
+
"""Run a git command and return stdout."""
|
|
23
|
+
result = subprocess.run(["git"] + list(args), capture_output=True, text=True, check=True)
|
|
24
|
+
return result.stdout.strip()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_latest_stable_tag():
|
|
28
|
+
"""Get the latest vX.Y.Z tag (ignoring -rc tags)."""
|
|
29
|
+
try:
|
|
30
|
+
tags = run_git("tag", "-l", "v*", "--sort=-v:refname").splitlines()
|
|
31
|
+
except subprocess.CalledProcessError:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
for tag in tags:
|
|
35
|
+
# Match only pure semver without suffix
|
|
36
|
+
if re.fullmatch(r"v\d+\.\d+\.\d+", tag):
|
|
37
|
+
return tag
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def parse_version(tag):
|
|
42
|
+
"""Parse vX.Y.Z into (major, minor, patch)."""
|
|
43
|
+
if tag is None:
|
|
44
|
+
return (0, 0, 0)
|
|
45
|
+
m = re.match(r"v(\d+)\.(\d+)\.(\d+)", tag)
|
|
46
|
+
if not m:
|
|
47
|
+
return (0, 0, 0)
|
|
48
|
+
return tuple(int(x) for x in m.groups())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def bump_version(major, minor, patch, bump_type):
|
|
52
|
+
"""Return (new_major, new_minor, new_patch, suffix)."""
|
|
53
|
+
suffix = ""
|
|
54
|
+
if bump_type == "MAJOR":
|
|
55
|
+
major += 1
|
|
56
|
+
minor = 0
|
|
57
|
+
patch = 0
|
|
58
|
+
elif bump_type == "MINOR":
|
|
59
|
+
minor += 1
|
|
60
|
+
patch = 0
|
|
61
|
+
elif bump_type == "PATCH":
|
|
62
|
+
patch += 1
|
|
63
|
+
elif bump_type == "RC":
|
|
64
|
+
patch += 1
|
|
65
|
+
suffix = "-rc"
|
|
66
|
+
else:
|
|
67
|
+
raise ValueError(f"Unknown bump type: {bump_type}")
|
|
68
|
+
return major, minor, patch, suffix
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_commits_since_tag(tag):
|
|
72
|
+
"""Get commit messages from tag..HEAD (or all if tag is None)."""
|
|
73
|
+
if tag is None:
|
|
74
|
+
# All commits reachable from HEAD
|
|
75
|
+
rev_range = "HEAD"
|
|
76
|
+
else:
|
|
77
|
+
rev_range = f"{tag}..HEAD"
|
|
78
|
+
|
|
79
|
+
# Get conventional commits; format: <type>(<scope>): <subject>
|
|
80
|
+
# We'll grab the full subject line (skip body for changelog)
|
|
81
|
+
log = run_git("log", rev_range, "--pretty=format:%s", "--no-merges")
|
|
82
|
+
commits = [line.strip() for line in log.splitlines() if line.strip()]
|
|
83
|
+
return commits
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def categorize_commits(commits):
|
|
87
|
+
"""Map conventional commit types to changelog headings."""
|
|
88
|
+
mapping = {
|
|
89
|
+
"feat": "Added",
|
|
90
|
+
"fix": "Fixed",
|
|
91
|
+
"perf": "Performance",
|
|
92
|
+
"docs": "Documentation",
|
|
93
|
+
"style": "Style",
|
|
94
|
+
"refactor": "Refactored",
|
|
95
|
+
"test": "Tests",
|
|
96
|
+
"chore": "Chores",
|
|
97
|
+
"ci": "CI/CD",
|
|
98
|
+
"build": "Build",
|
|
99
|
+
"revert": "Reverts",
|
|
100
|
+
}
|
|
101
|
+
categories: dict[str, list[str]] = {v: [] for v in mapping.values()}
|
|
102
|
+
categories["Other"] = []
|
|
103
|
+
|
|
104
|
+
for msg in commits:
|
|
105
|
+
m = re.match(r"^([a-zA-Z]+)(?:\([^)]+\))?!?:\s*(.+)", msg)
|
|
106
|
+
if m:
|
|
107
|
+
commit_type = m.group(1).lower()
|
|
108
|
+
subject = m.group(2)
|
|
109
|
+
heading = mapping.get(commit_type, "Other")
|
|
110
|
+
categories[heading].append(f"- {msg}")
|
|
111
|
+
else:
|
|
112
|
+
# Non-conventional or cannot parse: include verbatim under Other
|
|
113
|
+
categories["Other"].append(f"- {msg}")
|
|
114
|
+
|
|
115
|
+
# Remove empty categories
|
|
116
|
+
return {k: v for k, v in categories.items() if v}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def format_changelog_entry(version, date, categories):
|
|
120
|
+
"""Format a single changelog entry for the given version."""
|
|
121
|
+
lines = [f"\n## [{version}] - {date}\n"]
|
|
122
|
+
for heading in ["Added", "Fixed", "Performance", "Documentation", "Style",
|
|
123
|
+
"Refactored", "Tests", "Chores", "CI/CD", "Build", "Reverts", "Other"]:
|
|
124
|
+
items = categories.get(heading)
|
|
125
|
+
if items:
|
|
126
|
+
lines.append(f"### {heading}\n" + "\n".join(items) + "\n")
|
|
127
|
+
return "".join(lines).strip() + "\n"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def prepend_to_changelog(entry, changelog_path):
|
|
131
|
+
"""Prepend entry to CHANGELOG.md, after any header."""
|
|
132
|
+
path = Path(changelog_path)
|
|
133
|
+
if not path.exists():
|
|
134
|
+
# Create minimal file with entry at top
|
|
135
|
+
header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
|
|
136
|
+
path.write_text(header + entry)
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
content = path.read_text(encoding="utf-8")
|
|
140
|
+
# Find the position of the first existing "## [" section
|
|
141
|
+
match = re.search(r"(?m)^## \[", content)
|
|
142
|
+
if match:
|
|
143
|
+
insert_pos = match.start()
|
|
144
|
+
new_content = content[:insert_pos] + entry + content[insert_pos:]
|
|
145
|
+
else:
|
|
146
|
+
# No existing entries, just append after header (or at end)
|
|
147
|
+
new_content = content.rstrip() + "\n\n" + entry
|
|
148
|
+
|
|
149
|
+
path.write_text(new_content, encoding="utf-8")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def tag_exists(tag):
|
|
153
|
+
"""Check if a git tag exists."""
|
|
154
|
+
try:
|
|
155
|
+
existing = run_git("tag", "-l", tag)
|
|
156
|
+
return tag in existing.splitlines()
|
|
157
|
+
except subprocess.CalledProcessError:
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def main():
|
|
162
|
+
bump = os.getenv("BUMP")
|
|
163
|
+
if not bump:
|
|
164
|
+
print("Error: BUMP environment variable not set", file=sys.stderr)
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
|
|
167
|
+
changelog_path = Path("CHANGELOG.md")
|
|
168
|
+
|
|
169
|
+
latest_tag = get_latest_stable_tag()
|
|
170
|
+
major, minor, patch = parse_version(latest_tag)
|
|
171
|
+
new_major, new_minor, new_patch, suffix = bump_version(major, minor, patch, bump)
|
|
172
|
+
new_version = f"{new_major}.{new_minor}.{new_patch}{suffix}"
|
|
173
|
+
tag_name = f"v{new_version}"
|
|
174
|
+
|
|
175
|
+
# Check if tag already exists
|
|
176
|
+
if tag_exists(tag_name):
|
|
177
|
+
print(f"Error: Tag '{tag_name}' already exists. Choose a different bump or delete the existing tag.", file=sys.stderr)
|
|
178
|
+
sys.exit(1)
|
|
179
|
+
|
|
180
|
+
commits = get_commits_since_tag(latest_tag)
|
|
181
|
+
categories = categorize_commits(commits)
|
|
182
|
+
|
|
183
|
+
date_str = datetime.now().strftime("%Y-%m-%d")
|
|
184
|
+
entry = format_changelog_entry(new_version, date_str, categories)
|
|
185
|
+
|
|
186
|
+
prepend_to_changelog(entry, changelog_path)
|
|
187
|
+
|
|
188
|
+
# Set GITHUB_OUTPUT
|
|
189
|
+
gh_output = os.getenv("GITHUB_OUTPUT")
|
|
190
|
+
if gh_output:
|
|
191
|
+
with open(gh_output, "a", encoding="utf-8") as f:
|
|
192
|
+
f.write(f"new_version={new_version}\n")
|
|
193
|
+
f.write(f"tag_name={tag_name}\n")
|
|
194
|
+
else:
|
|
195
|
+
# For local testing: print to stdout
|
|
196
|
+
print(f"::set-output name=new_version::{new_version}")
|
|
197
|
+
print(f"::set-output name=tag_name::{tag_name}")
|
|
198
|
+
|
|
199
|
+
print(f"Generated changelog for {tag_name}")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
main()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "**" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v4
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- run: uv sync --frozen
|
|
18
|
+
- run: uv run ruff check src/ tests/
|
|
19
|
+
- run: uv run ruff format --check src/ tests/
|
|
20
|
+
- run: uv run pytest tests/ --cov=piagentsync --cov-report=term-missing
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
id-token: write
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v4
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
- run: uv sync --frozen
|
|
19
|
+
- run: uv build
|
|
20
|
+
- run: uv publish
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Tag
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
bump:
|
|
7
|
+
description: 'Version bump type'
|
|
8
|
+
required: true
|
|
9
|
+
type: choice
|
|
10
|
+
options:
|
|
11
|
+
- PATCH
|
|
12
|
+
- MINOR
|
|
13
|
+
- MAJOR
|
|
14
|
+
- RC
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.13"
|
|
27
|
+
- run: uv sync --frozen
|
|
28
|
+
- run: uv run ruff check src/ tests/
|
|
29
|
+
- run: uv run ruff format --check src/ tests/
|
|
30
|
+
- run: uv run pytest tests/ --cov=piagentsync --cov-report=term-missing
|
|
31
|
+
|
|
32
|
+
tag:
|
|
33
|
+
needs: [test]
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
40
|
+
- name: Generate changelog and compute version
|
|
41
|
+
id: changelog
|
|
42
|
+
env:
|
|
43
|
+
BUMP: ${{ inputs.bump }}
|
|
44
|
+
run: |
|
|
45
|
+
python .github/scripts/generate_changelog.py
|
|
46
|
+
- name: Bump version in pyproject.toml
|
|
47
|
+
run: |
|
|
48
|
+
sed -i "s/^version = .*/version = \"${{ steps.changelog.outputs.new_version }}\"/" pyproject.toml
|
|
49
|
+
- name: Bump version in src/piagentsync/__init__.py
|
|
50
|
+
run: |
|
|
51
|
+
sed -i "s/^__version__ = .*/__version__ = \"${{ steps.changelog.outputs.new_version }}\"/" src/piagentsync/__init__.py
|
|
52
|
+
- name: Configure git identity
|
|
53
|
+
run: |
|
|
54
|
+
git config user.name "github-actions[bot]"
|
|
55
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
56
|
+
- name: Commit version bump
|
|
57
|
+
run: |
|
|
58
|
+
git add pyproject.toml src/piagentsync/__init__.py CHANGELOG.md
|
|
59
|
+
git commit -m "chore(release): bump version to ${{ steps.changelog.outputs.new_version }}"
|
|
60
|
+
git push origin main
|
|
61
|
+
- name: Create and push tag
|
|
62
|
+
run: |
|
|
63
|
+
git tag ${{ steps.changelog.outputs.tag_name }}
|
|
64
|
+
git push origin ${{ steps.changelog.outputs.tag_name }}
|
|
65
|
+
- name: Print confirmation
|
|
66
|
+
run: echo "Created tag ${{ steps.changelog.outputs.tag_name }} for version ${{ steps.changelog.outputs.new_version }}"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
venv/
|
|
25
|
+
env/
|
|
26
|
+
.venv
|
|
27
|
+
ENV/
|
|
28
|
+
env.bak/
|
|
29
|
+
venv.bak/
|
|
30
|
+
|
|
31
|
+
# IDEs
|
|
32
|
+
.idea/
|
|
33
|
+
.vscode/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
|
|
37
|
+
# Testing
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.coverage
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
|
|
43
|
+
# UV
|
|
44
|
+
.uv/
|
|
45
|
+
|
|
46
|
+
# Local config
|
|
47
|
+
.env
|
|
48
|
+
.env.local
|
|
49
|
+
|
|
50
|
+
# Project specific
|
|
51
|
+
.vault/
|
|
52
|
+
.opencode/
|
|
53
|
+
|
|
54
|
+
# macOS
|
|
55
|
+
.DS_Store
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.0.1-rc] - 2026-03-27
|
|
9
|
+
### Added
|
|
10
|
+
- feat(ci): automate changelog generation and remove test gate from publish
|
|
11
|
+
- feat(piagentsync): initial implementation with full test suite
|
|
12
|
+
### Fixed
|
|
13
|
+
- fix(ci): prevent duplicate tags by checking existing tags
|
|
14
|
+
### Tests
|
|
15
|
+
- test(cli): make version check dynamic using package __version__
|
|
16
|
+
### Chores
|
|
17
|
+
- chore(release): bump version to 0.0.1-rc
|
|
18
|
+
- chore(release): bump version to 0.0.1-rc
|
|
19
|
+
### CI/CD
|
|
20
|
+
- ci: add GitHub Actions workflows for CI, tagging, and publishing
|
|
21
|
+
### Other
|
|
22
|
+
- Initial commit
|
|
23
|
+
## [0.0.1-rc] - 2026-03-27
|
|
24
|
+
### Added
|
|
25
|
+
- feat(ci): automate changelog generation and remove test gate from publish
|
|
26
|
+
- feat(piagentsync): initial implementation with full test suite
|
|
27
|
+
### Tests
|
|
28
|
+
- test(cli): make version check dynamic using package __version__
|
|
29
|
+
### Chores
|
|
30
|
+
- chore(release): bump version to 0.0.1-rc
|
|
31
|
+
### CI/CD
|
|
32
|
+
- ci: add GitHub Actions workflows for CI, tagging, and publishing
|
|
33
|
+
### Other
|
|
34
|
+
- Initial commit
|
|
35
|
+
## [0.1.0] - 2026-03-27
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- Initial release of piagentsync
|
|
39
|
+
- Sync OpenCode agents and skills from Obsidian vault to workspace
|
|
40
|
+
- CLI commands: `pull`, `status`, `init`
|
|
41
|
+
- Support for global agents
|
|
42
|
+
- Dry-run mode
|
|
43
|
+
- Full TDD implementation with pytest
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Piwero
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: piagentsync
|
|
3
|
+
Version: 0.0.1rc0
|
|
4
|
+
Summary: Sync OpenCode agents and skills from an Obsidian vault to workspace directories
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: pydantic-settings>=2.3
|
|
9
|
+
Requires-Dist: pydantic>=2.7
|
|
10
|
+
Requires-Dist: python-frontmatter>=1.1
|
|
11
|
+
Requires-Dist: rich>=13
|
|
12
|
+
Requires-Dist: typer>=0.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# piagentsync
|
|
16
|
+
|
|
17
|
+
Sync OpenCode agents and skills from an Obsidian vault to workspace directories.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv add piagentsync
|
|
23
|
+
# or
|
|
24
|
+
pip install piagentsync
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
1. Initialize a new project in your vault:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
piagentsync init myproject --workspace ~/workspace/myproject
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. Pull the synced files to your workspace:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
piagentsync pull myproject
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
Environment variables (also configurable via `.env` file):
|
|
44
|
+
|
|
45
|
+
| Variable | Default | Description |
|
|
46
|
+
|----------|---------|-------------|
|
|
47
|
+
| `PIAGENTSYNC_VAULT_PATH` | `~/vault` | Path to Obsidian vault |
|
|
48
|
+
| `PIAGENTSYNC_GLOBAL_OPENCODE_PATH` | `~/.config/opencode` | Path to global OpenCode config |
|
|
49
|
+
|
|
50
|
+
## CLI reference
|
|
51
|
+
|
|
52
|
+
### `piagentsync pull <project>`
|
|
53
|
+
|
|
54
|
+
Sync a single project from vault to workspace.
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
- `--dry-run` / `--no-dry-run` — preview changes without writing
|
|
58
|
+
- `--global` / `--no-global` — also sync global agents
|
|
59
|
+
- `--all` — sync all discovered projects
|
|
60
|
+
|
|
61
|
+
### `piagentsync status [project]`
|
|
62
|
+
|
|
63
|
+
Show diff between vault and workspace.
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
- `--all` — show status for all projects (default when no project given)
|
|
67
|
+
|
|
68
|
+
### `piagentsync init <project>`
|
|
69
|
+
|
|
70
|
+
Scaffold a new project in the vault.
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
- `--workspace PATH` — required, workspace directory
|
|
74
|
+
- `--notion-board-id TEXT` — optional Notion DB ID
|
|
75
|
+
- `--notion-project-filter TEXT` — optional Notion project filter (defaults to project slug)
|
|
76
|
+
|
|
77
|
+
### `--version`
|
|
78
|
+
|
|
79
|
+
Print version and exit.
|
|
80
|
+
|
|
81
|
+
## AGENTS.md manifest format
|
|
82
|
+
|
|
83
|
+
Each project must have an `AGENTS.md` file in its root with YAML frontmatter:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
---
|
|
87
|
+
project: myproject
|
|
88
|
+
workspace: ~/workspace/myproject
|
|
89
|
+
notion_board_id: 3305f9479a8d8055b3c3e86a9006cf91
|
|
90
|
+
notion_project_filter: myproject
|
|
91
|
+
---
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The body below the frontmatter is the OpenCode routing table.
|
|
95
|
+
|
|
96
|
+
## Expected vault structure
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
vault/
|
|
100
|
+
├── agents/
|
|
101
|
+
│ └── global/
|
|
102
|
+
│ └── *.md
|
|
103
|
+
└── projects/
|
|
104
|
+
└── {project}/
|
|
105
|
+
├── AGENTS.md # manifest with frontmatter
|
|
106
|
+
├── context.md # optional context file
|
|
107
|
+
├── decisions.md # optional decisions log
|
|
108
|
+
├── agents/
|
|
109
|
+
│ └── *.md
|
|
110
|
+
└── skills/
|
|
111
|
+
└── *.md
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Contributing
|
|
115
|
+
|
|
116
|
+
Development setup:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv sync
|
|
120
|
+
uv run pytest
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Lint and format:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv run ruff check src/ tests/
|
|
127
|
+
uv run ruff format src/ tests/
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
All commits follow [Conventional Commits](https://www.conventionalcommits.org/).
|
|
131
|
+
|
|
132
|
+
### Publishing (maintainers)
|
|
133
|
+
|
|
134
|
+
This repository uses GitHub Actions for CI and automated releases.
|
|
135
|
+
|
|
136
|
+
#### One-time PyPI setup (trusted publisher)
|
|
137
|
+
|
|
138
|
+
1. Enable OIDC on PyPI for the repository:
|
|
139
|
+
- Publisher: GitHub Actions
|
|
140
|
+
- Repository owner: `Piwero`
|
|
141
|
+
- Repository name: `piagentsync`
|
|
142
|
+
- Workflow filename: `publish.yml`
|
|
143
|
+
- Environment name: (leave blank)
|
|
144
|
+
|
|
145
|
+
2. Push a tag to trigger the `tag.yml` workflow to bump version and create a git tag.
|
|
146
|
+
|
|
147
|
+
3. After the tag workflow completes, the `publish.yml` workflow will automatically build and publish the package to PyPI.
|
|
148
|
+
|
|
149
|
+
#### Release process
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
git push → ci.yml passes
|
|
153
|
+
→ trigger tag.yml (choose MAJOR/MINOR/PATCH/RC)
|
|
154
|
+
→ tests re-run → version bumped → tag pushed
|
|
155
|
+
→ trigger publish.yml
|
|
156
|
+
→ tests re-run → built → published to PyPI
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|