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.
Files changed (80) hide show
  1. haru_cli-0.1.0/.github/workflows/ci.yml +120 -0
  2. haru_cli-0.1.0/.github/workflows/release.yml +105 -0
  3. haru_cli-0.1.0/.gitignore +218 -0
  4. haru_cli-0.1.0/.kiro/steering/coding-standards.md +14 -0
  5. haru_cli-0.1.0/.kiro/steering/git-workflow.md +14 -0
  6. haru_cli-0.1.0/.kiro/steering/product.md +17 -0
  7. haru_cli-0.1.0/.kiro/steering/security.md +15 -0
  8. haru_cli-0.1.0/.kiro/steering/structure.md +13 -0
  9. haru_cli-0.1.0/.kiro/steering/tech.md +16 -0
  10. haru_cli-0.1.0/.kiro/steering/testing.md +13 -0
  11. haru_cli-0.1.0/.pre-commit-config.yaml +32 -0
  12. haru_cli-0.1.0/.python-version +1 -0
  13. haru_cli-0.1.0/CHANGELOG.md +37 -0
  14. haru_cli-0.1.0/CLAUDE.md +45 -0
  15. haru_cli-0.1.0/LICENSE +202 -0
  16. haru_cli-0.1.0/PKG-INFO +120 -0
  17. haru_cli-0.1.0/README.md +92 -0
  18. haru_cli-0.1.0/config/agents.yaml +30 -0
  19. haru_cli-0.1.0/config/guardrails.yaml +8 -0
  20. haru_cli-0.1.0/config/haru.yaml +21 -0
  21. haru_cli-0.1.0/config/logging.yaml +10 -0
  22. haru_cli-0.1.0/config/mcp.yaml +13 -0
  23. haru_cli-0.1.0/config/models.yaml +20 -0
  24. haru_cli-0.1.0/config/prompts/researcher.md +14 -0
  25. haru_cli-0.1.0/config/prompts/supervisor.md +15 -0
  26. haru_cli-0.1.0/config/prompts/writer.md +12 -0
  27. haru_cli-0.1.0/docs/configuration.md +55 -0
  28. haru_cli-0.1.0/pyproject.toml +94 -0
  29. haru_cli-0.1.0/renovate.json +15 -0
  30. haru_cli-0.1.0/src/haru/__init__.py +1 -0
  31. haru_cli-0.1.0/src/haru/__main__.py +12 -0
  32. haru_cli-0.1.0/src/haru/agents/__init__.py +18 -0
  33. haru_cli-0.1.0/src/haru/agents/factory.py +76 -0
  34. haru_cli-0.1.0/src/haru/agents/orchestration.py +169 -0
  35. haru_cli-0.1.0/src/haru/auth/__init__.py +14 -0
  36. haru_cli-0.1.0/src/haru/auth/cache.py +82 -0
  37. haru_cli-0.1.0/src/haru/auth/pkce.py +21 -0
  38. haru_cli-0.1.0/src/haru/auth/session.py +95 -0
  39. haru_cli-0.1.0/src/haru/auth/sso.py +206 -0
  40. haru_cli-0.1.0/src/haru/cli.py +20 -0
  41. haru_cli-0.1.0/src/haru/commands/__init__.py +1 -0
  42. haru_cli-0.1.0/src/haru/commands/chat.py +102 -0
  43. haru_cli-0.1.0/src/haru/commands/login.py +34 -0
  44. haru_cli-0.1.0/src/haru/commands/run.py +60 -0
  45. haru_cli-0.1.0/src/haru/commands/session.py +36 -0
  46. haru_cli-0.1.0/src/haru/commands/streaming.py +34 -0
  47. haru_cli-0.1.0/src/haru/config/__init__.py +6 -0
  48. haru_cli-0.1.0/src/haru/config/loader.py +163 -0
  49. haru_cli-0.1.0/src/haru/config/schema.py +261 -0
  50. haru_cli-0.1.0/src/haru/errors.py +25 -0
  51. haru_cli-0.1.0/src/haru/models/__init__.py +5 -0
  52. haru_cli-0.1.0/src/haru/models/bedrock.py +77 -0
  53. haru_cli-0.1.0/src/haru/observability/__init__.py +6 -0
  54. haru_cli-0.1.0/src/haru/observability/guardrails.py +36 -0
  55. haru_cli-0.1.0/src/haru/observability/telemetry.py +34 -0
  56. haru_cli-0.1.0/src/haru/sessions/__init__.py +5 -0
  57. haru_cli-0.1.0/src/haru/sessions/manager.py +70 -0
  58. haru_cli-0.1.0/src/haru/steering/__init__.py +5 -0
  59. haru_cli-0.1.0/src/haru/steering/prompts.py +42 -0
  60. haru_cli-0.1.0/src/haru/tools/__init__.py +12 -0
  61. haru_cli-0.1.0/src/haru/tools/mcp.py +137 -0
  62. haru_cli-0.1.0/src/haru/tools/registry.py +41 -0
  63. haru_cli-0.1.0/tests/conftest.py +10 -0
  64. haru_cli-0.1.0/tests/integration/test_packaging.py +22 -0
  65. haru_cli-0.1.0/tests/unit/test_agent_factory.py +143 -0
  66. haru_cli-0.1.0/tests/unit/test_bedrock.py +145 -0
  67. haru_cli-0.1.0/tests/unit/test_cache.py +107 -0
  68. haru_cli-0.1.0/tests/unit/test_cli.py +272 -0
  69. haru_cli-0.1.0/tests/unit/test_config_loader.py +407 -0
  70. haru_cli-0.1.0/tests/unit/test_guardrails.py +78 -0
  71. haru_cli-0.1.0/tests/unit/test_main.py +15 -0
  72. haru_cli-0.1.0/tests/unit/test_mcp.py +212 -0
  73. haru_cli-0.1.0/tests/unit/test_orchestration.py +219 -0
  74. haru_cli-0.1.0/tests/unit/test_pkce.py +45 -0
  75. haru_cli-0.1.0/tests/unit/test_prompts.py +78 -0
  76. haru_cli-0.1.0/tests/unit/test_session.py +176 -0
  77. haru_cli-0.1.0/tests/unit/test_sessions.py +148 -0
  78. haru_cli-0.1.0/tests/unit/test_sso.py +272 -0
  79. haru_cli-0.1.0/tests/unit/test_telemetry.py +65 -0
  80. 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.
@@ -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.