diff --git a/.claude/settings.json b/.claude/settings.json index 2d8365d..c9879d3 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -4,4 +4,4 @@ "code-review@claude-plugins-official": true, "pr-review-toolkit@claude-plugins-official": true } -} \ No newline at end of file +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7dc7701..794959c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,4 +6,4 @@ ## File must end with CODEOWNERS file -.github/CODEOWNERS @aws-samples/coding-agents-admin \ No newline at end of file +.github/CODEOWNERS @aws-samples/coding-agents-admin diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 6b8951a..a58498a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -34,7 +34,7 @@ body: id: use-case attributes: label: Use case - description: Why do you need this? For example: "I'm always frustrated when..." + description: "Why do you need this? For example: I'm always frustrated when..." validations: required: true - type: textarea diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 03a266e..f97d030 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -3,4 +3,4 @@ If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. > [!IMPORTANT] -> Please do not create a GitHub issue, pull request, or other public announcements. \ No newline at end of file +> Please do not create a GitHub issue, pull request, or other public announcements. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 29f0c2e..c655d2d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -22,4 +22,4 @@ Tip: [AGENTS.md](https://github.com/aws-samples/sample-autonomous-cloud-coding-a #### Acknowledgment -By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the [project license](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/blob/main/LICENSE). \ No newline at end of file +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the [project license](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/blob/main/LICENSE). diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3ff3438 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,78 @@ +# Git hooks via https://github.com/j178/prek (installed by `mise run install` in a Git checkout; re-run `mise run hooks:install` after edits). +# Config format matches pre-commit; run hooks with `prek` from mise (`mise.toml` [tools]). + +default_install_hook_types: [pre-commit, pre-push] +fail_fast: false +exclude: ^\.threat-composer/ + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + # Skip generated trees and paths often checked in read-only (0444); mutating hooks must not touch them. + exclude: (cdk/cdk\.out/|node_modules/|^docs/src/content/docs/.*\.md$|(^|/)LICENSE$|(^|/)\.gitattributes$|(^|/)\.npmignore$|(^|/)\.gitignore$|^cli/header\.js$|^docs/astro\.config\.mjs$|^docs/tsconfig\.json$|^docs/src/content\.config\.ts$|\.(snap|lock)$) + - id: end-of-file-fixer + exclude: (cdk/cdk\.out/|node_modules/|^docs/src/content/docs/.*\.md$|(^|/)LICENSE$|(^|/)\.gitattributes$|(^|/)\.npmignore$|(^|/)\.gitignore$|^cli/header\.js$|^docs/astro\.config\.mjs$|^docs/tsconfig\.json$|^docs/src/content\.config\.ts$) + - id: check-merge-conflict + - id: check-yaml + exclude: ^(cdk/cdk\.out/|cdk\.out/|node_modules/|agent/\.venv/) + - id: check-json + exclude: ^(cdk/cdk\.out/|node_modules/) + + - repo: local + hooks: + - id: gitleaks-staged + name: gitleaks (staged) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run security:secrets:staged' + language: system + pass_filenames: false + stages: [pre-commit] + + - id: cdk-eslint + name: eslint (cdk) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && export MISE_EXPERIMENTAL=1 && mise //cdk:eslint' + language: system + pass_filenames: false + files: ^cdk/.*\.(ts|tsx|cjs|mjs|js)$ + exclude: ^cdk/cdk\.out/ + stages: [pre-commit] + + - id: cli-eslint + name: eslint (cli) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && export MISE_EXPERIMENTAL=1 && mise //cli:eslint' + language: system + pass_filenames: false + files: ^cli/.*\.(ts|tsx|cjs|mjs|js)$ + stages: [pre-commit] + + - id: agent-quality + name: quality (agent) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)/agent" && mise run quality' + language: system + pass_filenames: false + files: ^agent/.*\.py$ + stages: [pre-commit] + + - id: docs-astro-check + name: astro check (docs) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)/docs" && ./node_modules/.bin/astro check' + language: system + pass_filenames: false + files: ^docs/ + exclude: ^docs/node_modules/ + stages: [pre-commit] + + - id: monorepo-security-pre-push + name: security scans (pre-push) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run hooks:pre-push:security' + language: system + pass_filenames: false + stages: [pre-push] + + - id: monorepo-tests-pre-push + name: package tests (pre-push) + entry: bash -lc 'cd "$(git rev-parse --show-toplevel)" && mise run hooks:pre-push:tests' + language: system + pass_filenames: false + stages: [pre-push] diff --git a/.threat-composer/20260331-1834/components/applicationInfo.tc.json b/.threat-composer/20260331-1834/components/applicationInfo.tc.json index 6e15fc3..7316712 100644 --- a/.threat-composer/20260331-1834/components/applicationInfo.tc.json +++ b/.threat-composer/20260331-1834/components/applicationInfo.tc.json @@ -92,4 +92,4 @@ "threats": [], "mitigations": [], "mitigationLinks": [] -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/components/architectureDescription.tc.json b/.threat-composer/20260331-1834/components/architectureDescription.tc.json index a3768b1..c85c4be 100644 --- a/.threat-composer/20260331-1834/components/architectureDescription.tc.json +++ b/.threat-composer/20260331-1834/components/architectureDescription.tc.json @@ -123,4 +123,4 @@ "threats": [], "mitigations": [], "mitigationLinks": [] -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/components/dataflowDescription.tc.json b/.threat-composer/20260331-1834/components/dataflowDescription.tc.json index 91c82a7..23f0482 100644 --- a/.threat-composer/20260331-1834/components/dataflowDescription.tc.json +++ b/.threat-composer/20260331-1834/components/dataflowDescription.tc.json @@ -123,4 +123,4 @@ "threats": [], "mitigations": [], "mitigationLinks": [] -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/components/mitigations.tc.json b/.threat-composer/20260331-1834/components/mitigations.tc.json index f545c15..2e7f1e0 100644 --- a/.threat-composer/20260331-1834/components/mitigations.tc.json +++ b/.threat-composer/20260331-1834/components/mitigations.tc.json @@ -450,4 +450,4 @@ "linkedId": "30b38afc-9930-4602-8637-56ddb166d2a5" } ] -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/components/threats.tc.json b/.threat-composer/20260331-1834/components/threats.tc.json index 81002bd..a87d03f 100644 --- a/.threat-composer/20260331-1834/components/threats.tc.json +++ b/.threat-composer/20260331-1834/components/threats.tc.json @@ -741,4 +741,4 @@ ], "mitigations": [], "mitigationLinks": [] -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/config/config.json b/.threat-composer/20260331-1834/config/config.json index c0c5b23..4d298ae 100644 --- a/.threat-composer/20260331-1834/config/config.json +++ b/.threat-composer/20260331-1834/config/config.json @@ -18,4 +18,4 @@ "show_tool_use": true, "agent_auto_context": true } -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/config/environment-vars.json b/.threat-composer/20260331-1834/config/environment-vars.json index c138373..0affec2 100644 --- a/.threat-composer/20260331-1834/config/environment-vars.json +++ b/.threat-composer/20260331-1834/config/environment-vars.json @@ -14,4 +14,4 @@ "THREAT_COMPOSER_AI_GENERATED_TAG": null }, "note": "Only non-sensitive environment variables are logged. Credentials and secrets are never exported." -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/config/run-metadata.json b/.threat-composer/20260331-1834/config/run-metadata.json index ba0d18b..d489f40 100644 --- a/.threat-composer/20260331-1834/config/run-metadata.json +++ b/.threat-composer/20260331-1834/config/run-metadata.json @@ -34,4 +34,4 @@ "output_tokens": 26992, "total_tokens": 1254413 } -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/applicationInfo.tc.json.hash b/.threat-composer/20260331-1834/hashes/applicationInfo.tc.json.hash index 20f7cd1..64dd2ae 100644 --- a/.threat-composer/20260331-1834/hashes/applicationInfo.tc.json.hash +++ b/.threat-composer/20260331-1834/hashes/applicationInfo.tc.json.hash @@ -1,4 +1,4 @@ { "hash": "sha256:8e04446db0fa8d5b2691dac847b5f154dd190b6ef6d9cebbba72f2bf46baf02d", "timestamp": "2026-03-31T18:36:37.928352Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/architectureDescription.tc.json.hash b/.threat-composer/20260331-1834/hashes/architectureDescription.tc.json.hash index 9ff55d7..59248d8 100644 --- a/.threat-composer/20260331-1834/hashes/architectureDescription.tc.json.hash +++ b/.threat-composer/20260331-1834/hashes/architectureDescription.tc.json.hash @@ -1,4 +1,4 @@ { "hash": "sha256:b771f3e8d9a92e49a20ccc5d927ce42c87a520a65bfbb5bdbd7f0580b310fe34", "timestamp": "2026-03-31T18:39:40.535180Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/architectureDiagram.svg.hash b/.threat-composer/20260331-1834/hashes/architectureDiagram.svg.hash index 54372a6..77bf538 100644 --- a/.threat-composer/20260331-1834/hashes/architectureDiagram.svg.hash +++ b/.threat-composer/20260331-1834/hashes/architectureDiagram.svg.hash @@ -1,4 +1,4 @@ { "hash": "sha256:83fe0f76a33b1d0a4f1d0b4077e444f184873432c996fa947f654b9c7b037aeb", "timestamp": "2026-03-31T18:40:29.327880Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/dataflowDescription.tc.json.hash b/.threat-composer/20260331-1834/hashes/dataflowDescription.tc.json.hash index 97d651b..6bfea61 100644 --- a/.threat-composer/20260331-1834/hashes/dataflowDescription.tc.json.hash +++ b/.threat-composer/20260331-1834/hashes/dataflowDescription.tc.json.hash @@ -1,4 +1,4 @@ { "hash": "sha256:4c8d99ef15fbfa1ebad8fcff8de40d48899924259f5b663fefd242d80b1aa939", "timestamp": "2026-03-31T18:42:33.090247Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/dataflowDiagram.svg.hash b/.threat-composer/20260331-1834/hashes/dataflowDiagram.svg.hash index 5f74e6f..a44f80a 100644 --- a/.threat-composer/20260331-1834/hashes/dataflowDiagram.svg.hash +++ b/.threat-composer/20260331-1834/hashes/dataflowDiagram.svg.hash @@ -1,4 +1,4 @@ { "hash": "sha256:7ad487991f9f3486f5cc86a4919f22b2c6d9e3b9947ade2756c2baac6e023401", "timestamp": "2026-03-31T18:43:01.992971Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/mitigations.tc.json.hash b/.threat-composer/20260331-1834/hashes/mitigations.tc.json.hash index faf81ae..b247dde 100644 --- a/.threat-composer/20260331-1834/hashes/mitigations.tc.json.hash +++ b/.threat-composer/20260331-1834/hashes/mitigations.tc.json.hash @@ -1,4 +1,4 @@ { "hash": "sha256:d8861d31dbabf23236dd0b86b2eb8cf27f567c60a2f31076750249f1c1fdc5d6", "timestamp": "2026-03-31T18:48:46.207662Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/hashes/threats.tc.json.hash b/.threat-composer/20260331-1834/hashes/threats.tc.json.hash index 11a4d43..4de67b4 100644 --- a/.threat-composer/20260331-1834/hashes/threats.tc.json.hash +++ b/.threat-composer/20260331-1834/hashes/threats.tc.json.hash @@ -1,4 +1,4 @@ { "hash": "sha256:4a3e44367ba33cefafd2fe22c44b2db73abd9bb839746014dd1f64bd57c4061e", "timestamp": "2026-03-31T18:45:20.201511Z" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/session_20260331-1834/multi_agents/multi_agent_default_graph/multi_agent.json b/.threat-composer/20260331-1834/session_20260331-1834/multi_agents/multi_agent_default_graph/multi_agent.json index fb2319a..d87888f 100644 --- a/.threat-composer/20260331-1834/session_20260331-1834/multi_agents/multi_agent_default_graph/multi_agent.json +++ b/.threat-composer/20260331-1834/session_20260331-1834/multi_agents/multi_agent_default_graph/multi_agent.json @@ -259,4 +259,4 @@ "activated": false } } -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/session_20260331-1834/session.json b/.threat-composer/20260331-1834/session_20260331-1834/session.json index b64140f..6692da9 100644 --- a/.threat-composer/20260331-1834/session_20260331-1834/session.json +++ b/.threat-composer/20260331-1834/session_20260331-1834/session.json @@ -3,4 +3,4 @@ "session_type": "AGENT", "created_at": "2026-03-31T18:34:16.261021+00:00", "updated_at": "2026-03-31T18:34:16.261028+00:00" -} \ No newline at end of file +} diff --git a/.threat-composer/20260331-1834/threatmodel.tc.json b/.threat-composer/20260331-1834/threatmodel.tc.json index e1d8e7f..57627b1 100644 --- a/.threat-composer/20260331-1834/threatmodel.tc.json +++ b/.threat-composer/20260331-1834/threatmodel.tc.json @@ -1990,4 +1990,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/AGENTS.md b/AGENTS.md index a622d13..89b1a99 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,6 +42,7 @@ Handler entry tests: `cdk/test/handlers/orchestrate-task.test.ts`, `create-task. - Running raw **`jest`/`tsc`/`cdk`** from muscle memory — prefer **`mise //cdk:test`**, **`mise //cdk:compile`**, **`mise //cdk:synth`** (see [Commands you can use](#commands-you-can-use)). - **`MISE_EXPERIMENTAL=1`** — required for namespaced tasks like **`mise //cdk:build`** (see [CONTRIBUTING.md](./CONTRIBUTING.md)). - **`mise run build`** runs **`//agent:quality`** before CDK — the deployed image bundles **`agent/`**; agent changes belong in that tree. +- **`prek install`** fails if Git **`core.hooksPath`** is set — another hook manager owns hooks; see [CONTRIBUTING.md](./CONTRIBUTING.md). ### Tech stack @@ -105,6 +106,8 @@ Run `mise tasks --all` (with `MISE_EXPERIMENTAL=1`) for the full list. Common co - **`mise run security:deps`** — OSV Scanner on **`yarn.lock`** (all JS workspaces) and **`agent/uv.lock`**. - **`mise run security`** — Runs **`security:secrets`** then **`security:sast`**. - **`mise run security:retire`** — Retire.js on CDK, CLI, and docs packages. +- **`mise run hooks:install`** — Re-install **[prek](https://github.com/j178/prek)** git hooks (also run automatically at the end of **`mise run install`** inside a Git checkout). See [CONTRIBUTING.md](./CONTRIBUTING.md) if `core.hooksPath` blocks install. +- **`mise run hooks:run`** — Run the same **pre-commit** and **pre-push** hook stages on all files (local verification). Use these instead of running `tsc`, `jest`, or `cdk` directly when possible, so the project's scripts and config stay consistent. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index abfab6c..5c5edd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,33 @@ This repository uses [mise](https://mise.jdx.dev/) for tool versions and tasks. Project configuration is hand-owned in this repository. Prefer `mise` tasks from the repo root (`mise run install`, `mise run build`) or package-level tasks (`mise //cdk:build`, `mise //cli:build`, `mise //docs:build`). +### Git hooks ([prek](https://github.com/j178/prek)) + +**`mise run install`** already runs **`prek install --prepare-hooks`** when the current directory is inside a **Git** working tree (it is skipped if there is no `.git`, e.g. a source tarball). [`prek`](https://github.com/j178/prek) is pinned in the root **`mise.toml`** and reads **`.pre-commit-config.yaml`**. + +Re-apply hook shims after you change hook config or if install was skipped: + +```bash +mise run hooks:install +``` + +| Stage | What runs | +|-------|-----------| +| **pre-commit** | Trailing whitespace / EOF / merge-conflict / YAML+JSON checks; **gitleaks** on **staged** changes only; **eslint** (cdk, cli), **ruff** (agent), **astro check** (docs) when matching paths are touched. | +| **pre-push** | Two pre-push hooks run in order: +1. **`mise run hooks:pre-push:security`** — root security scans. +2. **`mise run hooks:pre-push:tests`** — tests in `cdk`, `cli`, and `agent` packages. + +For convenience, **`mise run hooks:pre-push`** runs both steps sequentially. | + +Dry-run or reproduce locally without committing: + +```bash +mise run hooks:run +``` + +If **`prek install`** exits with *refusing to install hooks with `core.hooksPath` set* — another tool owns your hooks. Either unset it (`git config --unset-all core.hooksPath` for **local** and/or **global**) or integrate these checks into that hook manager instead. + ### Step 1: Open Issue If there isn't one already, open an issue describing what you intend to contribute. It's useful to communicate in advance, because sometimes, someone is already working in this space, so maybe it's worth collaborating with them instead of duplicating the efforts. diff --git a/README.md b/README.md index ed997e2..4e80c7f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Autonomous Background Coding Agents on AWS - +

diff --git a/agent/.python_version b/agent/.python_version index 3a4f41e..24ee5b1 100644 --- a/agent/.python_version +++ b/agent/.python_version @@ -1 +1 @@ -3.13 \ No newline at end of file +3.13 diff --git a/cdk/header.js b/cdk/header.js index 447a486..dab76e1 100644 --- a/cdk/header.js +++ b/cdk/header.js @@ -15,4 +15,4 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. - */ \ No newline at end of file + */ diff --git a/docs/README.md b/docs/README.md index b3fa7dd..23279d6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1 +1 @@ -# replace this \ No newline at end of file +# replace this diff --git a/docs/design/API_CONTRACT.md b/docs/design/API_CONTRACT.md index 41bb83a..2946981 100644 --- a/docs/design/API_CONTRACT.md +++ b/docs/design/API_CONTRACT.md @@ -661,4 +661,3 @@ The API is implemented as an **Amazon API Gateway REST API** (or HTTP API) with ### Relationship to internal message schema The API request/response schemas defined here are the **external** contract. The input gateway normalizes API requests into the **internal message schema** (see [INPUT_GATEWAY.md](./INPUT_GATEWAY.md)) before dispatching to the task pipeline. The internal schema may include additional fields (e.g. `channel_metadata`, `normalized_at`) that are not exposed in the API. - diff --git a/docs/design/ARCHITECTURE.md b/docs/design/ARCHITECTURE.md index 7db666c..fabba88 100644 --- a/docs/design/ARCHITECTURE.md +++ b/docs/design/ARCHITECTURE.md @@ -211,4 +211,4 @@ Different tasks and repos may benefit from different models. The `model_id` fiel - **Implementation tasks (`new_task`):** Claude Sonnet 4 (good balance of quality and cost) - **PR iteration tasks (`pr_iteration`):** Claude Sonnet 4 (needs to understand review feedback and make code changes — similar complexity to implementation) - **PR review tasks (`pr_review`):** Claude Haiku (fast, cheap — review is read-only analysis) -- **Complex/critical repos:** Claude Opus 4 (highest quality, highest cost — opt-in per repo) \ No newline at end of file +- **Complex/critical repos:** Claude Opus 4 (highest quality, highest cost — opt-in per repo) diff --git a/docs/design/INPUT_GATEWAY.md b/docs/design/INPUT_GATEWAY.md index f1e37e8..b279ffc 100644 --- a/docs/design/INPUT_GATEWAY.md +++ b/docs/design/INPUT_GATEWAY.md @@ -28,39 +28,39 @@ In short: **every input channel connects through this central point; the gateway ### Inbound (requests from users) -- **Single entry point** +- **Single entry point** All channels must go through the gateway. No channel should talk directly to task storage or orchestration. -- **Channel-specific authentication and verification** +- **Channel-specific authentication and verification** Each channel has its own way to prove legitimacy (Cognito JWT, Slack signing secret, webhook secrets, etc.). The gateway (or per-channel adapters) must verify every request before processing. -- **Normalization to an internal message schema** +- **Normalization to an internal message schema** Every channel-specific payload must be transformed into the same internal message structure. The rest of the system only ever sees this normalized form. -- **Validation** +- **Validation** The gateway must validate normalized messages (required fields, types, allowed actions, target repo/issue refs, size limits) and reject malformed or invalid requests with clear errors. -- **Access control** +- **Access control** The gateway enforces who can do what (e.g. only the task owner can cancel; only authenticated users can create tasks). This may be defined per channel or globally. -- **Support for multiple action types** +- **Support for multiple action types** At minimum: create task, get task(s), cancel task. If the product supports human-in-the-loop, add approve/reject and possibly free-form message. The internal schema must represent these distinctly. -- **Multi-modal input** +- **Multi-modal input** Users can send text and, where the channel allows it, images or other attachments. The internal message format must carry these in a channel-agnostic way (e.g. type + URL or inline data). -- **Channel metadata preservation** +- **Channel metadata preservation** Enough channel-specific metadata (e.g. Slack channel + thread, CLI request id) must be stored with the task so that outbound notifications can be delivered to the right place. ### Outbound (notifications to users) -- **Single internal notification format** +- **Single internal notification format** The core emits one canonical structure for all outbound events (e.g. status change, task completed, error, approval requested). Channel adapters consume only this. -- **Channel-specific rendering and delivery** +- **Channel-specific rendering and delivery** Each channel gets a renderer that turns the internal notification into the right format (Slack blocks, CLI text, email HTML, etc.) and sends it using the channel’s API or protocol. -- **Routing and preferences** +- **Routing and preferences** The system must know where to send notifications (e.g. only to the channel the task was created from, or to multiple channels per user preferences). Routing rules are part of the gateway/notification design, not of the core task logic. ### User channel preferences (future) @@ -90,22 +90,22 @@ MVP can use **implicit routing**: send notifications only to the channel the tas The gateway defines a single **internal message** format that all channels produce. The rest of the system (task creation, orchestration) depends only on this. The following is a conceptual schema, not an implementation spec. -- **Message identity** +- **Message identity** A unique id (e.g. ULID) for deduplication and tracing. - **Channel source** Which channel the message came from (e.g. `api`, `webhook`, `slack`, `web`, `github_actions`). -- **Channel metadata** +- **Channel metadata** Opaque or structured data needed to route replies (e.g. Slack channel id, thread ts; CLI session or request id). Stored with the task for outbound. -- **User identity** +- **User identity** A stable platform user id (e.g. Cognito sub or mapped id). All channels must map to this so authorization and “my tasks” work consistently. -- **Action type** +- **Action type** One of: create task, get task(s), get one task, cancel task, approve/reject (if HITL), or other defined actions. -- **Payload** +- **Payload** Action-specific data, for example: - **Create task:** user message text, repo URL or org/repo, issue/PR ref (e.g. issue number), optional attachments (images, files) with type and URL or inline data. - **Cancel / approve:** task id, and for approve: approval decision and optional response text. @@ -118,16 +118,16 @@ Validation rules (e.g. required fields per action, max message length, allowed U When the core needs to notify the user, it produces a single **internal notification** format. Channel adapters turn this into Slack messages, CLI output, emails, etc. -- **Notification identity** +- **Notification identity** Unique id for the notification. -- **Task and user** +- **Task and user** Task id and user id so adapters can route and filter. -- **Notification type** +- **Notification type** E.g. status change, task completed, error, approval requested, log or progress update. -- **Payload** +- **Payload** Type-specific content: status value, short message, approval question, PR URL, error message, log snippet, etc. Adapters are responsible for rendering this into channel-specific formats (e.g. Slack Block Kit, plain text for CLI) and delivering via the channel’s API or protocol. @@ -140,35 +140,35 @@ Adapters are responsible for rendering this into channel-specific formats (e.g. - User runs: `bgagent submit --repo org/myapp --issue 42` (and optionally adds a message or attachment). - The CLI sends an HTTP request to the gateway with Cognito JWT. -- **Gateway (inbound):** - - Verifies the JWT. - - Normalizes the request into the internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from args, channel_source = cli, user_id from JWT. - - Validates required fields and allowed values. +- **Gateway (inbound):** + - Verifies the JWT. + - Normalizes the request into the internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from args, channel_source = cli, user_id from JWT. + - Validates required fields and allowed values. - Dispatches to the task pipeline (create task). - The task pipeline creates the task and starts orchestration. Later, when status changes or the task completes, the core emits internal notifications. -- **Gateway (outbound):** +- **Gateway (outbound):** In MVP with CLI-only, “outbound” may be implicit (e.g. user polls `GET /tasks/{id}` and sees status in the response). When push is added, an adapter could stream or push these notifications to the CLI (e.g. over WebSocket or SSE). ### Example 2: User checks status from the CLI - User runs: `bgagent status abc-123`. - CLI sends `GET /tasks/abc-123` with Cognito JWT. -- **Gateway:** +- **Gateway:** Verifies JWT, normalizes to internal “get one task” action with task_id = abc-123 and user_id from JWT, validates, dispatches. The handler loads the task, enforces that the user owns it, and returns status (and optionally PR URL, error message, etc.) in a consistent response format. The CLI renders that as text. ### Example 3: User cancels a task - User runs: `bgagent cancel abc-123`. -- **Gateway:** +- **Gateway:** Verifies JWT, normalizes to “cancel task” with task_id and user_id, validates ownership (or delegates to a downstream service), dispatches. The task pipeline marks the task cancelled and stops the agent run. Outbound notifications (if any) can inform the user that the task was cancelled. ### Example 4: Future — User submits a task from Slack - User sends: “Implement the feature from issue #42 in org/myapp” in a Slack channel (or via a slash command). - Slack sends an HTTP POST to the gateway (e.g. `/channels/slack/events`) with its own signing and payload. -- **Gateway (inbound):** +- **Gateway (inbound):** Verifies Slack signing secret, normalizes to the same internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from Slack text, channel_source = slack, channel_metadata = { channel_id, thread_ts }, user_id = mapped from Slack user (e.g. via a Slack→Cognito or Slack→platform-user mapping). Validates and dispatches. -- **Gateway (outbound):** +- **Gateway (outbound):** When the task completes or needs approval, the core emits an internal notification. The Slack adapter renders it (e.g. Block Kit with “Task completed” and PR link, or approval buttons), and sends it to the right channel/thread using the stored channel_metadata. ### Example 5: Same user, different channels diff --git a/docs/guides/DEVELOPER_GUIDE.md b/docs/guides/DEVELOPER_GUIDE.md index 08fc5a7..c4f32a9 100644 --- a/docs/guides/DEVELOPER_GUIDE.md +++ b/docs/guides/DEVELOPER_GUIDE.md @@ -80,10 +80,10 @@ cd sample-autonomous-cloud-coding-agents - [AWS CLI](https://aws.amazon.com/cli/): configure your credentials ``` -aws configure --profile [your-profile] +aws configure --profile [your-profile] AWS Access Key ID [None]: xxxxxx AWS Secret Access Key [None]:yyyyyyyyyy -Default region name [None]: us-east-1 +Default region name [None]: us-east-1 Default output format [None]: json ``` @@ -471,4 +471,4 @@ cdk/test/ ├── repo-config.test.ts ├── response.test.ts └── validation.test.ts -``` \ No newline at end of file +``` diff --git a/docs/src/content/docs/design/Api-contract.md b/docs/src/content/docs/design/Api-contract.md index 1586441..528823d 100644 --- a/docs/src/content/docs/design/Api-contract.md +++ b/docs/src/content/docs/design/Api-contract.md @@ -665,4 +665,3 @@ The API is implemented as an **Amazon API Gateway REST API** (or HTTP API) with ### Relationship to internal message schema The API request/response schemas defined here are the **external** contract. The input gateway normalizes API requests into the **internal message schema** (see [INPUT_GATEWAY.md](/design/input-gateway)) before dispatching to the task pipeline. The internal schema may include additional fields (e.g. `channel_metadata`, `normalized_at`) that are not exposed in the API. - diff --git a/docs/src/content/docs/design/Architecture.md b/docs/src/content/docs/design/Architecture.md index 9504b89..3d3de1a 100644 --- a/docs/src/content/docs/design/Architecture.md +++ b/docs/src/content/docs/design/Architecture.md @@ -215,4 +215,4 @@ Different tasks and repos may benefit from different models. The `model_id` fiel - **Implementation tasks (`new_task`):** Claude Sonnet 4 (good balance of quality and cost) - **PR iteration tasks (`pr_iteration`):** Claude Sonnet 4 (needs to understand review feedback and make code changes — similar complexity to implementation) - **PR review tasks (`pr_review`):** Claude Haiku (fast, cheap — review is read-only analysis) -- **Complex/critical repos:** Claude Opus 4 (highest quality, highest cost — opt-in per repo) \ No newline at end of file +- **Complex/critical repos:** Claude Opus 4 (highest quality, highest cost — opt-in per repo) diff --git a/docs/src/content/docs/design/Input-gateway.md b/docs/src/content/docs/design/Input-gateway.md index 2f1b3cd..db89e53 100644 --- a/docs/src/content/docs/design/Input-gateway.md +++ b/docs/src/content/docs/design/Input-gateway.md @@ -32,39 +32,39 @@ In short: **every input channel connects through this central point; the gateway ### Inbound (requests from users) -- **Single entry point** +- **Single entry point** All channels must go through the gateway. No channel should talk directly to task storage or orchestration. -- **Channel-specific authentication and verification** +- **Channel-specific authentication and verification** Each channel has its own way to prove legitimacy (Cognito JWT, Slack signing secret, webhook secrets, etc.). The gateway (or per-channel adapters) must verify every request before processing. -- **Normalization to an internal message schema** +- **Normalization to an internal message schema** Every channel-specific payload must be transformed into the same internal message structure. The rest of the system only ever sees this normalized form. -- **Validation** +- **Validation** The gateway must validate normalized messages (required fields, types, allowed actions, target repo/issue refs, size limits) and reject malformed or invalid requests with clear errors. -- **Access control** +- **Access control** The gateway enforces who can do what (e.g. only the task owner can cancel; only authenticated users can create tasks). This may be defined per channel or globally. -- **Support for multiple action types** +- **Support for multiple action types** At minimum: create task, get task(s), cancel task. If the product supports human-in-the-loop, add approve/reject and possibly free-form message. The internal schema must represent these distinctly. -- **Multi-modal input** +- **Multi-modal input** Users can send text and, where the channel allows it, images or other attachments. The internal message format must carry these in a channel-agnostic way (e.g. type + URL or inline data). -- **Channel metadata preservation** +- **Channel metadata preservation** Enough channel-specific metadata (e.g. Slack channel + thread, CLI request id) must be stored with the task so that outbound notifications can be delivered to the right place. ### Outbound (notifications to users) -- **Single internal notification format** +- **Single internal notification format** The core emits one canonical structure for all outbound events (e.g. status change, task completed, error, approval requested). Channel adapters consume only this. -- **Channel-specific rendering and delivery** +- **Channel-specific rendering and delivery** Each channel gets a renderer that turns the internal notification into the right format (Slack blocks, CLI text, email HTML, etc.) and sends it using the channel’s API or protocol. -- **Routing and preferences** +- **Routing and preferences** The system must know where to send notifications (e.g. only to the channel the task was created from, or to multiple channels per user preferences). Routing rules are part of the gateway/notification design, not of the core task logic. ### User channel preferences (future) @@ -94,22 +94,22 @@ MVP can use **implicit routing**: send notifications only to the channel the tas The gateway defines a single **internal message** format that all channels produce. The rest of the system (task creation, orchestration) depends only on this. The following is a conceptual schema, not an implementation spec. -- **Message identity** +- **Message identity** A unique id (e.g. ULID) for deduplication and tracing. - **Channel source** Which channel the message came from (e.g. `api`, `webhook`, `slack`, `web`, `github_actions`). -- **Channel metadata** +- **Channel metadata** Opaque or structured data needed to route replies (e.g. Slack channel id, thread ts; CLI session or request id). Stored with the task for outbound. -- **User identity** +- **User identity** A stable platform user id (e.g. Cognito sub or mapped id). All channels must map to this so authorization and “my tasks” work consistently. -- **Action type** +- **Action type** One of: create task, get task(s), get one task, cancel task, approve/reject (if HITL), or other defined actions. -- **Payload** +- **Payload** Action-specific data, for example: - **Create task:** user message text, repo URL or org/repo, issue/PR ref (e.g. issue number), optional attachments (images, files) with type and URL or inline data. - **Cancel / approve:** task id, and for approve: approval decision and optional response text. @@ -122,16 +122,16 @@ Validation rules (e.g. required fields per action, max message length, allowed U When the core needs to notify the user, it produces a single **internal notification** format. Channel adapters turn this into Slack messages, CLI output, emails, etc. -- **Notification identity** +- **Notification identity** Unique id for the notification. -- **Task and user** +- **Task and user** Task id and user id so adapters can route and filter. -- **Notification type** +- **Notification type** E.g. status change, task completed, error, approval requested, log or progress update. -- **Payload** +- **Payload** Type-specific content: status value, short message, approval question, PR URL, error message, log snippet, etc. Adapters are responsible for rendering this into channel-specific formats (e.g. Slack Block Kit, plain text for CLI) and delivering via the channel’s API or protocol. @@ -144,35 +144,35 @@ Adapters are responsible for rendering this into channel-specific formats (e.g. - User runs: `bgagent submit --repo org/myapp --issue 42` (and optionally adds a message or attachment). - The CLI sends an HTTP request to the gateway with Cognito JWT. -- **Gateway (inbound):** - - Verifies the JWT. - - Normalizes the request into the internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from args, channel_source = cli, user_id from JWT. - - Validates required fields and allowed values. +- **Gateway (inbound):** + - Verifies the JWT. + - Normalizes the request into the internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from args, channel_source = cli, user_id from JWT. + - Validates required fields and allowed values. - Dispatches to the task pipeline (create task). - The task pipeline creates the task and starts orchestration. Later, when status changes or the task completes, the core emits internal notifications. -- **Gateway (outbound):** +- **Gateway (outbound):** In MVP with CLI-only, “outbound” may be implicit (e.g. user polls `GET /tasks/{id}` and sees status in the response). When push is added, an adapter could stream or push these notifications to the CLI (e.g. over WebSocket or SSE). ### Example 2: User checks status from the CLI - User runs: `bgagent status abc-123`. - CLI sends `GET /tasks/abc-123` with Cognito JWT. -- **Gateway:** +- **Gateway:** Verifies JWT, normalizes to internal “get one task” action with task_id = abc-123 and user_id from JWT, validates, dispatches. The handler loads the task, enforces that the user owns it, and returns status (and optionally PR URL, error message, etc.) in a consistent response format. The CLI renders that as text. ### Example 3: User cancels a task - User runs: `bgagent cancel abc-123`. -- **Gateway:** +- **Gateway:** Verifies JWT, normalizes to “cancel task” with task_id and user_id, validates ownership (or delegates to a downstream service), dispatches. The task pipeline marks the task cancelled and stops the agent run. Outbound notifications (if any) can inform the user that the task was cancelled. ### Example 4: Future — User submits a task from Slack - User sends: “Implement the feature from issue #42 in org/myapp” in a Slack channel (or via a slash command). - Slack sends an HTTP POST to the gateway (e.g. `/channels/slack/events`) with its own signing and payload. -- **Gateway (inbound):** +- **Gateway (inbound):** Verifies Slack signing secret, normalizes to the same internal message: action = create task, repo = org/myapp, issue_ref = 42, user message from Slack text, channel_source = slack, channel_metadata = { channel_id, thread_ts }, user_id = mapped from Slack user (e.g. via a Slack→Cognito or Slack→platform-user mapping). Validates and dispatches. -- **Gateway (outbound):** +- **Gateway (outbound):** When the task completes or needs approval, the core emits an internal notification. The Slack adapter renders it (e.g. Block Kit with “Task completed” and PR link, or approval buttons), and sends it to the right channel/thread using the stored channel_metadata. ### Example 5: Same user, different channels diff --git a/docs/src/content/docs/developer-guide/Contributing.md b/docs/src/content/docs/developer-guide/Contributing.md index 21ab021..3a2323d 100644 --- a/docs/src/content/docs/developer-guide/Contributing.md +++ b/docs/src/content/docs/developer-guide/Contributing.md @@ -54,6 +54,33 @@ This repository uses [mise](https://mise.jdx.dev/) for tool versions and tasks. Project configuration is hand-owned in this repository. Prefer `mise` tasks from the repo root (`mise run install`, `mise run build`) or package-level tasks (`mise //cdk:build`, `mise //cli:build`, `mise //docs:build`). +### Git hooks ([prek](https://github.com/j178/prek)) + +**`mise run install`** already runs **`prek install --prepare-hooks`** when the current directory is inside a **Git** working tree (it is skipped if there is no `.git`, e.g. a source tarball). [`prek`](https://github.com/j178/prek) is pinned in the root **`mise.toml`** and reads **`.pre-commit-config.yaml`**. + +Re-apply hook shims after you change hook config or if install was skipped: + +```bash +mise run hooks:install +``` + +| Stage | What runs | +|-------|-----------| +| **pre-commit** | Trailing whitespace / EOF / merge-conflict / YAML+JSON checks; **gitleaks** on **staged** changes only; **eslint** (cdk, cli), **ruff** (agent), **astro check** (docs) when matching paths are touched. | +| **pre-push** | Two pre-push hooks run in order: +1. **`mise run hooks:pre-push:security`** — root security scans. +2. **`mise run hooks:pre-push:tests`** — tests in `cdk`, `cli`, and `agent` packages. + +For convenience, **`mise run hooks:pre-push`** runs both steps sequentially. | + +Dry-run or reproduce locally without committing: + +```bash +mise run hooks:run +``` + +If **`prek install`** exits with *refusing to install hooks with `core.hooksPath` set* — another tool owns your hooks. Either unset it (`git config --unset-all core.hooksPath` for **local** and/or **global**) or integrate these checks into that hook manager instead. + ### Step 1: Open Issue If there isn't one already, open an issue describing what you intend to contribute. It's useful to communicate in advance, because sometimes, someone is already working in this space, so maybe it's worth collaborating with them instead of duplicating the efforts. diff --git a/docs/src/content/docs/developer-guide/Installation.md b/docs/src/content/docs/developer-guide/Installation.md index 65e9950..3ee8646 100644 --- a/docs/src/content/docs/developer-guide/Installation.md +++ b/docs/src/content/docs/developer-guide/Installation.md @@ -15,10 +15,10 @@ cd sample-autonomous-cloud-coding-agents - [AWS CLI](https://aws.amazon.com/cli/): configure your credentials ``` -aws configure --profile [your-profile] +aws configure --profile [your-profile] AWS Access Key ID [None]: xxxxxx AWS Secret Access Key [None]:yyyyyyyyyy -Default region name [None]: us-east-1 +Default region name [None]: us-east-1 Default output format [None]: json ``` diff --git a/docs/src/content/docs/index.md b/docs/src/content/docs/index.md index cf481bc..d39dd01 100644 --- a/docs/src/content/docs/index.md +++ b/docs/src/content/docs/index.md @@ -37,4 +37,3 @@ Each task follows a **blueprint** — a hybrid workflow that mixes deterministic 3. **Pre-flight** — fail-closed readiness checks verify GitHub API reachability and repository access before consuming compute. Doomed tasks fail fast with a clear reason (`GITHUB_UNREACHABLE`, `REPO_NOT_FOUND_OR_NO_ACCESS`) instead of burning runtime. 4. **Agent execution** — the agent runs in an isolated MicroVM with persistent session storage for select caches: clones the repo, creates a branch, edits code, commits, runs tests and lint. The orchestrator polls for completion without blocking compute. 5. **Finalization** — the orchestrator infers the result (PR created or not), runs optional validation (lint, tests), extracts learnings into memory, and updates task status. - diff --git a/mise.toml b/mise.toml index c42b88f..088c750 100644 --- a/mise.toml +++ b/mise.toml @@ -9,6 +9,7 @@ experimental = true [tools] node = "22" +prek = "0.3.8" gitleaks = "latest" semgrep = "latest" osv-scanner = "latest" @@ -24,10 +25,12 @@ config_roots = ["cdk", "agent", "cli", "docs"] # One `yarn install` at the repo root installs every Yarn workspace (`cdk`, `cli`, `docs`). # `//cdk:install`, `//cli:install`, and `//docs:install` are thin wrappers around the same command. [tasks.install] -description = "Yarn workspaces (cdk, cli, docs) + agent Python (uv)" +description = "Yarn workspaces (cdk, cli, docs) + agent Python (uv) + prek git hooks when inside a Git repo" run = [ "yarn install --check-files", "cd agent && mise run install", + # Install prek shims only in a real checkout (skip tarballs / no-.git environments). + "bash -c 'git rev-parse --git-dir >/dev/null 2>&1 || exit 0; prek install --prepare-hooks'", ] ################## @@ -51,6 +54,10 @@ run = [ description = "Scan for secrets with gitleaks" run = "gitleaks detect --source . --no-banner" +[tasks."security:secrets:staged"] +description = "gitleaks on staged changes (pre-commit hook)" +run = "gitleaks protect --staged --no-banner" + [tasks."security:sast"] description = "SAST scan with semgrep (auto + OWASP top 10)" run = "semgrep scan --config auto --config p/python --config p/typescript --config p/owasp-top-ten --config p/security-audit --error --quiet ." @@ -81,6 +88,40 @@ run = [ "MISE_EXPERIMENTAL=1 mise //agent:security", ] +################## +##### HOOKS ###### +################## + +[tasks."hooks:install"] +description = "Install or refresh prek git hooks (also runs at end of `mise run install` in a Git repo)" +run = "prek install --prepare-hooks" + +[tasks."hooks:run"] +description = "Run prek on all files (pre-commit then pre-push stages)" +run = [ + "prek run --all-files --stage pre-commit", + "prek run --all-files --stage pre-push", +] + +[tasks."hooks:pre-push:security"] +description = "Pre-push security scans" +run = "mise run security" + +[tasks."hooks:pre-push:tests"] +description = "Pre-push tests in cdk/cli/agent" +run = [ + "MISE_EXPERIMENTAL=1 mise //cdk:test", + "MISE_EXPERIMENTAL=1 mise //cli:test", + "cd agent && mise run test", +] + +[tasks."hooks:pre-push"] +description = "Pre-push gate: security scans then tests in cdk/cli/agent" +run = [ + "mise run hooks:pre-push:security", + "mise run hooks:pre-push:tests", +] + ################## ##### BUILD ##### ################## @@ -98,4 +139,4 @@ run = [ [tasks.default] description = "Install + build" -depends = [":install", ":build"] \ No newline at end of file +depends = [":install", ":build"]