haru-cli 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.
- haru_cli-0.1.0/.github/workflows/ci.yml +120 -0
- haru_cli-0.1.0/.github/workflows/release.yml +105 -0
- haru_cli-0.1.0/.gitignore +218 -0
- haru_cli-0.1.0/.kiro/steering/coding-standards.md +14 -0
- haru_cli-0.1.0/.kiro/steering/git-workflow.md +14 -0
- haru_cli-0.1.0/.kiro/steering/product.md +17 -0
- haru_cli-0.1.0/.kiro/steering/security.md +15 -0
- haru_cli-0.1.0/.kiro/steering/structure.md +13 -0
- haru_cli-0.1.0/.kiro/steering/tech.md +16 -0
- haru_cli-0.1.0/.kiro/steering/testing.md +13 -0
- haru_cli-0.1.0/.pre-commit-config.yaml +32 -0
- haru_cli-0.1.0/.python-version +1 -0
- haru_cli-0.1.0/CHANGELOG.md +37 -0
- haru_cli-0.1.0/CLAUDE.md +45 -0
- haru_cli-0.1.0/LICENSE +202 -0
- haru_cli-0.1.0/PKG-INFO +120 -0
- haru_cli-0.1.0/README.md +92 -0
- haru_cli-0.1.0/config/agents.yaml +30 -0
- haru_cli-0.1.0/config/guardrails.yaml +8 -0
- haru_cli-0.1.0/config/haru.yaml +21 -0
- haru_cli-0.1.0/config/logging.yaml +10 -0
- haru_cli-0.1.0/config/mcp.yaml +13 -0
- haru_cli-0.1.0/config/models.yaml +20 -0
- haru_cli-0.1.0/config/prompts/researcher.md +14 -0
- haru_cli-0.1.0/config/prompts/supervisor.md +15 -0
- haru_cli-0.1.0/config/prompts/writer.md +12 -0
- haru_cli-0.1.0/docs/configuration.md +55 -0
- haru_cli-0.1.0/pyproject.toml +94 -0
- haru_cli-0.1.0/renovate.json +15 -0
- haru_cli-0.1.0/src/haru/__init__.py +1 -0
- haru_cli-0.1.0/src/haru/__main__.py +12 -0
- haru_cli-0.1.0/src/haru/agents/__init__.py +18 -0
- haru_cli-0.1.0/src/haru/agents/factory.py +76 -0
- haru_cli-0.1.0/src/haru/agents/orchestration.py +169 -0
- haru_cli-0.1.0/src/haru/auth/__init__.py +14 -0
- haru_cli-0.1.0/src/haru/auth/cache.py +82 -0
- haru_cli-0.1.0/src/haru/auth/pkce.py +21 -0
- haru_cli-0.1.0/src/haru/auth/session.py +95 -0
- haru_cli-0.1.0/src/haru/auth/sso.py +206 -0
- haru_cli-0.1.0/src/haru/cli.py +20 -0
- haru_cli-0.1.0/src/haru/commands/__init__.py +1 -0
- haru_cli-0.1.0/src/haru/commands/chat.py +102 -0
- haru_cli-0.1.0/src/haru/commands/login.py +34 -0
- haru_cli-0.1.0/src/haru/commands/run.py +60 -0
- haru_cli-0.1.0/src/haru/commands/session.py +36 -0
- haru_cli-0.1.0/src/haru/commands/streaming.py +34 -0
- haru_cli-0.1.0/src/haru/config/__init__.py +6 -0
- haru_cli-0.1.0/src/haru/config/loader.py +163 -0
- haru_cli-0.1.0/src/haru/config/schema.py +261 -0
- haru_cli-0.1.0/src/haru/errors.py +25 -0
- haru_cli-0.1.0/src/haru/models/__init__.py +5 -0
- haru_cli-0.1.0/src/haru/models/bedrock.py +77 -0
- haru_cli-0.1.0/src/haru/observability/__init__.py +6 -0
- haru_cli-0.1.0/src/haru/observability/guardrails.py +36 -0
- haru_cli-0.1.0/src/haru/observability/telemetry.py +34 -0
- haru_cli-0.1.0/src/haru/sessions/__init__.py +5 -0
- haru_cli-0.1.0/src/haru/sessions/manager.py +70 -0
- haru_cli-0.1.0/src/haru/steering/__init__.py +5 -0
- haru_cli-0.1.0/src/haru/steering/prompts.py +42 -0
- haru_cli-0.1.0/src/haru/tools/__init__.py +12 -0
- haru_cli-0.1.0/src/haru/tools/mcp.py +137 -0
- haru_cli-0.1.0/src/haru/tools/registry.py +41 -0
- haru_cli-0.1.0/tests/conftest.py +10 -0
- haru_cli-0.1.0/tests/integration/test_packaging.py +22 -0
- haru_cli-0.1.0/tests/unit/test_agent_factory.py +143 -0
- haru_cli-0.1.0/tests/unit/test_bedrock.py +145 -0
- haru_cli-0.1.0/tests/unit/test_cache.py +107 -0
- haru_cli-0.1.0/tests/unit/test_cli.py +272 -0
- haru_cli-0.1.0/tests/unit/test_config_loader.py +407 -0
- haru_cli-0.1.0/tests/unit/test_guardrails.py +78 -0
- haru_cli-0.1.0/tests/unit/test_main.py +15 -0
- haru_cli-0.1.0/tests/unit/test_mcp.py +212 -0
- haru_cli-0.1.0/tests/unit/test_orchestration.py +219 -0
- haru_cli-0.1.0/tests/unit/test_pkce.py +45 -0
- haru_cli-0.1.0/tests/unit/test_prompts.py +78 -0
- haru_cli-0.1.0/tests/unit/test_session.py +176 -0
- haru_cli-0.1.0/tests/unit/test_sessions.py +148 -0
- haru_cli-0.1.0/tests/unit/test_sso.py +272 -0
- haru_cli-0.1.0/tests/unit/test_telemetry.py +65 -0
- haru_cli-0.1.0/uv.lock +2206 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Python ${{ matrix.python }}
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python: ["3.13", "3.14"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v5
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
python-version: ${{ matrix.python }}
|
|
27
|
+
|
|
28
|
+
- name: Sync dependencies
|
|
29
|
+
run: uv sync --locked
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
run: uv run ruff check
|
|
33
|
+
|
|
34
|
+
- name: Format check
|
|
35
|
+
run: uv run ruff format --check
|
|
36
|
+
|
|
37
|
+
- name: Type check
|
|
38
|
+
run: uv run mypy src
|
|
39
|
+
|
|
40
|
+
- name: Tests (coverage gate 90%)
|
|
41
|
+
run: uv run pytest
|
|
42
|
+
|
|
43
|
+
- name: CLI smoke test
|
|
44
|
+
run: uv run haru --version
|
|
45
|
+
|
|
46
|
+
# Auto-tag on push to main (dcert pattern): determine the next semver patch
|
|
47
|
+
# version from the latest v* tag, bump pyproject.toml + uv.lock with a
|
|
48
|
+
# [skip ci] commit, push the tag, then explicitly dispatch the release
|
|
49
|
+
# workflow at that tag (tags pushed with GITHUB_TOKEN do not trigger
|
|
50
|
+
# workflows on their own; workflow_dispatch is exempt).
|
|
51
|
+
auto-tag:
|
|
52
|
+
name: Auto-tag release
|
|
53
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
54
|
+
needs: [test]
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
permissions:
|
|
57
|
+
contents: write
|
|
58
|
+
actions: write
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
with:
|
|
62
|
+
fetch-depth: 0
|
|
63
|
+
|
|
64
|
+
- name: Install uv
|
|
65
|
+
uses: astral-sh/setup-uv@v5
|
|
66
|
+
|
|
67
|
+
- name: Determine next version
|
|
68
|
+
id: version
|
|
69
|
+
run: |
|
|
70
|
+
LATEST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -1)
|
|
71
|
+
if [ -z "$LATEST_TAG" ]; then
|
|
72
|
+
# First release: use the version already declared in pyproject.toml
|
|
73
|
+
NEXT=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
74
|
+
else
|
|
75
|
+
CURRENT="${LATEST_TAG#v}"
|
|
76
|
+
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
|
77
|
+
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
|
78
|
+
PATCH=$(echo "$CURRENT" | cut -d. -f3 | cut -d- -f1)
|
|
79
|
+
NEXT="${MAJOR}.${MINOR}.$((PATCH + 1))"
|
|
80
|
+
fi
|
|
81
|
+
echo "version=${NEXT}" >> "$GITHUB_OUTPUT"
|
|
82
|
+
echo "tag=v${NEXT}" >> "$GITHUB_OUTPUT"
|
|
83
|
+
echo "Next version: ${NEXT} (tag: v${NEXT})"
|
|
84
|
+
|
|
85
|
+
- name: Check if tag already exists
|
|
86
|
+
id: check_tag
|
|
87
|
+
run: |
|
|
88
|
+
if git rev-parse "refs/tags/${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
|
|
89
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
90
|
+
echo "Tag ${{ steps.version.outputs.tag }} already exists, skipping"
|
|
91
|
+
else
|
|
92
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
- name: Bump version, commit, and tag
|
|
96
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
97
|
+
run: |
|
|
98
|
+
VERSION="${{ steps.version.outputs.version }}"
|
|
99
|
+
TAG="${{ steps.version.outputs.tag }}"
|
|
100
|
+
|
|
101
|
+
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
|
|
102
|
+
uv lock
|
|
103
|
+
|
|
104
|
+
git config user.name "github-actions[bot]"
|
|
105
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
106
|
+
if ! git diff --quiet; then
|
|
107
|
+
git add pyproject.toml uv.lock
|
|
108
|
+
git commit -m "chore: bump version to ${VERSION} [skip ci]"
|
|
109
|
+
git push origin main
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
git tag "$TAG"
|
|
113
|
+
git push origin "$TAG"
|
|
114
|
+
echo "::notice::Created tag $TAG"
|
|
115
|
+
|
|
116
|
+
- name: Dispatch release workflow
|
|
117
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
118
|
+
env:
|
|
119
|
+
GH_TOKEN: ${{ github.token }}
|
|
120
|
+
run: gh workflow run release.yml --ref "${{ steps.version.outputs.tag }}"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build distributions
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
|
|
23
|
+
- name: Build sdist and wheel
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Smoke test built wheel
|
|
27
|
+
run: |
|
|
28
|
+
uv venv smoke-env
|
|
29
|
+
uv pip install --python smoke-env dist/*.whl
|
|
30
|
+
smoke-env/bin/haru --version
|
|
31
|
+
|
|
32
|
+
- name: Upload distributions
|
|
33
|
+
uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
name: Publish to PyPI
|
|
40
|
+
# Trusted Publishing (OIDC) — no tokens or passwords; the trusted publisher
|
|
41
|
+
# is configured in the PyPI project settings for this repo/workflow/environment.
|
|
42
|
+
needs: build
|
|
43
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
environment: pypi
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write
|
|
48
|
+
steps:
|
|
49
|
+
- name: Download distributions
|
|
50
|
+
uses: actions/download-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- name: Publish
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
57
|
+
|
|
58
|
+
github-release:
|
|
59
|
+
name: Create GitHub Release
|
|
60
|
+
needs: [build, publish]
|
|
61
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
permissions:
|
|
64
|
+
contents: write
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
|
|
68
|
+
- name: Download distributions
|
|
69
|
+
uses: actions/download-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: dist
|
|
72
|
+
path: dist/
|
|
73
|
+
|
|
74
|
+
- name: Generate release notes
|
|
75
|
+
run: |
|
|
76
|
+
TAG="${{ github.ref_name }}"
|
|
77
|
+
REPO="${{ github.repository }}"
|
|
78
|
+
cat > release-notes.md <<EOF
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
\`\`\`bash
|
|
82
|
+
pipx install haru-cli==${TAG#v}
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
or \`pip install haru-cli==${TAG#v}\` — published on [PyPI](https://pypi.org/project/haru-cli/${TAG#v}/).
|
|
86
|
+
|
|
87
|
+
See the [CHANGELOG](https://github.com/${REPO}/blob/main/CHANGELOG.md) for details.
|
|
88
|
+
EOF
|
|
89
|
+
|
|
90
|
+
- name: Create or update release
|
|
91
|
+
env:
|
|
92
|
+
GH_TOKEN: ${{ github.token }}
|
|
93
|
+
run: |
|
|
94
|
+
TAG="${{ github.ref_name }}"
|
|
95
|
+
if gh release view "$TAG" > /dev/null 2>&1; then
|
|
96
|
+
gh release edit "$TAG" --notes-file release-notes.md --latest
|
|
97
|
+
gh release upload "$TAG" --clobber dist/*
|
|
98
|
+
else
|
|
99
|
+
gh release create "$TAG" \
|
|
100
|
+
--title "$TAG" \
|
|
101
|
+
--notes-file release-notes.md \
|
|
102
|
+
--generate-notes \
|
|
103
|
+
--latest \
|
|
104
|
+
dist/*
|
|
105
|
+
fi
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Coding standards
|
|
6
|
+
|
|
7
|
+
- Functional first: pure functions, frozen dataclasses, explicit inputs and outputs. Avoid classes unless a library contract requires one.
|
|
8
|
+
- Full type annotations on every function; mypy strict must pass with no ignores except where narrowly justified and commented.
|
|
9
|
+
- No hardcoding: model IDs, regions, ARNs, URLs, ports, and file paths come from YAML config.
|
|
10
|
+
- Configuration is validated into typed schema objects at load time; fail fast with `ConfigError` carrying the offending path.
|
|
11
|
+
- Errors: raise typed exceptions from `errors.py`; never swallow exceptions silently; convert third-party errors at module boundaries.
|
|
12
|
+
- Logging via the standard logging module configured from YAML; structured JSON in production; never log secrets, tokens, or customer data.
|
|
13
|
+
- Docstrings on public functions (PEP 257). Keep functions small and composable.
|
|
14
|
+
- Line length 100. Format with ruff. No dead code.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Git workflow
|
|
6
|
+
|
|
7
|
+
- `main` is protected. Never commit directly to `main`.
|
|
8
|
+
- Every change starts on a new branch off the latest `main`.
|
|
9
|
+
- Branch naming: `feat/<slug>`, `fix/<slug>`, `chore/<slug>`, `docs/<slug>`, `test/<slug>`. Example: `feat/sso-pkce-login`.
|
|
10
|
+
- Commits follow Conventional Commits: `type(scope): summary`, for example `feat(auth): add PKCE authorization code login`.
|
|
11
|
+
- One logical change per commit; keep commits small and reviewable.
|
|
12
|
+
- Open a pull request into `main`; CI (ruff, mypy, pytest with coverage gate) must pass before merge.
|
|
13
|
+
- Rebase on `main` before merge; prefer a linear history. Delete the branch after merge.
|
|
14
|
+
- Each chunk from the specification is its own branch and pull request.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Product
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
haru-cli gives engineers at a regulated asset manager a secure, scriptable command line for interacting with Amazon Bedrock Claude models through governed, observable, multi-agent workflows.
|
|
9
|
+
|
|
10
|
+
## Users
|
|
11
|
+
Internal engineers and API integration teams who authenticate through corporate IAM Identity Center and operate within the firm's AWS security boundary.
|
|
12
|
+
|
|
13
|
+
## Key capabilities
|
|
14
|
+
Interactive and one-shot chat, tool use, MCP client integration, supervisor/swarm/graph orchestration, session persistence, OpenTelemetry tracing, and Bedrock Guardrails.
|
|
15
|
+
|
|
16
|
+
## Non-negotiables
|
|
17
|
+
US data residency by default (us. inference profiles), no secrets in configuration, auditable logs, and enterprise-grade quality gates.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Security
|
|
6
|
+
|
|
7
|
+
- Authentication: IAM Identity Center via OAuth 2.0 Authorization Code flow with PKCE (S256). Register a public client, use a 127.0.0.1 loopback redirect, validate the `state` parameter, and never accept non-loopback redirects.
|
|
8
|
+
- Tokens: cache under `~/.aws/sso/cache` with 0600 permissions in the botocore-compatible schema; rely on botocore's SSOTokenProvider for refresh; treat access and refresh tokens as secrets.
|
|
9
|
+
- Credentials: resolve AWS credentials from the SSO session and the standard credential chain; never store long-lived keys.
|
|
10
|
+
- No secrets in configuration or source. YAML carries only references (`${env:VAR}`, ARNs, profile names). Reject inline secrets at config load.
|
|
11
|
+
- Data residency: default to `us.` Bedrock inference profiles; do not use `global.` endpoints without explicit approval.
|
|
12
|
+
- Guardrails: Bedrock Guardrails enabled by default with input redaction; do not disable to pass tests.
|
|
13
|
+
- Logging: never log tokens, credentials, or customer data; redact prompts where required; keep audit-friendly structured logs.
|
|
14
|
+
- Dependencies: keep boto3 and all dependencies current; enable Renovate/Dependabot; review the ruff security (S) rules.
|
|
15
|
+
- This is a regulated financial services firm: prefer least privilege, fail closed, and make every security-relevant action auditable.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project structure
|
|
6
|
+
|
|
7
|
+
- `src/haru/` holds the package; tests live in `tests/unit` and `tests/integration`.
|
|
8
|
+
- Configuration lives in `config/*.yaml`; prompts in `config/prompts/`.
|
|
9
|
+
- One responsibility per module: auth, config, models, agents, tools, sessions, observability, commands.
|
|
10
|
+
- Commands in `commands/` are thin; business logic lives in the domain modules and is unit tested independently of Click.
|
|
11
|
+
- Naming: modules and functions snake_case; dataclasses PascalCase; constants UPPER_SNAKE_CASE.
|
|
12
|
+
- Imports ordered by ruff isort rules: standard library, third party, first party.
|
|
13
|
+
- Factory functions return Strands objects; they take typed config plus a boto3 session and never read global state.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Technology stack
|
|
6
|
+
|
|
7
|
+
- Language: Python 3.13 (floor), tested on 3.13 and 3.14. PEP 8 and PEP 257.
|
|
8
|
+
- CLI: Click 8.2+.
|
|
9
|
+
- Agents: AWS Strands Agents SDK 1.42+ with the native BedrockModel provider (Converse/ConverseStream, streaming on).
|
|
10
|
+
- AWS: boto3/botocore 1.43+ (always latest), sso-oidc for PKCE login, bedrock-runtime for inference.
|
|
11
|
+
- Packaging: uv with pyproject.toml (hatchling backend) and a committed uv.lock.
|
|
12
|
+
- Quality: ruff (lint and format), mypy strict, pytest with pytest-cov (>=90%).
|
|
13
|
+
- Observability: OpenTelemetry SDK and OTLP exporter via Strands telemetry.
|
|
14
|
+
- Config: PyYAML, validated into typed models.
|
|
15
|
+
|
|
16
|
+
Prefer these tools over alternatives. Do not add a dependency without a clear need; pin lower bounds and rely on uv.lock for reproducibility.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Testing
|
|
6
|
+
|
|
7
|
+
- Framework: pytest. Coverage: pytest-cov with a 90% floor (`--cov-fail-under=90`), branch coverage on.
|
|
8
|
+
- Every module has a matching `tests/unit/test_<module>.py`. New code ships with tests in the same change.
|
|
9
|
+
- Mock all network and AWS calls: use pytest-mock and moto; never hit real Bedrock, sso-oidc, or S3 in unit tests.
|
|
10
|
+
- Test the CLI with `click.testing.CliRunner`, asserting exit codes and output.
|
|
11
|
+
- Cover happy paths, error paths, and edge cases (missing config, expired tokens, disabled MCP servers, guardrail interventions).
|
|
12
|
+
- Deterministic tests only: no sleeps, no real time dependence, no external services. Use fixtures and `tmp_path`.
|
|
13
|
+
- Integration tests live in `tests/integration` and are marked so they can be skipped in fast CI.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: detect-private-key
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.16.0
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff-check
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
21
|
+
rev: 0.11.32
|
|
22
|
+
hooks:
|
|
23
|
+
- id: uv-lock
|
|
24
|
+
|
|
25
|
+
- repo: local
|
|
26
|
+
hooks:
|
|
27
|
+
- id: mypy
|
|
28
|
+
name: mypy (strict)
|
|
29
|
+
entry: uv run mypy src
|
|
30
|
+
language: system
|
|
31
|
+
types: [python]
|
|
32
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,37 @@
|
|
|
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.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-07-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `haru login`: IAM Identity Center sign-in via the OAuth 2.0 authorization-code
|
|
15
|
+
flow with PKCE (S256), loopback-only redirect capture, and a
|
|
16
|
+
botocore-compatible token cache (`~/.aws/sso/cache`, 0600) with automatic
|
|
17
|
+
refresh via the `refresh_token` grant.
|
|
18
|
+
- `haru chat`: interactive streaming REPL with clean Ctrl-C/Ctrl-D handling,
|
|
19
|
+
`--agent` selection, and `--session-id` persistence; `haru run` for one-shot
|
|
20
|
+
prompts; `haru session list` for stored conversations.
|
|
21
|
+
- Typed YAML configuration with includes, `${env:VAR}` interpolation,
|
|
22
|
+
inline-secret rejection, and load-time cross-reference validation.
|
|
23
|
+
- Strands BedrockModel factory with `us.` data-residency defaults, streaming
|
|
24
|
+
on, and Bedrock Guardrails attached (input redaction on; enabled guardrails
|
|
25
|
+
without an id fail closed).
|
|
26
|
+
- Built-in tool allowlist (strands_tools) and MCP clients for stdio and
|
|
27
|
+
streamable-http transports with disabled/continue-on-error handling.
|
|
28
|
+
- Versioned steering prompts under `config/prompts/` with base+overlay
|
|
29
|
+
composition.
|
|
30
|
+
- Multi-agent orchestration: supervisor (agents-as-tools), swarm, and graph
|
|
31
|
+
patterns built from configuration.
|
|
32
|
+
- Session persistence: project-local file backend (default) and S3 backend.
|
|
33
|
+
- OpenTelemetry OTLP tracing via the Strands telemetry helper (no-op when
|
|
34
|
+
disabled).
|
|
35
|
+
- Toolchain: uv packaging, ruff lint/format, mypy strict, pytest with a 90%
|
|
36
|
+
coverage gate, pre-commit hooks, Renovate, GitHub Actions CI (Python
|
|
37
|
+
3.13/3.14), and a PyPI release workflow using Trusted Publishing.
|
haru_cli-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file guides Claude Code when working in this repository. Read it fully before generating or editing code. The steering documents in `.kiro/steering/` are authoritative and expand on the rules here.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
haru-cli is an open, enterprise-grade Python command line interface for a regulated financial services firm. It is a thin, functional orchestration layer over the AWS Strands Agents SDK, talking to Amazon Bedrock Claude models through the Converse API with streaming, tool use, MCP client support, multi-agent orchestration, session persistence, OpenTelemetry observability, and Bedrock Guardrails.
|
|
8
|
+
|
|
9
|
+
## Golden rules
|
|
10
|
+
|
|
11
|
+
1. Latest stable Python (target 3.13, test on 3.13 and 3.14). PEP 8 and PEP 257 compliant.
|
|
12
|
+
2. Functional style. Prefer pure functions and frozen dataclasses. Do not introduce classes unless a third-party contract requires one. No global mutable state.
|
|
13
|
+
3. All configuration lives in YAML under `config/`. No hardcoded values in code. No secrets in YAML: use environment references (`${env:VAR}`) or the AWS credential chain.
|
|
14
|
+
4. Use Click for every command. Package as an installable distribution via pyproject.toml.
|
|
15
|
+
5. Solid unit test coverage with pytest. New code ships with tests. Keep coverage at or above 90%.
|
|
16
|
+
6. Every change goes on a new branch off `main`. Never commit directly to `main`.
|
|
17
|
+
7. Do not weaken security controls, logging, or guardrails to make a test pass.
|
|
18
|
+
|
|
19
|
+
## Toolchain
|
|
20
|
+
|
|
21
|
+
- Packaging and environments: uv. Lint and format: ruff. Types: mypy (strict). Tests: pytest with pytest-cov.
|
|
22
|
+
- Run locally: `uv sync`, `uv run ruff check`, `uv run ruff format`, `uv run mypy src`, `uv run pytest`.
|
|
23
|
+
|
|
24
|
+
## Dependencies
|
|
25
|
+
|
|
26
|
+
- strands-agents (>=1.42.0), boto3/botocore (>=1.43.0, always latest), click (>=8.2.0), pyyaml, pydantic, opentelemetry-sdk, rich.
|
|
27
|
+
- Keep boto3 current with a lower-bound pin and a committed uv.lock refreshed by Renovate.
|
|
28
|
+
|
|
29
|
+
## Architecture summary
|
|
30
|
+
|
|
31
|
+
- `auth/` IAM Identity Center PKCE login and boto3 session construction.
|
|
32
|
+
- `config/` YAML loading and typed schema.
|
|
33
|
+
- `models/` Strands BedrockModel factories.
|
|
34
|
+
- `agents/` agent factories and multi-agent orchestration.
|
|
35
|
+
- `tools/` built-in tool registry and MCP clients.
|
|
36
|
+
- `sessions/` file and S3 session managers.
|
|
37
|
+
- `observability/` OpenTelemetry and Bedrock Guardrails.
|
|
38
|
+
- `commands/` Click command implementations.
|
|
39
|
+
|
|
40
|
+
## What not to do
|
|
41
|
+
|
|
42
|
+
- Do not reimplement the agent loop, model transport, or tool execution; delegate to Strands.
|
|
43
|
+
- Do not hardcode model IDs, regions, ARNs, URLs, or ports; read them from YAML.
|
|
44
|
+
- Do not log secrets, tokens, or full prompts containing customer data.
|
|
45
|
+
- Do not consider a chunk done until ruff, mypy, and pytest all pass.
|