opsward 0.0.2__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.
- opsward-0.0.2/.gitattributes +1 -0
- opsward-0.0.2/.github/workflows/ci.yml +213 -0
- opsward-0.0.2/.gitignore +121 -0
- opsward-0.0.2/CLAUDE.md +174 -0
- opsward-0.0.2/LICENSE +21 -0
- opsward-0.0.2/PKG-INFO +135 -0
- opsward-0.0.2/README.md +113 -0
- opsward-0.0.2/misc/docs/ai_setup_meta_tooling.md +266 -0
- opsward-0.0.2/misc/docs/architecture.md +77 -0
- opsward-0.0.2/misc/docs/conventions.md +39 -0
- opsward-0.0.2/misc/docs/docs_catalog.md +83 -0
- opsward-0.0.2/misc/docs/docs_guide.md +15 -0
- opsward-0.0.2/misc/docs/known_issues.md +12 -0
- opsward-0.0.2/misc/docs/scoring_rubric.md +110 -0
- opsward-0.0.2/misc/docs/target_artifacts.md +131 -0
- opsward-0.0.2/opsward/__init__.py +18 -0
- opsward-0.0.2/opsward/__main__.py +13 -0
- opsward-0.0.2/opsward/base.py +204 -0
- opsward-0.0.2/opsward/cli.py +211 -0
- opsward-0.0.2/opsward/data/__init__.py +0 -0
- opsward-0.0.2/opsward/data/templates/__init__.py +0 -0
- opsward-0.0.2/opsward/data/templates/jsts/conventions.md +30 -0
- opsward-0.0.2/opsward/data/templates/jsts/testing.md +28 -0
- opsward-0.0.2/opsward/data/templates/python/conventions.md +33 -0
- opsward-0.0.2/opsward/data/templates/python/testing.md +29 -0
- opsward-0.0.2/opsward/data/templates/shared/architecture.md +21 -0
- opsward-0.0.2/opsward/data/templates/shared/claude_md.md +28 -0
- opsward-0.0.2/opsward/data/templates/shared/decisions/0000-template.md +17 -0
- opsward-0.0.2/opsward/data/templates/shared/dependencies.md +17 -0
- opsward-0.0.2/opsward/data/templates/shared/deployment.md +17 -0
- opsward-0.0.2/opsward/data/templates/shared/diagnose-setup/SKILL.md +22 -0
- opsward-0.0.2/opsward/data/templates/shared/docs_guide.md +24 -0
- opsward-0.0.2/opsward/data/templates/shared/glossary.md +7 -0
- opsward-0.0.2/opsward/data/templates/shared/known_issues.md +16 -0
- opsward-0.0.2/opsward/data/templates/shared/maintain-docs/SKILL.md +21 -0
- opsward-0.0.2/opsward/data/templates/shared/roadmap.md +17 -0
- opsward-0.0.2/opsward/data/templates/shared/session_log.md +10 -0
- opsward-0.0.2/opsward/data/templates/shared/setup-auditor.md +21 -0
- opsward-0.0.2/opsward/generate.py +422 -0
- opsward-0.0.2/opsward/maintain.py +237 -0
- opsward-0.0.2/opsward/scan.py +155 -0
- opsward-0.0.2/opsward/score.py +451 -0
- opsward-0.0.2/opsward/util.py +48 -0
- opsward-0.0.2/pyproject.toml +159 -0
- opsward-0.0.2/tests/__init__.py +0 -0
- opsward-0.0.2/tests/fixtures/jsts_project/package.json +1 -0
- opsward-0.0.2/tests/fixtures/jsts_project/tsconfig.json +1 -0
- opsward-0.0.2/tests/fixtures/python_project/.claude/agents/code-reviewer.md +3 -0
- opsward-0.0.2/tests/fixtures/python_project/.claude/hooks.json +1 -0
- opsward-0.0.2/tests/fixtures/python_project/.claude/rules/no-print.md +1 -0
- opsward-0.0.2/tests/fixtures/python_project/.claude/skills/my-skill/SKILL.md +5 -0
- opsward-0.0.2/tests/fixtures/python_project/CLAUDE.md +7 -0
- opsward-0.0.2/tests/fixtures/python_project/docs/architecture.md +3 -0
- opsward-0.0.2/tests/fixtures/python_project/docs/docs_guide.md +3 -0
- opsward-0.0.2/tests/fixtures/python_project/pyproject.toml +3 -0
- opsward-0.0.2/tests/fixtures/stale_project/CLAUDE.md +18 -0
- opsward-0.0.2/tests/fixtures/stale_project/docs/architecture.md +3 -0
- opsward-0.0.2/tests/fixtures/stale_project/docs/conventions.md +3 -0
- opsward-0.0.2/tests/fixtures/stale_project/docs/docs_guide.md +9 -0
- opsward-0.0.2/tests/fixtures/stale_project/docs/unlisted_doc.md +3 -0
- opsward-0.0.2/tests/fixtures/stale_project/pyproject.toml +3 -0
- opsward-0.0.2/tests/test_base.py +86 -0
- opsward-0.0.2/tests/test_cli.py +75 -0
- opsward-0.0.2/tests/test_generate.py +224 -0
- opsward-0.0.2/tests/test_maintain.py +156 -0
- opsward-0.0.2/tests/test_scan.py +140 -0
- opsward-0.0.2/tests/test_score.py +127 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.ipynb linguist-documentation
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
name: Continuous Integration (2025-11)
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
|
|
4
|
+
# Note: Environment variables (PROJECT_NAME and vars from [tool.wads.ci.env])
|
|
5
|
+
# are set by the read-ci-config action in the setup job and made available
|
|
6
|
+
# to all subsequent jobs via GITHUB_ENV
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
# First job: Read configuration from pyproject.toml
|
|
10
|
+
setup:
|
|
11
|
+
name: Read Configuration
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
outputs:
|
|
14
|
+
project-name: ${{ steps.config.outputs.project-name }}
|
|
15
|
+
python-versions: ${{ steps.config.outputs.python-versions }}
|
|
16
|
+
pytest-args: ${{ steps.config.outputs.pytest-args }}
|
|
17
|
+
coverage-enabled: ${{ steps.config.outputs.coverage-enabled }}
|
|
18
|
+
exclude-paths: ${{ steps.config.outputs.exclude-paths }}
|
|
19
|
+
test-on-windows: ${{ steps.config.outputs.test-on-windows }}
|
|
20
|
+
build-sdist: ${{ steps.config.outputs.build-sdist }}
|
|
21
|
+
build-wheel: ${{ steps.config.outputs.build-wheel }}
|
|
22
|
+
metrics-enabled: ${{ steps.config.outputs.metrics-enabled }}
|
|
23
|
+
metrics-config-path: ${{ steps.config.outputs.metrics-config-path }}
|
|
24
|
+
metrics-storage-branch: ${{ steps.config.outputs.metrics-storage-branch }}
|
|
25
|
+
metrics-python-version: ${{ steps.config.outputs.metrics-python-version }}
|
|
26
|
+
metrics-force-run: ${{ steps.config.outputs.metrics-force-run }}
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Read CI Config
|
|
32
|
+
id: config
|
|
33
|
+
uses: i2mint/wads/actions/read-ci-config@master
|
|
34
|
+
with:
|
|
35
|
+
pyproject-path: .
|
|
36
|
+
|
|
37
|
+
# Second job: Validation using the config
|
|
38
|
+
validation:
|
|
39
|
+
name: Validation
|
|
40
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
41
|
+
needs: setup
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
strategy:
|
|
44
|
+
matrix:
|
|
45
|
+
python-version: ${{ fromJson(needs.setup.outputs.python-versions) }}
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
51
|
+
uses: actions/setup-python@v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: ${{ matrix.python-version }}
|
|
54
|
+
|
|
55
|
+
- name: Install System Dependencies
|
|
56
|
+
uses: i2mint/wads/actions/install-system-deps@master
|
|
57
|
+
with:
|
|
58
|
+
pyproject-path: .
|
|
59
|
+
|
|
60
|
+
- name: Install Dependencies
|
|
61
|
+
uses: i2mint/wads/actions/install-deps@master
|
|
62
|
+
with:
|
|
63
|
+
dependency-files: pyproject.toml
|
|
64
|
+
extras: dev,test
|
|
65
|
+
|
|
66
|
+
- name: Format Source Code
|
|
67
|
+
uses: i2mint/wads/actions/ruff-format@master
|
|
68
|
+
with:
|
|
69
|
+
line-length: 88
|
|
70
|
+
target-path: .
|
|
71
|
+
|
|
72
|
+
- name: Lint Validation
|
|
73
|
+
uses: i2mint/wads/actions/ruff-lint@master
|
|
74
|
+
with:
|
|
75
|
+
root-dir: ${{ needs.setup.outputs.project-name }}
|
|
76
|
+
output-format: github
|
|
77
|
+
|
|
78
|
+
- name: Run Tests
|
|
79
|
+
uses: i2mint/wads/actions/run-tests@master
|
|
80
|
+
with:
|
|
81
|
+
root-dir: ${{ needs.setup.outputs.project-name }}
|
|
82
|
+
exclude: ${{ needs.setup.outputs.exclude-paths }}
|
|
83
|
+
coverage: ${{ needs.setup.outputs.coverage-enabled }}
|
|
84
|
+
pytest-args: ${{ needs.setup.outputs.pytest-args }}
|
|
85
|
+
|
|
86
|
+
- name: Track Code Metrics
|
|
87
|
+
if: needs.setup.outputs.metrics-enabled == 'true'
|
|
88
|
+
uses: i2mint/umpyre/actions/track-metrics@master
|
|
89
|
+
continue-on-error: true
|
|
90
|
+
with:
|
|
91
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
92
|
+
config-path: ${{ needs.setup.outputs.metrics-config-path }}
|
|
93
|
+
storage-branch: ${{ needs.setup.outputs.metrics-storage-branch }}
|
|
94
|
+
python-version: ${{ needs.setup.outputs.metrics-python-version }}
|
|
95
|
+
force-run: ${{ needs.setup.outputs.metrics-force-run }}
|
|
96
|
+
|
|
97
|
+
# Optional Windows testing (if enabled in config)
|
|
98
|
+
windows-validation:
|
|
99
|
+
name: Windows Tests
|
|
100
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]') && needs.setup.outputs.test-on-windows == 'true'"
|
|
101
|
+
needs: setup
|
|
102
|
+
runs-on: windows-latest
|
|
103
|
+
continue-on-error: true
|
|
104
|
+
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v4
|
|
107
|
+
|
|
108
|
+
- name: Set up Python
|
|
109
|
+
uses: actions/setup-python@v6
|
|
110
|
+
with:
|
|
111
|
+
python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
|
|
112
|
+
|
|
113
|
+
- name: Install System Dependencies
|
|
114
|
+
uses: i2mint/wads/actions/install-system-deps@master
|
|
115
|
+
with:
|
|
116
|
+
pyproject-path: .
|
|
117
|
+
|
|
118
|
+
- name: Install Dependencies
|
|
119
|
+
uses: i2mint/wads/actions/install-deps@master
|
|
120
|
+
with:
|
|
121
|
+
dependency-files: pyproject.toml
|
|
122
|
+
extras: dev,test
|
|
123
|
+
|
|
124
|
+
- name: Run tests
|
|
125
|
+
id: test
|
|
126
|
+
continue-on-error: true
|
|
127
|
+
run: pytest
|
|
128
|
+
|
|
129
|
+
- name: Report test results
|
|
130
|
+
if: always()
|
|
131
|
+
run: |
|
|
132
|
+
if ("${{ steps.test.outcome }}" -eq "failure") {
|
|
133
|
+
echo "::warning::Windows tests failed but workflow continues"
|
|
134
|
+
echo "## ⚠️ Windows Tests Failed" >> $env:GITHUB_STEP_SUMMARY
|
|
135
|
+
echo "Tests failed on Windows but this is informational only." >> $env:GITHUB_STEP_SUMMARY
|
|
136
|
+
} else {
|
|
137
|
+
echo "## ✅ Windows Tests Passed" >> $env:GITHUB_STEP_SUMMARY
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Publishing job
|
|
141
|
+
publish:
|
|
142
|
+
name: Publish
|
|
143
|
+
permissions:
|
|
144
|
+
contents: write
|
|
145
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]') && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')"
|
|
146
|
+
needs: [setup, validation]
|
|
147
|
+
runs-on: ubuntu-latest
|
|
148
|
+
|
|
149
|
+
steps:
|
|
150
|
+
- uses: actions/checkout@v4
|
|
151
|
+
with:
|
|
152
|
+
fetch-depth: 0
|
|
153
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
154
|
+
|
|
155
|
+
- name: Set up Python
|
|
156
|
+
uses: actions/setup-python@v6
|
|
157
|
+
with:
|
|
158
|
+
python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
|
|
159
|
+
|
|
160
|
+
- name: Format Source Code
|
|
161
|
+
uses: i2mint/wads/actions/ruff-format@master
|
|
162
|
+
|
|
163
|
+
- name: Update Version Number
|
|
164
|
+
id: version
|
|
165
|
+
uses: i2mint/isee/actions/bump-version-number@master
|
|
166
|
+
|
|
167
|
+
- name: Build Distribution
|
|
168
|
+
uses: i2mint/wads/actions/build-dist@master
|
|
169
|
+
with:
|
|
170
|
+
sdist: ${{ needs.setup.outputs.build-sdist }}
|
|
171
|
+
wheel: ${{ needs.setup.outputs.build-wheel }}
|
|
172
|
+
|
|
173
|
+
- name: Publish to PyPI
|
|
174
|
+
uses: i2mint/wads/actions/pypi-upload@master
|
|
175
|
+
with:
|
|
176
|
+
pypi-username: ${{ secrets.PYPI_USERNAME }}
|
|
177
|
+
pypi-password: ${{ secrets.PYPI_PASSWORD }}
|
|
178
|
+
skip-existing: false
|
|
179
|
+
|
|
180
|
+
- name: Force SSH for git remote
|
|
181
|
+
run: |
|
|
182
|
+
git remote set-url origin git@github.com:${{ github.repository }}.git
|
|
183
|
+
|
|
184
|
+
- name: Commit Changes
|
|
185
|
+
uses: i2mint/wads/actions/git-commit@master
|
|
186
|
+
with:
|
|
187
|
+
commit-message: "**CI** Formatted code + Updated version to ${{ env.VERSION }} [skip ci]"
|
|
188
|
+
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
189
|
+
push: true
|
|
190
|
+
|
|
191
|
+
- name: Tag Repository
|
|
192
|
+
uses: i2mint/wads/actions/git-tag@master
|
|
193
|
+
with:
|
|
194
|
+
tag: ${{ env.VERSION }}
|
|
195
|
+
message: "Release version ${{ env.VERSION }}"
|
|
196
|
+
push: true
|
|
197
|
+
|
|
198
|
+
# Optional GitHub Pages
|
|
199
|
+
github-pages:
|
|
200
|
+
name: Publish GitHub Pages
|
|
201
|
+
permissions:
|
|
202
|
+
contents: write
|
|
203
|
+
pages: write
|
|
204
|
+
id-token: write
|
|
205
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)"
|
|
206
|
+
needs: publish
|
|
207
|
+
runs-on: ubuntu-latest
|
|
208
|
+
|
|
209
|
+
steps:
|
|
210
|
+
- uses: i2mint/epythet/actions/publish-github-pages@master
|
|
211
|
+
with:
|
|
212
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
213
|
+
ignore: "tests/,scrap/,examples/"
|
opsward-0.0.2/.gitignore
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
wads_configs.json
|
|
2
|
+
data/wads_configs.json
|
|
3
|
+
wads/data/wads_configs.json
|
|
4
|
+
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
.DS_Store
|
|
12
|
+
# C extensions
|
|
13
|
+
*.so
|
|
14
|
+
|
|
15
|
+
# TLS certificates
|
|
16
|
+
## Ignore all PEM files anywhere
|
|
17
|
+
*.pem
|
|
18
|
+
## Also ignore any certs directory
|
|
19
|
+
certs/
|
|
20
|
+
|
|
21
|
+
# Distribution / packaging
|
|
22
|
+
.Python
|
|
23
|
+
build/
|
|
24
|
+
develop-eggs/
|
|
25
|
+
dist/
|
|
26
|
+
downloads/
|
|
27
|
+
eggs/
|
|
28
|
+
.eggs/
|
|
29
|
+
lib/
|
|
30
|
+
lib64/
|
|
31
|
+
parts/
|
|
32
|
+
sdist/
|
|
33
|
+
var/
|
|
34
|
+
wheels/
|
|
35
|
+
*.egg-info/
|
|
36
|
+
.installed.cfg
|
|
37
|
+
*.egg
|
|
38
|
+
MANIFEST
|
|
39
|
+
_build
|
|
40
|
+
|
|
41
|
+
# PyInstaller
|
|
42
|
+
# Usually these files are written by a python script from a template
|
|
43
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
44
|
+
*.manifest
|
|
45
|
+
*.spec
|
|
46
|
+
|
|
47
|
+
# Installer logs
|
|
48
|
+
pip-log.txt
|
|
49
|
+
pip-delete-this-directory.txt
|
|
50
|
+
|
|
51
|
+
# Unit test / coverage reports
|
|
52
|
+
htmlcov/
|
|
53
|
+
.tox/
|
|
54
|
+
.coverage
|
|
55
|
+
.coverage.*
|
|
56
|
+
.cache
|
|
57
|
+
nosetests.xml
|
|
58
|
+
coverage.xml
|
|
59
|
+
*.cover
|
|
60
|
+
.hypothesis/
|
|
61
|
+
.pytest_cache/
|
|
62
|
+
|
|
63
|
+
# Translations
|
|
64
|
+
*.mo
|
|
65
|
+
*.pot
|
|
66
|
+
|
|
67
|
+
# Django stuff:
|
|
68
|
+
*.log
|
|
69
|
+
local_settings.py
|
|
70
|
+
db.sqlite3
|
|
71
|
+
|
|
72
|
+
# Flask stuff:
|
|
73
|
+
instance/
|
|
74
|
+
.webassets-cache
|
|
75
|
+
|
|
76
|
+
# Scrapy stuff:
|
|
77
|
+
.scrapy
|
|
78
|
+
|
|
79
|
+
# Sphinx documentation
|
|
80
|
+
docs/_build/
|
|
81
|
+
docs/*
|
|
82
|
+
|
|
83
|
+
# PyBuilder
|
|
84
|
+
target/
|
|
85
|
+
|
|
86
|
+
# Jupyter Notebook
|
|
87
|
+
.ipynb_checkpoints
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
.python-version
|
|
91
|
+
|
|
92
|
+
# celery beat schedule file
|
|
93
|
+
celerybeat-schedule
|
|
94
|
+
|
|
95
|
+
# SageMath parsed files
|
|
96
|
+
*.sage.py
|
|
97
|
+
|
|
98
|
+
# Environments
|
|
99
|
+
.env
|
|
100
|
+
.venv
|
|
101
|
+
env/
|
|
102
|
+
venv/
|
|
103
|
+
ENV/
|
|
104
|
+
env.bak/
|
|
105
|
+
venv.bak/
|
|
106
|
+
|
|
107
|
+
# Spyder project settings
|
|
108
|
+
.spyderproject
|
|
109
|
+
.spyproject
|
|
110
|
+
|
|
111
|
+
# Rope project settings
|
|
112
|
+
.ropeproject
|
|
113
|
+
|
|
114
|
+
# mkdocs documentation
|
|
115
|
+
/site
|
|
116
|
+
|
|
117
|
+
# mypy
|
|
118
|
+
.mypy_cache/
|
|
119
|
+
|
|
120
|
+
# PyCharm
|
|
121
|
+
.idea
|
opsward-0.0.2/CLAUDE.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Opsward
|
|
2
|
+
|
|
3
|
+
Diagnose, generate, and maintain the AI agent setup of your projects — CLAUDE.md, skills, subagents, rules, and supporting docs.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Opsward is a Python CLI tool (and library) that treats a project's AI agent configuration as a first-class artifact. It scans project roots, reports what's missing or stale, scaffolds missing pieces from templates, and keeps everything current as the codebase evolves. It works on Python projects, JS/TS projects, and mixed repos.
|
|
8
|
+
|
|
9
|
+
There is also an `ow` PyPI package that is a thin re-export shim for `opsward`.
|
|
10
|
+
|
|
11
|
+
## Tech Stack
|
|
12
|
+
|
|
13
|
+
- **Language:** Python 3.10+
|
|
14
|
+
- **CLI:** `argh` for dispatching functions to CLI commands
|
|
15
|
+
- **Templates:** String-based (`string.Template` or simple f-string/jinja2-minimal) — keep deps light
|
|
16
|
+
- **Data structures:** `dataclasses` for models, `Mapping`/`MutableMapping` where storage is involved
|
|
17
|
+
- **File access:** `importlib.resources.files` for bundled templates in `opsward/data/`
|
|
18
|
+
- **Testing:** `pytest`, doctests where practical
|
|
19
|
+
- **No heavy deps:** This is a lightweight diagnostic/scaffolding tool. Avoid pulling in large frameworks.
|
|
20
|
+
|
|
21
|
+
## Documentation
|
|
22
|
+
|
|
23
|
+
For detailed project knowledge, see `misc/docs/docs_guide.md`.
|
|
24
|
+
Read it to discover which docs to consult for your current task.
|
|
25
|
+
|
|
26
|
+
Key docs to read before starting work:
|
|
27
|
+
- `misc/docs/ai_setup_meta_tooling.md` — the foundational research report (what exists, what we're building, why)
|
|
28
|
+
- `misc/docs/architecture.md` — system design, module responsibilities, data flow
|
|
29
|
+
- `misc/docs/conventions.md` — coding style and patterns for this project
|
|
30
|
+
- `misc/docs/target_artifacts.md` — complete spec of what opsward generates for target projects
|
|
31
|
+
|
|
32
|
+
## Module Map
|
|
33
|
+
|
|
34
|
+
- `opsward/` — the main package
|
|
35
|
+
- `__init__.py` — public interface (the facade). Exports `diagnose`, `generate`, `maintain`, key types
|
|
36
|
+
- `base.py` — core data structures: `ProjectType`, `DiagnosisReport`, `DocSpec`, `SetupComponent`, scoring models
|
|
37
|
+
- `scan.py` — read-only filesystem scanning: detect project type, find CLAUDE.md, skills, agents, rules, hooks, docs
|
|
38
|
+
- `score.py` — quality scoring: CLAUDE.md rubric, skill validation, doc freshness, cross-reference checks
|
|
39
|
+
- `generate.py` — template rendering and file generation (never overwrites without confirmation)
|
|
40
|
+
- `maintain.py` — staleness detection, update suggestions, drift analysis
|
|
41
|
+
- `cli.py` — argh-based CLI entry point
|
|
42
|
+
- `util.py` — internal helpers (underscore-prefixed)
|
|
43
|
+
- `data/` — bundled package resources (accessed via `importlib.resources.files`)
|
|
44
|
+
- `templates/` — generation templates organized by target project type
|
|
45
|
+
- `shared/` — templates that work for any project type
|
|
46
|
+
- `python/` — Python-specific templates (paths use `misc/docs/`)
|
|
47
|
+
- `jsts/` — JS/TS-specific templates (paths use `docs/`)
|
|
48
|
+
- `rubrics/` — scoring criteria (YAML or TOML)
|
|
49
|
+
- `skills/` — Claude Code skill templates that opsward installs into target projects
|
|
50
|
+
- `tests/` — pytest tests
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Install
|
|
56
|
+
pip install opsward # or: pip install ow
|
|
57
|
+
|
|
58
|
+
# CLI usage
|
|
59
|
+
python -m opsward diagnose /path/to/project # Diagnose AI setup
|
|
60
|
+
python -m opsward diagnose /path/to/proj1 /path/to/proj2 # Multi-project
|
|
61
|
+
python -m opsward generate /path/to/project # Generate missing pieces
|
|
62
|
+
python -m opsward maintain /path/to/project # Check for staleness/drift
|
|
63
|
+
|
|
64
|
+
# After pip install (via pyproject.toml [project.scripts]):
|
|
65
|
+
opsward diagnose .
|
|
66
|
+
opsward generate . --dry-run
|
|
67
|
+
opsward maintain .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Conventions
|
|
71
|
+
|
|
72
|
+
### Python Style
|
|
73
|
+
|
|
74
|
+
- Functional over OOP. Use dataclasses for data, plain functions for logic.
|
|
75
|
+
- Keyword-only arguments from the 3rd position onward.
|
|
76
|
+
- Small helper functions: inner if single-caller, underscore-prefixed if module-private.
|
|
77
|
+
- Minimal docstrings on everything. Simple doctests when practical.
|
|
78
|
+
- `yield` over `return list`. Generators for sequences.
|
|
79
|
+
- `Mapping`/`MutableMapping` for storage abstractions.
|
|
80
|
+
- Use `importlib.resources.files("opsward.data")` to access bundled templates — never hardcode filesystem paths to package data.
|
|
81
|
+
|
|
82
|
+
### CLI Pattern
|
|
83
|
+
|
|
84
|
+
Follow the argh SSOT dispatch pattern:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# In cli.py
|
|
88
|
+
_dispatch_funcs = [diagnose, generate, maintain]
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
import argh
|
|
92
|
+
argh.dispatch_commands(_dispatch_funcs)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# In __main__.py
|
|
97
|
+
from opsward.cli import _dispatch_funcs
|
|
98
|
+
import argh
|
|
99
|
+
argh.dispatch_commands(_dispatch_funcs)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Template Pattern
|
|
103
|
+
|
|
104
|
+
Templates live in `opsward/data/templates/`. They are plain markdown files with `${variable}` placeholders (using `string.Template`). The generator reads them via `importlib.resources`, substitutes variables from the scan results, and writes to the target project.
|
|
105
|
+
|
|
106
|
+
### Scoring Pattern
|
|
107
|
+
|
|
108
|
+
Scoring functions are pure: `(scan_result) -> score_dict`. No side effects. The rubric definitions live in `opsward/data/rubrics/` as YAML. Scoring dimensions for CLAUDE.md (inspired by community best practices):
|
|
109
|
+
|
|
110
|
+
1. **Commands/Workflows** (0-20): Are build/test/run commands documented?
|
|
111
|
+
2. **Architecture Clarity** (0-20): Is the module map present and accurate?
|
|
112
|
+
3. **Conventions** (0-15): Are non-obvious patterns documented?
|
|
113
|
+
4. **Conciseness** (0-15): Is it dense and scannable, not bloated?
|
|
114
|
+
5. **Currency** (0-15): Do referenced paths/files actually exist?
|
|
115
|
+
6. **Actionability** (0-15): Can an agent act on the instructions without guessing?
|
|
116
|
+
|
|
117
|
+
### Project Type Detection
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Detection signals — check in this order:
|
|
121
|
+
PYTHON_SIGNALS = ("pyproject.toml", "setup.py", "setup.cfg", "Pipfile")
|
|
122
|
+
JSTS_SIGNALS = ("package.json", "tsconfig.json", "deno.json")
|
|
123
|
+
|
|
124
|
+
# Docs location by type:
|
|
125
|
+
# Python -> misc/docs/
|
|
126
|
+
# JS/TS -> docs/
|
|
127
|
+
# Mixed -> docs/ (JS/TS convention wins)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### What Opsward Generates for Target Projects
|
|
131
|
+
|
|
132
|
+
When generating for a target project, opsward can produce:
|
|
133
|
+
|
|
134
|
+
**Documentation layer** (the `docs/` or `misc/docs/` folder):
|
|
135
|
+
- `docs_guide.md` — always first. Entry point listing all other docs.
|
|
136
|
+
- `architecture.md`, `known_issues.md`, `conventions.md`, `roadmap.md`, `glossary.md`, `testing.md`, `dependencies.md`, `deployment.md`
|
|
137
|
+
- `decisions/` folder for MADR-style architectural decision records
|
|
138
|
+
|
|
139
|
+
**AI configuration layer** (the `.claude/` folder):
|
|
140
|
+
- `CLAUDE.md` at project root (or updates to existing one)
|
|
141
|
+
- `.claude/skills/diagnose-setup/SKILL.md` — the diagnostic skill
|
|
142
|
+
- `.claude/skills/maintain-docs/SKILL.md` — the doc maintenance skill
|
|
143
|
+
- `.claude/agents/setup-auditor.md` — read-only auditor subagent
|
|
144
|
+
- `.claude/rules/` files if appropriate
|
|
145
|
+
|
|
146
|
+
See `misc/docs/target_artifacts.md` for the full spec of generated artifacts.
|
|
147
|
+
|
|
148
|
+
### Output Principles
|
|
149
|
+
|
|
150
|
+
- **Never overwrite** existing files without explicit confirmation.
|
|
151
|
+
- **Dry-run by default** for generate/maintain. Use `--write` or `--apply` to actually write.
|
|
152
|
+
- **Diff-first**: Show what would change before changing it.
|
|
153
|
+
- **Minimal generation**: Only suggest docs/skills that are genuinely useful for the detected project type. Don't generate a `deployment.md` template for a library with no deployment.
|
|
154
|
+
|
|
155
|
+
## Non-Obvious Patterns
|
|
156
|
+
|
|
157
|
+
- Templates are **not** Jinja2. Use `string.Template` for simplicity and zero deps. If a template needs conditionals, split it into separate template files per variant.
|
|
158
|
+
- The `diagnose` function returns a `DiagnosisReport` dataclass that is both human-readable (via `__str__`) and machine-consumable (via dataclass fields). The CLI pretty-prints it; library users get structured data.
|
|
159
|
+
- The `scan.py` module must be **purely read-only** — it never modifies the target project. This is a hard invariant. It uses `pathlib.Path` and `os.walk`, never writes.
|
|
160
|
+
- For multi-project scanning, each project gets its own `DiagnosisReport`. There is also an aggregate `MultiProjectReport` that summarizes cross-project patterns.
|
|
161
|
+
- The `ow` shim package is a separate `pyproject.toml` / directory. It literally just does `from opsward import *` and declares `opsward` as a dependency.
|
|
162
|
+
|
|
163
|
+
## Git Workflow
|
|
164
|
+
|
|
165
|
+
- Conventional Commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`
|
|
166
|
+
- No AI attribution in commit messages.
|
|
167
|
+
|
|
168
|
+
## Build & Test
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pip install -e ".[dev]" --break-system-packages
|
|
172
|
+
pytest
|
|
173
|
+
pytest --doctest-modules opsward/
|
|
174
|
+
```
|
opsward-0.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thor Whalen
|
|
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.
|
opsward-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opsward
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Diagnose, generate, and maintain the AI agent setup of your projects — CLAUDE.md, skills, subagents, rules, and supporting docs.
|
|
5
|
+
Project-URL: Homepage, https://github.com/thorwhalen/opsward
|
|
6
|
+
Project-URL: Repository, https://github.com/thorwhalen/opsward
|
|
7
|
+
Project-URL: Documentation, https://thorwhalen.github.io/opsward
|
|
8
|
+
Author: Thor Whalen
|
|
9
|
+
License: mit
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agents,claude,code-quality,developer-tools,documentation
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: argh>=0.31
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
18
|
+
Provides-Extra: docs
|
|
19
|
+
Requires-Dist: sphinx-rtd-theme>=1.0; extra == 'docs'
|
|
20
|
+
Requires-Dist: sphinx>=6.0; extra == 'docs'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# opsward
|
|
24
|
+
|
|
25
|
+
Diagnose, generate, and maintain the AI agent setup of your projects —
|
|
26
|
+
CLAUDE.md, skills, subagents, rules, and supporting docs.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install opsward
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Diagnose
|
|
37
|
+
|
|
38
|
+
Score your project's AI setup health:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
opsward diagnose-cmd .
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Diagnosis Report: myproject
|
|
46
|
+
Project type: python
|
|
47
|
+
Overall score: 72/100 (Grade: C)
|
|
48
|
+
|
|
49
|
+
Components:
|
|
50
|
+
CLAUDE.md quality [################....] 81/100
|
|
51
|
+
Documentation [##############......] 70/100
|
|
52
|
+
Skills [############........] 60/100
|
|
53
|
+
Setup (rules/agents/hooks) [##########..........] 50/100
|
|
54
|
+
Cross-references [####################] 100/100
|
|
55
|
+
|
|
56
|
+
Missing:
|
|
57
|
+
[ ] docs_guide.md
|
|
58
|
+
[ ] docs/known_issues.md
|
|
59
|
+
|
|
60
|
+
Suggestions:
|
|
61
|
+
1. Create a docs_guide.md to index your documentation
|
|
62
|
+
2. Consider adding hooks in .claude/hooks.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Generate
|
|
66
|
+
|
|
67
|
+
Create missing artifacts (dry run by default):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
opsward generate-cmd .
|
|
71
|
+
opsward generate-cmd . --write # actually create files
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Generates CLAUDE.md, docs (architecture, conventions, known_issues, etc.),
|
|
75
|
+
skills (diagnose-setup, maintain-docs), and agents (setup-auditor) —
|
|
76
|
+
only what's missing, never overwrites existing files.
|
|
77
|
+
|
|
78
|
+
### Maintain
|
|
79
|
+
|
|
80
|
+
Find stale references and drift:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
opsward maintain-cmd .
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
myproject: 3 issue(s)
|
|
88
|
+
|
|
89
|
+
[stale_path] CLAUDE.md references `src/old_module.py` but it does not exist
|
|
90
|
+
[sync_issue] `new_doc.md` exists in docs/ but is not listed in docs_guide.md
|
|
91
|
+
[empty_doc] `conventions.md` appears to be an empty stub (12 bytes)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## What It Checks
|
|
95
|
+
|
|
96
|
+
**CLAUDE.md quality** (6 dimensions):
|
|
97
|
+
- Commands & workflows — are build/test/lint commands documented?
|
|
98
|
+
- Architecture clarity — is there a module map with role descriptions?
|
|
99
|
+
- Conventions — are project-specific style rules present?
|
|
100
|
+
- Conciseness — is the file scannable, not bloated?
|
|
101
|
+
- Currency — do referenced paths actually exist?
|
|
102
|
+
- Actionability — are instructions specific enough to act on?
|
|
103
|
+
|
|
104
|
+
**Documentation completeness**: docs_guide.md, architecture.md, conventions.md,
|
|
105
|
+
known_issues.md, and content quality.
|
|
106
|
+
|
|
107
|
+
**Skills & agents**: SKILL.md presence, descriptions, setup-auditor agent.
|
|
108
|
+
|
|
109
|
+
**Cross-references**: paths in CLAUDE.md validated against the filesystem.
|
|
110
|
+
|
|
111
|
+
**Overall health**: weighted score (A–F grade) combining all components.
|
|
112
|
+
|
|
113
|
+
## Output Formats
|
|
114
|
+
|
|
115
|
+
All commands support `--format json` for machine-parseable output:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
opsward diagnose-cmd . --format json
|
|
119
|
+
opsward generate-cmd . --format json
|
|
120
|
+
opsward maintain-cmd . --format json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Python API
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from opsward import scan, diagnose, generate, maintain
|
|
127
|
+
|
|
128
|
+
sr = scan('.')
|
|
129
|
+
report = diagnose(sr)
|
|
130
|
+
print(report) # human-readable report card
|
|
131
|
+
print(report.grade) # 'A', 'B', 'C', 'D', or 'F'
|
|
132
|
+
|
|
133
|
+
files = generate(sr) # list[GeneratedFile]
|
|
134
|
+
issues = maintain(sr) # list[MaintenanceSuggestion]
|
|
135
|
+
```
|