diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..8e9f201435 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owner for everything +* @KooshaPari diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..d599fd4d66 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,11 @@ +github: [] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: "npm/[email protected]" +community_bridge: # Replace with a single Community Bridge project slug-id +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project slug-e.g. +custom: # Replace with up to 3 custom sponsorship URLs e.g. ['https://example.com/donate'] diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index f4862d1039..bd1b308f21 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -37,15 +37,15 @@ jobs: contents: read steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Install SQLite run: sudo apt-get install -y libsqlite3-dev - name: Setup Protobuf Compiler - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Rust Toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 with: toolchain: nightly components: clippy, rustfmt diff --git a/.github/workflows/bounty.yml b/.github/workflows/bounty.yml index e41e7c79e3..190e47edb2 100644 --- a/.github/workflows/bounty.yml +++ b/.github/workflows/bounty.yml @@ -44,7 +44,7 @@ jobs: issues: write steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Install npm packages run: npm install - name: Sync all bounty labels @@ -57,7 +57,7 @@ jobs: pull-requests: write steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Install npm packages run: npm install - name: Sync bounty labels diff --git a/.github/workflows/cargo-deny.yml b/.github/workflows/cargo-deny.yml new file mode 100644 index 0000000000..cdf80e4d87 --- /dev/null +++ b/.github/workflows/cargo-deny.yml @@ -0,0 +1,19 @@ +name: Cargo Deny +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + cargo-deny: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + - uses: taiki-e/upload-rust-binary-action@f0d45ae91ee7b8ee928de7a9d04d893a08bcbec6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tool: cargo-deny + - name: Check + run: cargo deny check --log-level error diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 778df922e8..74c237c2e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,13 +41,13 @@ jobs: contents: read steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Protobuf Compiler - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Rust Toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 with: toolchain: stable - name: Install cargo-llvm-cov @@ -61,13 +61,13 @@ jobs: contents: read steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Protobuf Compiler - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Rust Toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 with: toolchain: stable - name: Run performance benchmark @@ -86,10 +86,10 @@ jobs: crate_release_id: ${{ steps.set_output.outputs.crate_release_id }} steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - id: create_release name: Draft Release - uses: release-drafter/release-drafter@v7 + uses: release-drafter/release-drafter@563bf132657a13ded0b01fcb723c5a58cdd824e2 with: config-name: release-drafter.yml env: @@ -106,7 +106,7 @@ jobs: crate_release_id: ${{ steps.set_output.outputs.crate_release_id }} steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - id: set_output name: Set Release Version run: echo "crate_release_name=pr-build-${{ github.event.number }}" >> $GITHUB_OUTPUT && echo "crate_release_id=pr-build-${{ github.event.number }}" >> $GITHUB_OUTPUT @@ -169,15 +169,15 @@ jobs: target: aarch64-linux-android steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Protobuf Compiler if: ${{ matrix.cross == 'false' }} - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Cross Toolchain if: ${{ matrix.cross == 'false' }} - uses: taiki-e/setup-cross-toolchain-action@v1 + uses: taiki-e/setup-cross-toolchain-action@74847e552ab5bf79fa4393ed975e297ea57d53fa with: target: ${{ matrix.target }} - name: Add Rust target @@ -187,7 +187,7 @@ jobs: if: '!(contains(matrix.target, ''-unknown-linux-'') || contains(matrix.target, ''-android''))' run: echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV - name: Build Binary - uses: ClementTsang/cargo-action@v0.0.7 + uses: ClementTsang/cargo-action@2438cc5f3ba4e971289fffca2a00dedea6911f14 with: command: build --release args: '--target ${{ matrix.target }}' @@ -200,7 +200,7 @@ jobs: - name: Copy Binary run: cp ${{ matrix.binary_path }} ${{ matrix.binary_name }} - name: Upload to Release - uses: xresloader/upload-to-github-release@v1 + uses: xresloader/upload-to-github-release@7497a58a53ca2f4450d41ca19fabb22de5c0ed0b with: release_id: ${{ needs.draft_release.outputs.crate_release_id }} file: ${{ matrix.binary_name }} @@ -264,15 +264,15 @@ jobs: target: aarch64-linux-android steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Protobuf Compiler if: ${{ matrix.cross == 'false' }} - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Cross Toolchain if: ${{ matrix.cross == 'false' }} - uses: taiki-e/setup-cross-toolchain-action@v1 + uses: taiki-e/setup-cross-toolchain-action@74847e552ab5bf79fa4393ed975e297ea57d53fa with: target: ${{ matrix.target }} - name: Add Rust target @@ -282,7 +282,7 @@ jobs: if: '!(contains(matrix.target, ''-unknown-linux-'') || contains(matrix.target, ''-android''))' run: echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV - name: Build Binary - uses: ClementTsang/cargo-action@v0.0.7 + uses: ClementTsang/cargo-action@2438cc5f3ba4e971289fffca2a00dedea6911f14 with: command: build --release args: '--target ${{ matrix.target }}' diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 6c8f168130..dc468911c1 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -30,7 +30,7 @@ jobs: issues: write steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Sync labels run: |- npx github-label-sync \ diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 53cddb0648..541defba62 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -40,13 +40,13 @@ jobs: steps: - name: Auto Labeler if: github.event_name == 'pull_request_target' - uses: release-drafter/release-drafter/autolabeler@v7 + uses: release-drafter/release-drafter/autolabeler@563bf132657a13ded0b01fcb723c5a58cdd824e2 with: config-name: release-drafter.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Release Drafter - uses: release-drafter/release-drafter@v7 + uses: release-drafter/release-drafter@563bf132657a13ded0b01fcb723c5a58cdd824e2 with: config-name: release-drafter.yml env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01574fb031..1907ab8a5b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,15 +80,15 @@ jobs: target: aarch64-linux-android steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Protobuf Compiler if: ${{ matrix.cross == 'false' }} - uses: arduino/setup-protoc@v3 + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Cross Toolchain if: ${{ matrix.cross == 'false' }} - uses: taiki-e/setup-cross-toolchain-action@v1 + uses: taiki-e/setup-cross-toolchain-action@74847e552ab5bf79fa4393ed975e297ea57d53fa with: target: ${{ matrix.target }} - name: Add Rust target @@ -98,7 +98,7 @@ jobs: if: '!(contains(matrix.target, ''-unknown-linux-'') || contains(matrix.target, ''-android''))' run: echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV - name: Build Binary - uses: ClementTsang/cargo-action@v0.0.7 + uses: ClementTsang/cargo-action@2438cc5f3ba4e971289fffca2a00dedea6911f14 with: command: build --release args: '--target ${{ matrix.target }}' @@ -111,7 +111,7 @@ jobs: - name: Copy Binary run: cp ${{ matrix.binary_path }} ${{ matrix.binary_name }} - name: Upload to Release - uses: xresloader/upload-to-github-release@v1 + uses: xresloader/upload-to-github-release@7497a58a53ca2f4450d41ca19fabb22de5c0ed0b with: release_id: ${{ github.event.release.id }} file: ${{ matrix.binary_name }} @@ -128,7 +128,7 @@ jobs: - antinomyhq/npm-forgecode steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: repository: ${{ matrix.repository }} ref: main @@ -146,7 +146,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: repository: antinomyhq/homebrew-code-forge ref: main diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2d795379e0..fda3287e7c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Mark Stale Issues - uses: actions/stale@v10 + uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f with: stale-issue-label: 'state: inactive' stale-pr-label: 'state: inactive' diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml new file mode 100644 index 0000000000..2b440b2f78 --- /dev/null +++ b/.github/workflows/trufflehog.yml @@ -0,0 +1,17 @@ +name: Trufflehog Secrets Scan +on: + push: + branches: [main] + pull_request: + +jobs: + trufflehog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + fetch-depth: 0 + - uses: trufflehog/actions/setup@main + - run: trufflehog github --only-verified --no-update + env: + GH_TOKEN: \${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..0f5121659c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + +## [0.1.0] - YYYY-MM-DD + +### Added +- Initial release diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..cadf755f10 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,133 @@ +# forgecode — CLAUDE.md + +> **Fork of [tailcallhq/forgecode](https://github.com/tailcallhq/forgecode).** +> Phenotype-org additions: `deny.toml` + `cargo-deny.yml` CI bootstrapped 2026-05-01. + +--- + +This repo is a **fork** of the upstream [tailcallhq/forgecode](https://github.com/tailcallhq/forgecode) +project — an AI-enhanced terminal development environment with ZSH plugin support, +TUI, and multi-provider LLM integration. + +Do not rewrite upstream content. Any changes to upstream-origin files must be +clearly annotated as Phenotype-org-specific additions. + +## Project Overview + +| Field | Value | +|-------|-------| +| Workspace | Multi-crate (21 internal crates under `crates/`) | +| Edition | 2024 | +| Rust version | 1.92 | +| License | MIT | +| Upstream | | + +## Phenotype-Org Additions + +The following files are Phenotype-org-specific additions (not present in upstream): + +- `deny.toml` — cargo-deny configuration +- `cargo-deny.yml` — GitHub Actions CI workflow for dependency auditing + +All other files follow upstream conventions. + +## Stack + +| Layer | Technology | +|-------|------------| +| Runtime | tokio (full, rt-multi-thread, macros, sync, fs, process, signal) | +| HTTP client | reqwest (rustls, hickory-dns, http2) | +| Auth | aws-config, aws-sdk-bedrockruntime, google-cloud-auth | +| CLI | clap 4.6 + clap_complete | +| TUI | reedline 0.47, rustyline 18, termimad, console | +| Serialization | serde, serde_json, serde_yml, toml_edit | +| Diff/patch | dissimilar, similar, strip-ansi-escapes | +| Search | grep-searcher, fzf-wrapped, ignore | +| MCP | rmcp (client + SSE + subprocess + streamable-http transports) | +| Observability | tracing, tracing-subscriber, posthog-rs | +| Git | gix | +| Misc | anyhow, thiserror, uuid, chrono, url, is_ci | + +## Key Commands + +```bash +# Build (from repo root) +cargo build --release + +# Test +cargo test --workspace + +# Format +cargo fmt --check + +# Lint +cargo clippy --workspace --all-targets -- -D warnings + +# Full quality gate +cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace +``` + +## Crate Map + +``` +crates/ +├── forge_main # Binary entry point +├── forge_app # Application layer +├── forge_domain # Domain types & logic +├── forge_infra # Infrastructure / adapters +├── forge_api # API layer +├── forge_embed # Embedded resources +├── forge_ci # CI utilities +├── forge_display # Display / TUI rendering +├── forge_fs # Filesystem operations +├── forge_repo # Git repository integration +├── forge_services # Service layer +├── forge_snaps # Snapshot testing (insta) +├── forge_spinner # Spinner / progress UI +├── forge_stream # Streaming utilities +├── forge_template # Template rendering (handlebars) +├── forge_tool_macros # Proc-macro helpers +├── forge_tracker # Telemetry / tracking +├── forge_walker # Directory traversal +├── forge_json_repair # JSON repair +├── forge_select # Interactive selection (fzf) +├── forge_test_kit # Test utilities +├── forge_markdown_stream # Markdown streaming +├── forge_config # Configuration handling +├── forge_eventsource # Event source +└── forge_eventsource_stream # Event source streaming +``` + +## Quality Gates + +- `cargo fmt --check` — formatting must pass +- `cargo clippy --workspace --all-targets -- -D warnings` — zero lints allowed +- `cargo test --workspace` — all tests must pass +- `cargo deny check` — dependency audit (configured in `deny.toml`) +- Snapshot tests via `insta` — review snapshots with `cargo insta review` + +## CI / GitHub Actions + +- `cargo-deny.yml` runs `cargo deny check advisories licenses` on every PR +- `deny.toml` defines allowlist rules for crates and licenses +- Run `cargo deny check` locally before opening PRs + +## Git Workflow + +``` +origin = KooshaPari/forgecode (Phenotype-org fork) +upstream = tailcallhq/forgecode (canonical upstream) +``` + +Sync from upstream: +```bash +git fetch upstream +git checkout main +git merge upstream/main +git push origin main +``` + +## Security & Compliance + +- `deny.toml` + `cargo-deny.yml` enforce dependency audit (advisories + licenses) +- `cargo deny check` must pass before merging diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..afbec8b8b2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing + +Contributions are welcome! Please follow these guidelines: + +## Development Setup + +1. Fork the repository +2. Clone your fork: `git clone https://github.com//.git` +3. Install dependencies +4. Run tests: follow the repo's test suite + +## Code Style + +Follow the project's formatting and linting rules. Run `cargo fmt` for Rust projects, or the appropriate linter for your stack. + +## Submitting Changes + +1. Create a feature branch +2. Make your changes +3. Add tests if applicable +4. Submit a pull request + +## Questions + +Open an issue for questions or discussions. diff --git a/Cargo.lock b/Cargo.lock index 3ed658eebc..b1f3dac87d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2219,7 +2219,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "forge_api" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-trait", @@ -2238,7 +2238,7 @@ dependencies = [ [[package]] name = "forge_app" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-recursion", @@ -2290,7 +2290,7 @@ dependencies = [ [[package]] name = "forge_ci" -version = "0.1.0" +version = "0.1.1" dependencies = [ "derive_setters", "gh-workflow", @@ -2301,7 +2301,7 @@ dependencies = [ [[package]] name = "forge_config" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "config", @@ -2324,7 +2324,7 @@ dependencies = [ [[package]] name = "forge_display" -version = "0.1.0" +version = "0.1.1" dependencies = [ "console", "derive_setters", @@ -2341,7 +2341,7 @@ dependencies = [ [[package]] name = "forge_domain" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-trait", @@ -2383,7 +2383,7 @@ dependencies = [ [[package]] name = "forge_embed" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "handlebars", @@ -2392,7 +2392,7 @@ dependencies = [ [[package]] name = "forge_eventsource" -version = "0.1.0" +version = "0.1.1" dependencies = [ "forge_eventsource_stream", "futures", @@ -2411,7 +2411,7 @@ dependencies = [ [[package]] name = "forge_eventsource_stream" -version = "0.1.0" +version = "0.1.1" dependencies = [ "futures", "futures-core", @@ -2425,7 +2425,7 @@ dependencies = [ [[package]] name = "forge_fs" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "bstr", @@ -2441,7 +2441,7 @@ dependencies = [ [[package]] name = "forge_infra" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-trait", @@ -2492,7 +2492,7 @@ dependencies = [ [[package]] name = "forge_json_repair" -version = "0.1.0" +version = "0.1.1" dependencies = [ "pretty_assertions", "regex", @@ -2505,7 +2505,7 @@ dependencies = [ [[package]] name = "forge_main" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "arboard", @@ -2569,7 +2569,7 @@ dependencies = [ [[package]] name = "forge_markdown_stream" -version = "0.1.0" +version = "0.1.1" dependencies = [ "colored", "insta", @@ -2587,7 +2587,7 @@ dependencies = [ [[package]] name = "forge_repo" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-openai", @@ -2651,7 +2651,7 @@ dependencies = [ [[package]] name = "forge_select" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "colored", @@ -2664,7 +2664,7 @@ dependencies = [ [[package]] name = "forge_services" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-recursion", @@ -2723,7 +2723,7 @@ dependencies = [ [[package]] name = "forge_snaps" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "chrono", @@ -2738,7 +2738,7 @@ dependencies = [ [[package]] name = "forge_spinner" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "colored", @@ -2754,7 +2754,7 @@ dependencies = [ [[package]] name = "forge_stream" -version = "0.1.0" +version = "0.1.1" dependencies = [ "futures", "tokio", @@ -2762,7 +2762,7 @@ dependencies = [ [[package]] name = "forge_template" -version = "0.1.0" +version = "0.1.1" dependencies = [ "html-escape", "pretty_assertions", @@ -2770,7 +2770,7 @@ dependencies = [ [[package]] name = "forge_test_kit" -version = "0.1.0" +version = "0.1.1" dependencies = [ "serde", "serde_json", @@ -2779,7 +2779,7 @@ dependencies = [ [[package]] name = "forge_tool_macros" -version = "0.1.0" +version = "0.1.1" dependencies = [ "proc-macro2", "quote", @@ -2788,7 +2788,7 @@ dependencies = [ [[package]] name = "forge_tracker" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-trait", @@ -2819,7 +2819,7 @@ dependencies = [ [[package]] name = "forge_walker" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "derive_setters", diff --git a/Cargo.toml b/Cargo.toml index 63056488e7..208d487b08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ resolver = "2" [workspace.package] -version = "0.1.0" +license = "MIT" +version = "0.1.1" rust-version = "1.92" edition = "2024" diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000000..5dd72d162a --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,3 @@ +github: [KooshaPari] +custom: ["https://kooshapari.com/sponsor"] + diff --git a/README.md b/README.md index db588d3a78..845059a38e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@

⚒️ Forge: AI-Enhanced Terminal Development Environment

+ +> **Phenotype-org fork of [tailcallhq/forgecode](https://github.com/tailcallhq/forgecode).** +> Phenotype-org additions: `deny.toml` + `cargo-deny.yml` CI bootstrapped 2026-05-01. +> All upstream docs mirror [upstream README](https://github.com/tailcallhq/forgecode/blob/main/README.md). +

A comprehensive coding agent that integrates AI capabilities with your development environment

curl -fsSL https://forgecode.dev/cli | sh

@@ -6,49 +11,6 @@ [![CI Status](https://img.shields.io/github/actions/workflow/status/tailcallhq/forgecode/ci.yml?style=for-the-badge)](https://github.com/tailcallhq/forgecode/actions) [![GitHub Release](https://img.shields.io/github/v/release/tailcallhq/forgecode?style=for-the-badge)](https://github.com/tailcallhq/forgecode/releases) [![Discord](https://img.shields.io/discord/1044859667798568962?style=for-the-badge&cacheSeconds=120&logo=discord)](https://discord.gg/kRZBPpkgwq) -[![CLA assistant](https://cla-assistant.io/readme/badge/tailcallhq/forgecode?style=for-the-badge)](https://cla-assistant.io/tailcallhq/forgecode) - -![Code-Forge Demo](https://assets.antinomy.ai/images/forge_demo_2x.gif) - ---- - -
-Table of Contents - -- [Quickstart](#quickstart) -- [Usage Examples](#usage-examples) -- [Why Forge?](#why-forge) -- [How Forge Works: Three Modes](#how-forge-works-three-modes) - - [Interactive Mode (TUI)](#interactive-mode-tui) - - [One-Shot CLI Mode](#one-shot-cli-mode) - - [ZSH Plugin Mode (`:` prefix)](#zsh-plugin-mode--prefix) -- [ZSH Plugin: The `:` Prefix System](#zsh-plugin-the--prefix-system) - - [Agents](#agents) - - [Sending Prompts](#sending-prompts) - - [Attaching Files](#attaching-files) - - [Conversation Management](#conversation-management) - - [Git Integration](#git-integration) - - [Shell Command Tools](#shell-command-tools) - - [Session & Configuration](#session--configuration) - - [Skills](#skills) - - [Customizing Agent Behavior](#customizing-agent-behavior) - - [Semantic Search (Workspace)](#semantic-search-workspace) - - [Quick Reference: All `:` Commands](#quick-reference-all--commands) -- [Command-Line Options](#command-line-options) -- [Advanced Configuration](#advanced-configuration) - - [Provider Configuration](#provider-configuration) - - [Managing Provider Credentials](#managing-provider-credentials) - - [Deprecated: Environment Variables](#deprecated-environment-variables) - - [forge.yaml Configuration Options](#forgeyaml-configuration-options) - - [Environment Variables](#environment-variables) - - [MCP Configuration](#mcp-configuration) - - [Example Use Cases](#example-use-cases) - - [Usage in Multi-Agent Workflows](#usage-in-multi-agent-workflows) -- [Documentation](#documentation) -- [Community](#community) -- [Support Us](#support-us) - -
--- @@ -177,923 +139,6 @@ Forge helps you code faster, solve complex problems, and learn new technologies --- -## How Forge Works: Three Modes - -Forge has three distinct ways to use it. Understanding this distinction upfront will save you confusion. - -### Interactive Mode (TUI) - -Running `forge` with no arguments starts the interactive terminal UI, a persistent session where you type prompts and the AI responds in a conversational loop. This is the primary way to do multi-step work. - -```bash -forge # Start a new interactive session -forge conversation resume # Resume a specific saved conversation in interactive mode -forge --conversation-id # Same: resume conversation by ID -forge --agent # Start interactive session with a specific agent -forge -C /path/to/project # Start in a specific directory -forge --sandbox experiment-name # Create an isolated git worktree + branch, then start there -``` - -Once inside interactive mode, type your prompt and press Enter. Forge reads files, writes patches, runs commands, and maintains context across the whole session. - -### One-Shot CLI Mode - -Pass `-p` (or `--prompt`) to run a single prompt and exit. Forge does the work and returns to your shell. Useful for scripts, piping output, or quick tasks. - -```bash -forge -p "Explain the purpose of src/main.rs" -forge -p "Add error handling to the parse() function in lib.rs" -echo "What does this do?" | forge # Pipe input as the prompt -forge commit # Generate an AI commit message and commit (exits when done) -forge commit --preview # Generate commit message, print it, then exit -forge suggest "find large log files" # Translate natural language to a shell command, then exit -``` - -> **Note:** `forge conversation resume ` opens the interactive TUI. It does **not** just print a message and exit. If you run it and see the cursor waiting, you are inside the interactive session. Type your prompt or press `Ctrl+C` to exit. - -### ZSH Plugin Mode (`:` prefix) - -Install the ZSH plugin once with `forge setup`, then use `:` commands directly at your shell prompt without ever typing `forge`. This is the fastest mode for day-to-day development: send prompts, switch conversations, commit, and suggest commands without leaving your shell. - -```zsh -: refactor the auth module # Send a prompt to the active agent -:commit # AI-powered git commit -:suggest "find large log files" # Translate description → shell command in your buffer -:conversation # Browse saved conversations with fzf preview -``` - -See the full [ZSH Plugin reference below](#zsh-plugin-the--prefix-system) for all commands and aliases. - ---- - -## ZSH Plugin: The `:` Prefix System - -When you install the ZSH plugin (`forge setup`), you get a `:` prefix command system at your shell prompt. This is the fastest way to use Forge during normal development; you never leave your shell. - -**How it works:** Lines starting with `:` are intercepted before the shell sees them and routed to Forge. Everything else runs normally. - -```zsh -: # Send a prompt to the active agent -:sage # Send a prompt to a specific agent by name (sage, muse, forge, or any custom agent) -:agent # Switch the active agent; opens fzf picker if no name given -``` - -### Agents - -Forge ships with three built-in agents, each with a different role: - -| Agent | Alias | Purpose | Modifies files? | -|---|---|---|---| -| `forge` | (default) | Implementation: builds features, fixes bugs, and runs tests | Yes | -| `sage` | `:ask` | Research: maps architecture, traces data flow, and reads code | No | -| `muse` | `:plan` | Planning: analyzes structure and writes implementation plans to `plans/` | No | - -### Sending Prompts - -```zsh -: refactor the auth module to use the new middleware -:sage how does the caching layer work? # sage = read-only research agent -:muse design a deployment strategy # muse = planning agent (writes to plans/) -:ask how does X work? # alias for :sage -:plan create a migration plan # alias for :muse -``` - -The agent context persists. Typing `:sage` alone (no prompt text) switches the active agent to sage for all subsequent `: ` commands. - -### Attaching Files - -Type `@` in a prompt, then press Tab to fuzzy-search and select files. The path is inserted as `@[filename]` and attached as context to the AI. - -```zsh -: review this code @[src/auth.rs] @[tests/auth_test.rs] -``` - -### Conversation Management - -Forge saves every conversation. You can switch between them like switching directories. - -```zsh -:new # Start a fresh conversation (saves current for :conversation -) -:new # Start a new conversation and immediately send a prompt -:conversation # Open fzf picker: browse and switch conversations with preview -:conversation # Switch directly to a conversation by ID -:conversation - # Toggle between current and previous conversation (like cd -) -:clone # Branch the current conversation (try a different direction) -:clone # Clone a specific conversation by ID -:rename # Rename the current conversation -:conversation-rename # Rename a conversation via fzf picker -:retry # Retry the last prompt (useful if the AI misunderstood) -:copy # Copy the last AI response to clipboard as markdown -:dump # Export conversation as JSON -:dump html # Export conversation as formatted HTML -:compact # Manually compact context to free up token budget -``` - -### Git Integration - -```zsh -:commit # AI reads your diff, writes a commit message, and commits immediately -:commit # Same, but pass extra context: :commit fix typo in readme -:commit-preview # AI generates the message and puts "git commit -m '...'" in your buffer - # so you can review/edit the message before pressing Enter -``` - -### Shell Command Tools - -```zsh -:suggest # Translate natural language to a shell command and put it in your buffer -:edit # Open $EDITOR to compose a complex multi-line prompt, then send it -``` - -### Session & Configuration - -Some commands change settings for the current session only. Others persist to your config file (`~/forge/.forge.toml`). The distinction matters: - -```zsh -# Session-only (reset when you close the terminal; not saved to config) -:model # Change model for this session only -:reasoning-effort # Set reasoning effort: none/minimal/low/medium/high/xhigh/max -:agent # Switch active agent for this session - -# Persistent (saved to config file) -:config-model # Set default model globally (alias: :cm) -:config-provider # Switch provider globally (alias: :provider, :p) -:config-reasoning-effort # Set default reasoning effort globally (alias: :cre) -:config-commit-model # Set model used for :commit (alias: :ccm) -:config-suggest-model # Set model used for :suggest (alias: :csm) -:config-reload # Reset session overrides back to global config (alias: :cr) - -# View & edit config -:info # Show current session info (model, agent, conversation ID) -:config # Display effective resolved configuration in TOML format -:config-edit # Open config file in $EDITOR (alias: :ce) -:tools # List available tools for the current agent -:skill # List available skills -``` - -### Skills - -Skills are reusable workflows the AI can invoke as tools. Forge ships three built-in skills: - -- **`create-skill`**: scaffold a new custom skill -- **`execute-plan`**: execute a plan file from `plans/` -- **`github-pr-description`**: generate a PR description from your diff - -Use `:skill` to list available skills. The AI invokes them automatically when relevant, or you can ask explicitly: `: generate a PR description using the github-pr-description skill`. - -**Custom skills** live in `SKILL.md` files with YAML front-matter. Precedence (highest first): - -| Location | Path | Scope | -|---|---|---| -| Project-local | `.forge/skills//SKILL.md` | This project only | -| Global | `~/forge/skills//SKILL.md` | All projects | -| Built-in | Embedded in binary | Always available | - -Project-local skills override global ones, which override built-in ones. To scaffold a new skill, ask: `: create a new skill`. - -### Customizing Agent Behavior - -**`AGENTS.md`:** Create this file in your project root (or `~/forge/AGENTS.md` globally) to give all agents persistent instructions such as coding conventions, commit message style, and things to avoid. Forge reads it automatically at the start of every conversation. - -**Custom agents:** Place a `.md` file with YAML front-matter in `.forge/agents/` (project) or `~/forge/agents/` (global) to define additional agents with their own models, tools, and system prompts. Project-local agents override global ones. The built-in agent files in `crates/forge_repo/src/agents/` are good examples of the format. - -**Custom commands:** Place YAML files in `.forge/commands/` (project) or `~/forge/commands/` (global) to define shortcut commands available via `:commandname`. Commands can also be defined inline in `forge.yaml` under the `commands:` key. - -### Semantic Search (Workspace) - -```zsh -:sync # Index your codebase for semantic search -:workspace-init # Initialize workspace for indexing -:workspace-status # Show indexing status -:workspace-info # Show workspace details -``` - -After running `:sync`, the AI can search your codebase by meaning rather than exact text matches. Indexing sends file content to the workspace server, which defaults to `https://api.forgecode.dev`. Set `FORGE_WORKSPACE_SERVER_URL` to override this if self-hosting. - -### Quick Reference: All `:` Commands - - -| Command | Alias | What it does | -|---|---|---| -| `: ` | | Send prompt to active agent | -| `:new` | `:n` | Start new conversation | -| `:conversation` | `:c` | Browse/switch conversations (fzf) | -| `:conversation -` | | Toggle to previous conversation | -| `:clone` | | Branch current conversation | -| `:rename ` | `:rn` | Rename current conversation | -| `:conversation-rename` | | Rename conversation (fzf picker) | -| `:retry` | `:r` | Retry last prompt | -| `:copy` | | Copy last response to clipboard | -| `:dump` | `:d` | Export conversation as JSON | -| `:compact` | | Compact context | -| `:commit` | | AI commit (immediate) | -| `:commit-preview` | | AI commit (review first) | -| `:suggest ` | `:s` | Translate natural language to command | -| `:edit` | `:ed` | Compose prompt in $EDITOR | -| `:sage ` | `:ask` | Q&A / code understanding agent | -| `:muse ` | `:plan` | Planning agent | -| `:agent ` | `:a` | Switch active agent (fzf picker if no name given) | -| `:model ` | `:m` | Set model for this session only | -| `:config-model ` | `:cm` | Set default model (persistent) | -| `:reasoning-effort ` | `:re` | Set reasoning effort for session | -| `:config-reload` | `:cr` | Reset session overrides to global config | -| `:info` | `:i` | Show session info | -| `:sync` | `:workspace-sync` | Index codebase for semantic search | -| `:tools` | `:t` | List available tools | -| `:skill` | | List available skills | -| `:login` | `:provider-login` | Login to a provider | -| `:logout` | | Logout from a provider | -| `:keyboard-shortcuts` | `:kb` | Show keyboard shortcuts | -| `:doctor` | | Run shell environment diagnostics | - ---- - -## Command-Line Options - -Here's a quick reference of Forge's command-line options: - -| Option | Description | -| ----------------------------------- | ------------------------------------------------------------------------ | -| `-p, --prompt ` | Direct prompt to process without entering interactive mode | -| `-e, --event ` | Dispatch an event to the workflow in JSON format | -| `--conversation ` | Path to a JSON file containing the conversation to execute | -| `--conversation-id ` | Resume or continue an existing conversation by ID | -| `--agent ` | Agent ID to use for this session | -| `-C, --directory ` | Change to this directory before starting | -| `--sandbox ` | Create an isolated git worktree + branch for safe experimentation | -| `--verbose` | Enable verbose logging output | -| `-h, --help` | Print help information | -| `-V, --version` | Print version | - -### Subcommands - -```bash -# Conversations -forge conversation list # List all saved conversations -forge conversation resume # Resume a conversation in interactive mode -forge conversation new # Create a new conversation ID (prints it) -forge conversation dump # Export conversation as JSON -forge conversation compact # Compact conversation context -forge conversation retry # Retry last message -forge conversation clone # Clone a conversation -forge conversation rename # Rename a conversation -forge conversation delete # Delete a conversation permanently -forge conversation info # Show conversation details -forge conversation stats # Show token usage statistics -forge conversation show # Show last assistant message - -# Commits -forge commit # Generate AI commit message and commit -forge commit --preview # Generate commit message only (prints it) -forge commit fix the auth bug # Pass extra context for the commit message - -# Shell command suggestion -forge suggest "list files by size" # Translate description to a shell command - -# Providers -forge provider login # Add or update provider credentials (interactive) -forge provider logout # Remove provider credentials -forge list provider # List supported providers - -# Models & agents -forge list model # List available models -forge list agent # List available agents - -# Workspace / semantic search -forge workspace sync # Index current directory for semantic search -forge workspace init # Initialize workspace -forge workspace status # Show indexing status -forge workspace query # Query the semantic index - -# MCP servers -forge mcp list # List configured MCP servers -forge mcp import # Add a server from JSON -forge mcp show # Show server configuration -forge mcp remove # Remove a server -forge mcp reload # Reload all servers and rebuild caches - -# Other -forge info # Show config, active model, environment -forge list tool --agent # List tools for a specific agent -forge doctor # Run shell environment diagnostics -forge update # Update forge to the latest version -forge setup # Install ZSH plugin (updates .zshrc) -``` - -## Advanced Configuration - -### Provider Configuration - -Forge supports multiple AI providers. The recommended way to configure providers is using the interactive login command: - -```bash -forge provider login -``` - -This will: - -1. Show you a list of available providers -2. Guide you through entering the required credentials - -#### Managing Provider Credentials - -```bash -# Login to a provider (add or update credentials) -forge provider login - -# Remove provider credentials -forge provider logout - -# List supported providers -forge provider list -``` - -#### Deprecated: Environment Variables - -> **⚠️ DEPRECATED**: Using `.env` files for provider configuration is deprecated and will be removed in a future version. Please use `forge provider login` instead. - -For backward compatibility, Forge still supports environment variables. On first run, any credentials found in environment variables will be automatically migrated to file-based storage. - -
-Legacy Environment Variable Setup (Deprecated) - -
-OpenRouter - -```bash -# .env -OPENROUTER_API_KEY= -``` - -
- -
-Requesty - -```bash -# .env -REQUESTY_API_KEY= -``` - -
- -
-x-ai - -```bash -# .env -XAI_API_KEY= -``` - -
- -
-z.ai - -```bash -# .env -ZAI_API_KEY= - -# Or for coding plan subscription -ZAI_CODING_API_KEY= -``` - -
- -
-Cerebras - -```bash -# .env -CEREBRAS_API_KEY= -``` - -
- -
-IO Intelligence - -```bash -# .env -IO_INTELLIGENCE_API_KEY= -``` - -```yaml -# forge.yaml -model: meta-llama/Llama-3.3-70B-Instruct -``` - -
- -
-OpenAI - -```bash -# .env -OPENAI_API_KEY= -``` - -```yaml -# forge.yaml -model: o3-mini-high -``` - -
- -
-Anthropic - -```bash -# .env -ANTHROPIC_API_KEY= -``` - -```yaml -# forge.yaml -model: claude-3.7-sonnet -``` - -
- -
-Google Vertex AI - -**Setup Instructions:** - -1. **Install Google Cloud CLI** and authenticate: - - ```bash - gcloud auth login - gcloud config set project YOUR_PROJECT_ID - ``` - -2. **Get your authentication token**: - - ```bash - gcloud auth print-access-token - ``` - -3. **Use the token when logging in via Forge**: - - ```bash - forge provider login - # Select Google Vertex AI and enter your credentials - ``` - -**Legacy `.env` setup:** - -```bash -# .env -PROJECT_ID= -LOCATION= -VERTEX_AI_AUTH_TOKEN= -``` - -```yaml -# forge.yaml -model: google/gemini-2.5-pro -``` - -**Available Models:** -- Claude models: `claude-sonnet-4@20250514` -- Gemini models: `gemini-2.5-pro`, `gemini-2.0-flash` - -Use the `/model` command in Forge CLI to see all available models. - -
- -
-OpenAI-Compatible Providers - -```bash -# .env -OPENAI_API_KEY= -OPENAI_URL= -``` - -```yaml -# forge.yaml -model: -``` - -
- -
-Groq - -```bash -# .env -OPENAI_API_KEY= -OPENAI_URL=https://api.groq.com/openai/v1 -``` - -```yaml -# forge.yaml -model: deepseek-r1-distill-llama-70b -``` - -
- -
-Amazon Bedrock - -To use Amazon Bedrock models with Forge, you'll need to first set up the [Bedrock Access Gateway](https://github.com/aws-samples/bedrock-access-gateway): - -1. **Set up Bedrock Access Gateway**: - - - Follow the deployment steps in the [Bedrock Access Gateway repo](https://github.com/aws-samples/bedrock-access-gateway) - - Create your own API key in Secrets Manager - - Deploy the CloudFormation stack - - Note your API Base URL from the CloudFormation outputs - -2. **Configure in Forge**: - - ```bash - forge provider login - # Select OpenAI-compatible provider and enter your Bedrock Gateway details - ``` - -**Legacy `.env` setup:** - -```bash -# .env -OPENAI_API_KEY= -OPENAI_URL= -``` - -```yaml -# forge.yaml -model: anthropic.claude-3-opus -``` - -
- -
-ForgeCode Services - -```bash -# .env -FORGE_API_KEY= -``` - -```yaml -# forge.yaml -model: claude-3.7-sonnet -``` - -
- -
- ---- - -### forge.yaml Configuration Options - -### Environment Variables - -Forge supports several environment variables for advanced configuration and fine-tuning. These can be set in your `.env` file or system environment. - -
-Retry Configuration - -Control how Forge handles retry logic for failed requests: - -```bash -# .env -FORGE_RETRY_INITIAL_BACKOFF_MS=1000 # Initial backoff time in milliseconds (default: 1000) -FORGE_RETRY_BACKOFF_FACTOR=2 # Multiplier for backoff time (default: 2) -FORGE_RETRY_MAX_ATTEMPTS=3 # Maximum retry attempts (default: 3) -FORGE_SUPPRESS_RETRY_ERRORS=false # Suppress retry error messages (default: false) -FORGE_RETRY_STATUS_CODES=429,500,502 # HTTP status codes to retry (default: 429,500,502,503,504) -``` - -
- -
-HTTP Configuration - -Fine-tune HTTP client behavior for API requests: - -```bash -# .env -FORGE_HTTP_CONNECT_TIMEOUT=30 # Connection timeout in seconds (default: 30) -FORGE_HTTP_READ_TIMEOUT=900 # Read timeout in seconds (default: 900) -FORGE_HTTP_POOL_IDLE_TIMEOUT=90 # Pool idle timeout in seconds (default: 90) -FORGE_HTTP_POOL_MAX_IDLE_PER_HOST=5 # Max idle connections per host (default: 5) -FORGE_HTTP_MAX_REDIRECTS=10 # Maximum redirects to follow (default: 10) -FORGE_HTTP_USE_HICKORY=false # Use Hickory DNS resolver (default: false) -FORGE_HTTP_TLS_BACKEND=default # TLS backend: "default" or "rustls" (default: "default") -FORGE_HTTP_MIN_TLS_VERSION=1.2 # Minimum TLS version: "1.0", "1.1", "1.2", "1.3" -FORGE_HTTP_MAX_TLS_VERSION=1.3 # Maximum TLS version: "1.0", "1.1", "1.2", "1.3" -FORGE_HTTP_ADAPTIVE_WINDOW=true # Enable HTTP/2 adaptive window (default: true) -FORGE_HTTP_KEEP_ALIVE_INTERVAL=60 # Keep-alive interval in seconds (default: 60, use "none"/"disabled" to disable) -FORGE_HTTP_KEEP_ALIVE_TIMEOUT=10 # Keep-alive timeout in seconds (default: 10) -FORGE_HTTP_KEEP_ALIVE_WHILE_IDLE=true # Keep-alive while idle (default: true) -FORGE_HTTP_ACCEPT_INVALID_CERTS=false # Accept invalid certificates (default: false) - USE WITH CAUTION -FORGE_HTTP_ROOT_CERT_PATHS=/path/to/cert1.pem,/path/to/cert2.crt # Paths to root certificate files (PEM, CRT, CER format), multiple paths separated by commas -``` - -> **⚠️ Security Warning**: Setting `FORGE_HTTP_ACCEPT_INVALID_CERTS=true` disables SSL/TLS certificate verification, which can expose you to man-in-the-middle attacks. Only use this in development environments or when you fully trust the network and endpoints. - -
- -
-API Configuration - -Override default API endpoints and provider/model settings: - -```bash -# .env -FORGE_API_URL=https://api.forgecode.dev # Custom Forge API URL (default: https://api.forgecode.dev) -FORGE_WORKSPACE_SERVER_URL=http://localhost:8080 # URL for the indexing server (default: https://api.forgecode.dev/) -``` - -
- -
-Tool Configuration - -Configuring the tool calls settings: - -```bash -# .env -FORGE_TOOL_TIMEOUT=300 # Maximum execution time in seconds for a tool before it is terminated to prevent hanging the session. (default: 300) -FORGE_MAX_IMAGE_SIZE=10485760 # Maximum image file size in bytes for read_image operations (default: 10485760 - 10 MB) -FORGE_DUMP_AUTO_OPEN=false # Automatically open dump files in browser (default: false) -FORGE_DEBUG_REQUESTS=/path/to/debug/requests.json # Write debug HTTP request files to specified path (supports absolute and relative paths) -``` - -
- -
-ZSH Plugin Configuration - -Configure the ZSH plugin behavior: - -```bash -# .env -FORGE_BIN=forge # Command to use for forge operations (default: "forge") -``` - -The `FORGE_BIN` environment variable allows you to customize the command used by the ZSH plugin when transforming `:` prefixed commands. If not set, it defaults to `"forge"`. - -
- -
-Display Configuration - -Configure display options for the Forge UI and ZSH theme: - -```bash -# .env -FORGE_CURRENCY_SYMBOL="$" # Currency symbol for cost display in ZSH theme (default: "$") -FORGE_CURRENCY_CONVERSION_RATE=1.0 # Conversion rate for currency display (default: 1.0) -NERD_FONT=1 # Enable Nerd Font icons in ZSH theme (default: auto-detected, set to "1" or "true" to enable, "0" or "false" to disable) -USE_NERD_FONT=1 # Alternative variable for enabling Nerd Font icons (same behavior as NERD_FONT) -``` - -The `FORGE_CURRENCY_SYMBOL` and `FORGE_CURRENCY_CONVERSION_RATE` variables control how costs are displayed in the ZSH theme right prompt. Use these to customize the currency display for your region or preferred currency. - -
- -
-System Configuration - -System-level environment variables (usually set automatically): - -```bash -# .env -FORGE_CONFIG=/custom/config/dir # Base directory for all Forge config files (default: ~/.forge) -FORGE_MAX_SEARCH_RESULT_BYTES=10240 # Maximum bytes for search results (default: 10240 - 10 KB) -FORGE_HISTORY_FILE=/path/to/history # Custom path for Forge history file (default: uses system default location) -FORGE_BANNER="Your custom banner text" # Custom banner text to display on startup (default: Forge ASCII art) -FORGE_MAX_CONVERSATIONS=100 # Maximum number of conversations to show in list (default: 100) -FORGE_MAX_LINE_LENGTH=2000 # Maximum characters per line for file read operations (default: 2000) -FORGE_STDOUT_MAX_LINE_LENGTH=2000 # Maximum characters per line for shell output (default: 2000) -SHELL=/bin/zsh # Shell to use for command execution (Unix/Linux/macOS) -COMSPEC=cmd.exe # Command processor to use (Windows) -``` - -
- -
-Semantic Search Configuration - -Configure semantic search behavior for code understanding: - -```bash -# .env -FORGE_SEM_SEARCH_LIMIT=200 # Maximum number of results to return from initial vector search (default: 200) -FORGE_SEM_SEARCH_TOP_K=20 # Top-k parameter for relevance filtering during semantic search (default: 20) -``` - -
- -
-Logging Configuration - -Configure logging verbosity and output: - -```bash -# .env -FORGE_LOG=forge=info # Log filter level (default: forge=debug when tracking disabled, forge=info when tracking enabled) -``` - -The `FORGE_LOG` variable controls the logging level for Forge's internal operations using the standard tracing filter syntax. Common values: -- `forge=error` - Only errors -- `forge=warn` - Warnings and errors -- `forge=info` - Informational messages (default when tracking enabled) -- `forge=debug` - Debug information (default when tracking disabled) -- `forge=trace` - Detailed tracing - -
- -
-Tracking Configuration - -Control tracking of user-identifying metadata in telemetry events: - -```bash -# .env -FORGE_TRACKER=false # Disable tracking enrichment metadata (default: true) -``` - -The `FORGE_TRACKER` variable controls whether tracking enrichment metadata is included in telemetry events. - -
- -The `forge.yaml` file supports several advanced configuration options that let you customize Forge's behavior. - -
-Custom Rules - -Add your own guidelines that all agents should follow when generating responses. - -```yaml -# forge.yaml -custom_rules: | - 1. Always add comprehensive error handling to any code you write. - 2. Include unit tests for all new functions. - 3. Follow our team's naming convention: camelCase for variables, PascalCase for classes. -``` - -
- -
-Commands - -Define custom commands as shortcuts for repetitive prompts: - -```yaml -# forge.yaml -commands: - - name: "refactor" - description: "Refactor selected code" - prompt: "Please refactor this code to improve readability and performance" -``` - -
- -
-Model - -Specify the default AI model to use for all agents in the workflow. - -```yaml -# forge.yaml -model: "claude-3.7-sonnet" -``` - -
- -
-Max Walker Depth - -Control how deeply Forge traverses your project directory structure when gathering context. - -```yaml -# forge.yaml -max_walker_depth: 3 # Limit directory traversal to 3 levels deep -``` - -
- -
-Temperature - -Adjust the creativity and randomness in AI responses. Lower values (0.0-0.3) produce more focused, deterministic outputs, while higher values (0.7-2.0) generate more diverse and creative results. - -```yaml -# forge.yaml -temperature: 0.7 # Balanced creativity and focus -``` - -
-
-Tool Max Failure Limit - -Control how many times a tool can fail before Forge forces completion to prevent infinite retry loops. This helps avoid situations where an agent gets stuck repeatedly trying the same failing operation. - -```yaml -# forge.yaml -max_tool_failure_per_turn: 3 # Allow up to 3 failures per tool before forcing completion -``` - -Set to a higher value if you want more retry attempts, or lower if you want faster failure detection. - -
- -
-Max Requests Per Turn - -Limit the maximum number of requests an agent can make in a single conversation turn. This prevents runaway conversations and helps control API usage and costs. - -```yaml -# forge.yaml -max_requests_per_turn: 50 # Allow up to 50 requests per turn -``` - -When this limit is reached, Forge will: - -- Ask you if you wish to continue -- If you respond with 'Yes', it will continue the conversation -- If you respond with 'No', it will end the conversation - -
- ---- - -
-Model Context Protocol (MCP) - -The MCP feature allows AI agents to communicate with external tools and services. This implementation follows Anthropic's [Model Context Protocol](https://docs.anthropic.com/en/docs/claude-code/tutorials#set-up-model-context-protocol-mcp) design. - -### MCP Configuration - -Configure MCP servers using the CLI: - -```bash -# List all MCP servers -forge mcp list - -# Import a server from JSON -forge mcp import - -# Show server configuration details -forge mcp show - -# Remove a server -forge mcp remove - -# Reload servers and rebuild caches -forge mcp reload -``` - -Or manually create a `.mcp.json` file with the following structure: - -```json -{ - "mcpServers": { - "server_name": { - "command": "command_to_execute", - "args": ["arg1", "arg2"], - "env": { "ENV_VAR": "value" } - }, - "another_server": { - "url": "http://localhost:3000/events" - } - } -} -``` - -MCP configurations are read from two locations (project-local takes precedence): - -1. **Project-local:** `.mcp.json` in your project directory -2. **Global:** `~/forge/.mcp.json` - -### Example Use Cases - -MCP can be used for various integrations: - -- Web browser automation -- External API interactions -- Tool integration -- Custom service connections - -### Usage in Multi-Agent Workflows - -MCP tools can be used as part of multi-agent workflows, allowing specialized agents to interact with external systems as part of a collaborative problem-solving approach. - -
- ---- - -## Documentation - -For comprehensive documentation on all features and capabilities, please visit the [documentation site](https://github.com/tailcallhq/forgecode/tree/main/docs). - ---- - ## Installation ```bash @@ -1104,21 +149,21 @@ curl -fsSL https://forgecode.dev/cli | sh nix run github:tailcallhq/forgecode # for latest dev branch ``` ---- - ## Community Join our vibrant Discord community to connect with other Forge users and contributors, get help with your projects, share ideas, and provide feedback! [![Discord](https://img.shields.io/discord/1044859667798568962?style=for-the-badge&cacheSeconds=120&logo=discord)](https://discord.gg/kRZBPpkgwq) ---- +## Documentation -## Support Us +For comprehensive documentation on all features and capabilities, please visit the [documentation site](https://github.com/tailcallhq/forgecode/tree/main/docs). -Your support drives Forge's continued evolution! By starring our GitHub repository, you: +--- -- Help others discover this powerful tool 🔍 -- Motivate our development team 💪 -- Enable us to prioritize new features 🛠️ -- Strengthen our open-source community 🌱 + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..5137c43ff1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.x | :white_check_mark: | + +## Reporting a Vulnerability + +If you discover a security vulnerability, please report it via: +- GitHub Security Advisories +- Or contact the maintainers directly + +Please do not disclose security issues publicly until a fix is available. diff --git a/crates/forge_api/Cargo.toml b/crates/forge_api/Cargo.toml index 9a567acfe6..8c31f70ce1 100644 --- a/crates/forge_api/Cargo.toml +++ b/crates/forge_api/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_api" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_app/Cargo.toml b/crates/forge_app/Cargo.toml index 6fbd6df6d9..bfdbe9690f 100644 --- a/crates/forge_app/Cargo.toml +++ b/crates/forge_app/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_app" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_ci/Cargo.toml b/crates/forge_ci/Cargo.toml index 03bd2eb0eb..1fd0a18d4b 100644 --- a/crates/forge_ci/Cargo.toml +++ b/crates/forge_ci/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_ci" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_config/Cargo.toml b/crates/forge_config/Cargo.toml index 96c58b829a..da51564e40 100644 --- a/crates/forge_config/Cargo.toml +++ b/crates/forge_config/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_config" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_display/Cargo.toml b/crates/forge_display/Cargo.toml index 2a11aca5fa..bd63b4ecc8 100644 --- a/crates/forge_display/Cargo.toml +++ b/crates/forge_display/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_display" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_domain/Cargo.toml b/crates/forge_domain/Cargo.toml index 966e2af9f6..6f64439cee 100644 --- a/crates/forge_domain/Cargo.toml +++ b/crates/forge_domain/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_domain" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_embed/Cargo.toml b/crates/forge_embed/Cargo.toml index c221cc13d4..c2c0f9b644 100644 --- a/crates/forge_embed/Cargo.toml +++ b/crates/forge_embed/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_embed" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_eventsource/Cargo.toml b/crates/forge_eventsource/Cargo.toml index 4f1aad9d8f..a351920cc2 100644 --- a/crates/forge_eventsource/Cargo.toml +++ b/crates/forge_eventsource/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_eventsource" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_eventsource_stream/Cargo.toml b/crates/forge_eventsource_stream/Cargo.toml index 781dbd09f0..ed0799e765 100644 --- a/crates/forge_eventsource_stream/Cargo.toml +++ b/crates/forge_eventsource_stream/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_eventsource_stream" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [features] diff --git a/crates/forge_fs/Cargo.toml b/crates/forge_fs/Cargo.toml index e6e2bf7dab..dadca50dda 100644 --- a/crates/forge_fs/Cargo.toml +++ b/crates/forge_fs/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_fs" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_infra/Cargo.toml b/crates/forge_infra/Cargo.toml index f907fa33db..db9c8b7574 100644 --- a/crates/forge_infra/Cargo.toml +++ b/crates/forge_infra/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_infra" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_json_repair/Cargo.toml b/crates/forge_json_repair/Cargo.toml index b60e51292e..0a2f4b4610 100644 --- a/crates/forge_json_repair/Cargo.toml +++ b/crates/forge_json_repair/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_json_repair" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] @@ -13,4 +14,4 @@ schemars = { workspace = true } serde_json5 = "0.2.1" [dev-dependencies] -pretty_assertions = { workspace = true } \ No newline at end of file +pretty_assertions = { workspace = true } diff --git a/crates/forge_main/Cargo.toml b/crates/forge_main/Cargo.toml index e136bc306d..efdf0bf823 100644 --- a/crates/forge_main/Cargo.toml +++ b/crates/forge_main/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_main" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [[bin]] diff --git a/crates/forge_markdown_stream/Cargo.toml b/crates/forge_markdown_stream/Cargo.toml index 5449822e33..71d2e97e1b 100644 --- a/crates/forge_markdown_stream/Cargo.toml +++ b/crates/forge_markdown_stream/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_markdown_stream" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [lib] diff --git a/crates/forge_repo/Cargo.toml b/crates/forge_repo/Cargo.toml index 788afb768f..09db6ea925 100644 --- a/crates/forge_repo/Cargo.toml +++ b/crates/forge_repo/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_repo" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_select/Cargo.toml b/crates/forge_select/Cargo.toml index d12b3a0e71..c2320b37ef 100644 --- a/crates/forge_select/Cargo.toml +++ b/crates/forge_select/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_select" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_services/Cargo.toml b/crates/forge_services/Cargo.toml index 5e3be29337..d16f516cd7 100644 --- a/crates/forge_services/Cargo.toml +++ b/crates/forge_services/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_services" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_snaps/Cargo.toml b/crates/forge_snaps/Cargo.toml index 4997a3685b..d201b5ae26 100644 --- a/crates/forge_snaps/Cargo.toml +++ b/crates/forge_snaps/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_snaps" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] @@ -17,4 +18,4 @@ forge_domain.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["macros", "rt", "time", "test-util"] } -tempfile.workspace = true \ No newline at end of file +tempfile.workspace = true diff --git a/crates/forge_spinner/Cargo.toml b/crates/forge_spinner/Cargo.toml index 2076ff0429..5930a93e3b 100644 --- a/crates/forge_spinner/Cargo.toml +++ b/crates/forge_spinner/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_spinner" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_stream/Cargo.toml b/crates/forge_stream/Cargo.toml index e601537459..80e9f4f8c6 100644 --- a/crates/forge_stream/Cargo.toml +++ b/crates/forge_stream/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_stream" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] @@ -9,4 +10,4 @@ futures.workspace = true tokio.workspace = true [dev-dependencies] -tokio = { workspace = true, features = ["macros", "rt", "time", "test-util"] } \ No newline at end of file +tokio = { workspace = true, features = ["macros", "rt", "time", "test-util"] } diff --git a/crates/forge_template/Cargo.toml b/crates/forge_template/Cargo.toml index 8123d00d9a..e817608681 100644 --- a/crates/forge_template/Cargo.toml +++ b/crates/forge_template/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_template" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_test_kit/Cargo.toml b/crates/forge_test_kit/Cargo.toml index f169443335..0e794a776a 100644 --- a/crates/forge_test_kit/Cargo.toml +++ b/crates/forge_test_kit/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_test_kit" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_tool_macros/Cargo.toml b/crates/forge_tool_macros/Cargo.toml index 93e102ed34..90ba6314ab 100644 --- a/crates/forge_tool_macros/Cargo.toml +++ b/crates/forge_tool_macros/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_tool_macros" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [lib] @@ -10,4 +11,4 @@ proc-macro = true [dependencies] syn.workspace = true quote.workspace = true -proc-macro2.workspace = true \ No newline at end of file +proc-macro2.workspace = true diff --git a/crates/forge_tracker/Cargo.toml b/crates/forge_tracker/Cargo.toml index 2ea2af50ce..043ed006dc 100644 --- a/crates/forge_tracker/Cargo.toml +++ b/crates/forge_tracker/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_tracker" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] diff --git a/crates/forge_walker/Cargo.toml b/crates/forge_walker/Cargo.toml index 2aed0d7af0..8511dd6a92 100644 --- a/crates/forge_walker/Cargo.toml +++ b/crates/forge_walker/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "forge_walker" -version = "0.1.0" +version = "0.1.1" edition.workspace = true +license.workspace = true rust-version.workspace = true [dependencies] @@ -12,4 +13,4 @@ derive_setters.workspace = true [dev-dependencies] pretty_assertions.workspace = true -tempfile.workspace = true \ No newline at end of file +tempfile.workspace = true diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000000..271ad23902 --- /dev/null +++ b/deny.toml @@ -0,0 +1,43 @@ +# Phenotype forgecode — Cargo Deny Configuration +# https://github.com/KooshaPari/forgecode + +[licenses] +version = 2 +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "Zlib", + "MPL-2.0", + "0BSD", + "CC0-1.0", + "Unicode-3.0", + "BSL-1.0", + "Unlicense", + "CDLA-Permissive-2.0", + "GPL-3.0-only", + "GPL-3.0-or-later", +] + +[advisories] +db-path = "$CARGO_HOME/advisory-db" +ignore = [ + # Pre-existing fork-specific ignores (preserve) + { id = "RUSTSEC-2026-0118" }, + { id = "RUSTSEC-2026-0119" }, + { id = "RUSTSEC-2026-0098" }, + { id = "RUSTSEC-2026-0099" }, + { id = "RUSTSEC-2026-0104" }, + + # Unmaintained-transitive advisories surfaced via upstream forgecode workspace + # deps (no direct use in Phenotype additions). All have "no safe upgrade" + # per RustSec; resolution depends on upstream tailcallhq/forgecode bumps. + { id = "RUSTSEC-2025-0141", reason = "bincode 1.x unmaintained; transitive via upstream workspace deps; bincode 2.x migration is upstream-owned." }, + { id = "RUSTSEC-2024-0436", reason = "paste unmaintained; transitive via proc-macro deps; paste! macro not invoked directly in Phenotype additions." }, + { id = "RUSTSEC-2025-0134", reason = "rustls-pemfile unmaintained (folded into rustls-pki-types); transitive via upstream rustls stack; pending upstream bump." }, + { id = "RUSTSEC-2024-0320", reason = "yaml-rust unmaintained; transitive via syntect (forge_display); upstream syntect has not migrated to yaml-rust2." }, + # Note: RUSTSEC-2026-0049 (advisory-not-detected) entry already removed in 40114d8dc. +] diff --git a/docs/adr/0001-compaction-summarization-strategy.md b/docs/adr/0001-compaction-summarization-strategy.md new file mode 100644 index 0000000000..301b54f39e --- /dev/null +++ b/docs/adr/0001-compaction-summarization-strategy.md @@ -0,0 +1,206 @@ +# ADR-0001: Compaction Summarization Strategy + +**Date:** 2026-05-02 +**Status:** Accepted +**Deciders:** Forgecode Team + +--- + +## Context + +The forgecode context compaction system currently uses pure structural extraction to summarize conversations. This approach: + +- Extracts tool calls, tool results, file paths, and commands +- Renders into a markdown template (`forge-partial-summary-frame.md`) +- Is fast (~0ms), deterministic, and cost-free + +However, this approach has limitations: +1. **Low semantic fidelity** — captures structure, not meaning +2. **No understanding of decisions** — can't capture why changes were made +3. **Verbose output** — includes all operations, even low-value ones +4. **No prioritization** — treats all content equally + +As forgecode grows more capable and handles complex multi-step tasks, the quality of context summarization directly impacts downstream task performance. + +--- + +## Decision + +We will implement a **hybrid summarization strategy** with three modes: + +```rust +pub enum SummarizationStrategy { + /// Pure structural extraction (current behavior) + Extract, + + /// LLM-based semantic summarization + Llm, + + /// Hybrid: extract first, then refine with LLM + Hybrid, +} +``` + +**Default:** `Extract` (backward compatible) +**Configuration:** Per-agent via `compact.summarization_strategy` + +--- + +## Rationale + +### Why not pure LLM? + +- **Latency**: LLM summarization adds 500ms-2s per compaction +- **Cost**: Per-token API costs accumulate with frequent compaction +- **Determinism**: Same input may produce different outputs +- **Complexity**: Requires error handling for API failures + +### Why not pure extraction? + +- **Semantic fidelity**: Can't capture decision rationale +- **Noise**: Includes low-value operations +- **Quality ceiling**: Limited improvement potential + +### Why hybrid? + +- **Best of both**: Fast extraction with LLM refinement +- **Progressive enhancement**: Users can opt into higher quality +- **Fallback safety**: Extract always available as fallback +- **Cost control**: Use cheaper models for summarization + +--- + +## Implementation Options + +### Option A: Extract-Only (Status Quo) + +**Pros:** +- Fastest (~0ms) +- Zero API cost +- Fully deterministic +- No API failure modes + +**Cons:** +- Low semantic fidelity +- Verbose summaries +- No decision capture + +### Option B: Pure LLM + +**Pros:** +- Highest semantic fidelity +- Captures decisions and rationale +- Can identify important context + +**Cons:** +- ~500ms-2s latency per compaction +- Per-token API cost +- Non-deterministic output +- API failure handling required + +### Option C: Hybrid (Selected) + +**Pros:** +- Balance of speed and quality +- Can use cheap models (haiku) +- Structured data from extraction + semantics from LLM +- Fallback to extract on failure + +**Cons:** +- More complex implementation +- Two-step process adds some latency +- Requires LLM integration + +### Option D: Adaptive Cascade + +**Pros:** +- Automatically chooses strategy based on complexity +- Best resource allocation +- Can escalate as needed + +**Cons:** +- Most complex implementation +- Harder to reason about behavior +- More configuration surface + +--- + +## Decision Outcome + +We select **Option C (Hybrid)** as the default for enhanced compaction, with: + +1. **Extract as default** for backward compatibility +2. **Hybrid mode** as the recommended upgrade path +3. **LLM-only** available as opt-in for users who prioritize quality over speed +4. **Configurable model** for summarization (default: haiku-3.5) +5. **Timeout protection** (3s max for LLM operations) +6. **Fallback to extract** on any LLM failure + +--- + +## Consequences + +### Positive + +- [x] Improved summary quality when enabled +- [x] Backward compatible with existing configurations +- [x] Users can choose their cost/quality tradeoff +- [x] Can use cheap models for summarization +- [x] Fallback ensures reliability + +### Negative + +- [ ] Adds complexity to Compactor implementation +- [ ] Requires LLM provider integration in forge_app +- [ ] Template engine needs enhancement for new formats + +### Neutral + +- [ ] New configuration options added (non-breaking) +- [ ] Metrics collection added for observability +- [ ] History tracking for incremental summarization + +--- + +## Configuration + +```yaml +# forge.toml +[compact] +enabled = true +token_threshold = 100_000 +eviction_window = 0.2 + +# NEW: Summarization configuration +summarization_strategy = "hybrid" # extract | llm | hybrid +summary_model = "claude-3-5-haiku" # cheaper model for summarization +summary_max_tokens = 4000 +summary_timeout_secs = 3 +``` + +--- + +## Risks + +| Risk | Likelihood | Impact | Mitigation | +|------|------------|--------|------------| +| LLM adds latency | High | Medium | Use cheap model, timeout, cache summaries | +| LLM quality inconsistent | Medium | High | Validate format, fallback to extract | +| API failures | Low | Medium | Graceful fallback to extract | +| Cost accumulation | Medium | Medium | Per-compaction budget, cheap models | + +--- + +## Review History + +- 2026-05-02: Initial draft +- 2026-05-02: Accepted (selecting Option C) + +--- + +## Related Documents + +- Plan: `plans/2026-05-02-compaction-enhancement-v1.md` +- Config: `crates/forge_config/src/compact.rs` +- Domain: `crates/forge_domain/src/compact/` +- App: `crates/forge_app/src/compact.rs` diff --git a/docs/journeys/manifests/README.md b/docs/journeys/manifests/README.md new file mode 100644 index 0000000000..424d933c00 --- /dev/null +++ b/docs/journeys/manifests/README.md @@ -0,0 +1 @@ +# Journey Manifests diff --git a/docs/operations/iconography/SPEC.md b/docs/operations/iconography/SPEC.md new file mode 100644 index 0000000000..e82e26737b --- /dev/null +++ b/docs/operations/iconography/SPEC.md @@ -0,0 +1,6 @@ +# Iconography Standard + +Implements the [phenotype-infra iconography standard](https://github.com/kooshapari/phenotype-infra/blob/main/docs/governance/iconography-standard.md). + +Three styles: Fluent (stroke), Material (filled+outlined), Liquid Glass (blur). +All icons: 24×24 SVG, `currentColor`, `role="img"`, `aria-label`. diff --git a/docs/operations/journey-traceability.md b/docs/operations/journey-traceability.md new file mode 100644 index 0000000000..c9c5ec1d9b --- /dev/null +++ b/docs/operations/journey-traceability.md @@ -0,0 +1,14 @@ +# Journey Traceability + +Implements the [phenotype-infra journey-traceability standard](https://github.com/kooshapari/phenotype-infra/blob/main/docs/governance/journey-traceability-standard.md). + +## User-Facing Flows + +Document key flows with journey manifests in `docs/journeys/manifests/`. + +## Status + +- [ ] Identify key user-facing flows +- [ ] Record VHS tapes for each flow +- [ ] Author manifests in `docs/journeys/manifests/` +- [ ] Run `phenotype-journey verify` in CI diff --git a/docs/tasks/task-compaction-enhancement.md b/docs/tasks/task-compaction-enhancement.md new file mode 100644 index 0000000000..afc25184a7 --- /dev/null +++ b/docs/tasks/task-compaction-enhancement.md @@ -0,0 +1,142 @@ +# TASK: Enhanced Compaction System + +**ID:** task-compaction-enhancement +**Status:** Open +**Created:** 2026-05-02 +**Parent Plan:** `plans/2026-05-02-compaction-enhancement-v1.md` +**Related ADR:** `docs/adr/0001-compaction-summarization-strategy.md` + +--- + +## Objective + +Enhance the forgecode context compaction system with LLM-based semantic summarization, adaptive eviction, importance-based preservation, and pre-compaction filtering. + +--- + +## Tasks + +### Phase 1 — Configuration & Core Types + +- [ ] **T1.1:** Extend `CompactConfig` with new options (`crates/forge_config/src/compact.rs`) + - Add `summarization_strategy: SummarizationStrategy` + - Add `enable_prefilter: bool` + - Add `enable_adaptive_eviction: bool` + - Add `enable_importance_scoring: bool` + - Add `summary_max_tokens: Option` + +- [ ] **T1.2:** Create `CompactionHistory` struct (`crates/forge_domain/src/compact/history.rs`) + - `summary_hashes: Vec` + - `file_versions: HashMap` + - `compaction_count: usize` + - `total_tokens_reduced: usize` + +- [ ] **T1.3:** Create `ImportanceScore` types (`crates/forge_domain/src/compact/importance.rs`) + - `MessageImportance` struct + - `ImportanceFactor` enum + - `calculate()` function + - `MIN_SURVIVAL_SCORE` constant + +### Phase 2 — Eviction Strategy + +- [ ] **T2.1:** Implement adaptive eviction window (`crates/forge_domain/src/compact/strategy.rs`) + - `adaptive_eviction()` function + - Configurable via `enable_adaptive_eviction` + +- [ ] **T2.2:** Implement importance-based range finding + - Filter protected messages from eviction candidates + - Preserve high-importance messages + +### Phase 3 — LLM Summarization + +- [ ] **T3.1:** Create summarization prompt template (`templates/forge-summarization-prompt.md`) + - Structured prompt for LLM summarization + - Include conversation context and history + +- [ ] **T3.2:** Implement `LlmSummarizer` service (`crates/forge_app/src/services/summarizer.rs`) + - `summarize()` async function + - Model selection (compact model or agent model) + - Timeout handling + +- [ ] **T3.3:** Integrate into `Compactor` (`crates/forge_app/src/compact.rs`) + - Add summarization strategy handling + - Hybrid mode: extract then refine + - Fallback on LLM failure + +### Phase 4 — Pre-Compaction Filtering + +- [ ] **T4.1:** Implement `PreCompactionFilter` (`crates/forge_app/src/transformers/prefilter.rs`) + - `filter()` function + - `collapse_duplicates()` function + - Minimum tool result length + - Debug pattern removal + +### Phase 5 — Templates & Output + +- [ ] **T5.1:** Create enhanced summary frame (`templates/forge-partial-summary-frame-v2.md`) + - Support both structured and LLM content + - Compact format with key sections + +### Phase 6 — Metrics + +- [ ] **T6.1:** Implement `CompactionMetrics` (`crates/forge_domain/src/compact/metrics.rs`) + - Track compaction count, token reduction, strategies used + - Error recording + +- [ ] **T6.2:** Integrate metrics collection into Compactor + - Record after each compaction + +--- + +## Verification + +### Unit Tests +- [ ] Test adaptive eviction window calculation +- [ ] Test importance score calculation +- [ ] Test pre-filter removes short tool results +- [ ] Test deduplication of consecutive tool calls +- [ ] Test LLM summarizer (mocked) + +### Integration Tests +- [ ] Test compaction with Extract strategy +- [ ] Test compaction with LLM strategy (mocked) +- [ ] Test compaction with Hybrid strategy +- [ ] Test fallback on LLM failure + +### Manual Testing +- [ ] Compact conversation with 50 messages +- [ ] Verify tool call atomicity preserved +- [ ] Verify reasoning chain preserved +- [ ] Compare Extract vs Hybrid output quality + +--- + +## Effort Estimate + +| Phase | Tasks | Estimated Hours | +|-------|-------|-----------------| +| Phase 1 | 3 | 4h | +| Phase 2 | 2 | 3h | +| Phase 3 | 3 | 8h | +| Phase 4 | 1 | 2h | +| Phase 5 | 1 | 1h | +| Phase 6 | 2 | 2h | +| **Total** | **12** | **20h** | + +--- + +## Dependencies + +- None (self-contained enhancement) + +## Blockers + +- None identified + +--- + +## Notes + +- LLM summarization should use cheap model by default (haiku-3.5) +- All new features gated behind config flags for backward compatibility +- Compaction should still work if LLM provider unavailable (fallback to extract) diff --git a/plans/2026-05-02-compaction-enhancement-v1.md b/plans/2026-05-02-compaction-enhancement-v1.md new file mode 100644 index 0000000000..01d85a17b4 --- /dev/null +++ b/plans/2026-05-02-compaction-enhancement-v1.md @@ -0,0 +1,629 @@ +# Forgecode Compaction System Enhancement Plan + +## Objective + +Enhance the forgecode context compaction system from a purely structural extraction approach to a hybrid system that combines **intelligent pre-processing**, **LLM-based semantic summarization**, and **adaptive eviction strategies** to maximize context retention of meaningful information while maintaining deterministic performance. + +--- + +## SOTA Research Summary + +### Current Industry Approaches + +| Approach | Provider | Characteristics | +|----------|----------|----------------| +| **Structural Extraction** | Current forgecode | Fast, deterministic, low semantic fidelity | +| **LLM Summarization** | Claude Code, OpenAI Agents | High fidelity, slow (~500ms+), expensive | +| **Hybrid Extraction** | Microsoft Copilot | Combines extraction + LLM refinement | +| **Importance Scoring** | Cursor AI | Scores messages by relevance, preserves high-value | +| **Incremental Summarization** | Perplexity AI | Accumulates summaries, reduces redundancy | +| **Semantic Chunking** | LangChain | Groups semantically similar content | + +### Key Findings from Anthropic Documentation + +1. **Compaction timing is critical**: Trigger at 70-80% of context window to preserve headroom +2. **Tool call atomicity**: Never split tool calls from their results +3. **Extended thinking preservation**: Reasoning chains must be maintained for model continuity +4. **Summary quality matters**: Poor summaries degrade subsequent model performance + +### Best Practices Identified + +1. **Pre-compaction filtering**: Remove noise before summarization +2. **Adaptive eviction windows**: More aggressive near context limits +3. **Importance-based preservation**: High-value messages protected from eviction +4. **Structured summaries**: Machine-parseable formats improve downstream processing +5. **Cost-latency tradeoff**: Cheaper models can be used for summarization + +--- + +## Implementation Plan + +### Phase 1 — Enhanced Configuration (`forge_config` + `forge_domain`) + +#### Task 1: Extend `CompactConfig` with new options + +**Files:** `crates/forge_config/src/compact.rs`, `crates/forge_domain/src/compact/compact_config.rs` + +```rust +// New fields in CompactConfig +pub struct Compact { + // ... existing fields ... + + /// Strategy for summarization: extract only, llm, or hybrid + #[serde(default)] + pub summarization_strategy: SummarizationStrategy, + + /// Enable pre-compaction filtering + #[serde(default)] + pub enable_prefilter: bool, + + /// Enable adaptive eviction window + #[serde(default)] + pub enable_adaptive_eviction: bool, + + /// Enable importance-based preservation + #[serde(default)] + pub enable_importance_scoring: bool, + + /// Maximum tokens in generated summary + #[serde(default)] + pub summary_max_tokens: Option, +} + +pub enum SummarizationStrategy { + /// Pure structural extraction (current behavior) + Extract, + /// LLM-based semantic summarization + Llm, + /// Hybrid: extract then refine with LLM + Hybrid, +} +``` + +#### Task 2: Add `CompactionHistory` for incremental tracking + +**Files:** `crates/forge_domain/src/compact/history.rs`, `crates/forge_domain/src/compact/mod.rs` + +```rust +#[derive(Default, Clone, Serialize, Deserialize)] +pub struct CompactionHistory { + /// Content hashes of past summaries to detect redundancy + pub summary_hashes: Vec, + /// Last seen file versions (path -> hash) + pub file_versions: HashMap, + /// Count of successful compactions + pub compaction_count: usize, + /// Total tokens reduced across all compactions + pub total_tokens_reduced: usize, +} +``` + +#### Task 3: Add `ImportanceScore` to messages + +**Files:** `crates/forge_domain/src/context.rs` + +```rust +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MessageImportance { + /// Base importance score (0-100) + pub score: u8, + /// Factors contributing to score + pub factors: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum ImportanceFactor { + HasToolCalls, + HasErrors, + HasFileChanges, + HasUserIntent, + ReasoningChain, + Decision, +} +``` + +--- + +### Phase 2 — Enhanced Eviction Strategy (`forge_domain`) + +#### Task 4: Implement adaptive eviction window + +**Files:** `crates/forge_domain/src/compact/strategy.rs` + +```rust +impl CompactionStrategy { + /// Calculate adaptive eviction percentage based on context state + pub fn adaptive_eviction(&self, context: &Context, threshold: usize) -> f64 { + let token_count = context.token_count(); + let ratio = token_count as f64 / threshold as f64; + + // Eviction aggressiveness increases as we approach threshold + match ratio { + r if r > 0.95 => 0.5, // 50% - critical zone + r if r > 0.85 => 0.35, // 35% - warning zone + r if r > 0.70 => 0.2, // 20% - normal + _ => 0.1, // 10% - conservative + } + } +} +``` + +#### Task 5: Implement importance-based message scoring + +**Files:** `crates/forge_domain/src/compact/importance.rs` + +```rust +impl MessageImportance { + pub fn calculate(msg: &ContextMessage) -> Self { + let mut score: u8 = 50; // Base score + let mut factors = Vec::new(); + + match msg.deref() { + ContextMessage::Text(t) => { + if t.tool_calls.is_some() { + score += 20; + factors.push(ImportanceFactor::HasToolCalls); + } + if t.reasoning_details.is_some() { + score += 15; + factors.push(ImportanceFactor::ReasoningChain); + } + } + ContextMessage::Tool(r) if r.is_error() => { + score = 100; // Critical + factors.push(ImportanceFactor::HasErrors); + } + _ => {} + } + + Self { score, factors } + } + + /// Minimum importance required to survive compaction + pub const MIN_SURVIVAL_SCORE: u8 = 60; +} +``` + +#### Task 6: Enhanced eviction range finding with importance + +**Files:** `crates/forge_domain/src/compact/strategy.rs` + +```rust +fn find_eviction_range_with_importance( + context: &Context, + max_retention: usize, + history: &CompactionHistory, +) -> Option<(usize, usize)> { + let messages = &context.messages; + + // Filter out high-importance messages from eviction candidates + let eviction_candidates: Vec = messages + .iter() + .enumerate() + .filter(|(_, msg)| { + let importance = MessageImportance::calculate(msg); + importance.score < MessageImportance::MIN_SURVIVAL_SCORE + }) + .map(|(i, _)| i) + .collect(); + + // Find range using only eviction candidates + find_sequence_preserving_last_n(context, max_retention) + .map(|(start, end)| { + // Adjust range to exclude protected messages + let protected: Vec = messages + .iter() + .enumerate() + .filter(|(_, msg)| { + let importance = MessageImportance::calculate(msg); + importance.score >= MessageImportance::MIN_SURVIVAL_SCORE + }) + .map(|(i, _)| i) + .collect(); + + // If protected messages fall in eviction range, shrink it + let new_start = protected.iter().find(|&&i| i >= start).copied().unwrap_or(start); + (new_start.max(start), end) + }) +} +``` + +--- + +### Phase 3 — LLM Summarization (`forge_app`) + +#### Task 7: Create summarization prompt template + +**Files:** `templates/forge-summarization-prompt.md` (new) + +```markdown +You are a precise code assistant summarizing previous conversation context. + +## Task +Summarize the following conversation history into a concise, structured format that preserves: +1. Key decisions and their rationale +2. Files modified and their purposes +3. Tool operations performed and their outcomes +4. Important constraints or requirements discovered + +## Format +Provide a summary with these sections: + +### Decisions +- [List key architectural/implementation decisions] + +### Files Changed +- `path/to/file`: Brief description of changes + +### Operations Summary +- **Read**: [files read and why] +- **Write/Modify**: [files changed and what] +- **Execute**: [commands run and outcomes] +- **Search**: [patterns searched and findings] + +### Discovered Constraints +- [Any limitations, requirements, or context important for continuation] + +### Current State +- [Where work left off, what's next] + +## Conversation to Summarize +{{conversation}} +``` + +#### Task 8: Implement `LlmSummarizer` service + +**Files:** `crates/forge_app/src/services/summarizer.rs`, `crates/forge_app/src/lib.rs` + +```rust +pub struct LlmSummarizer { + provider: Arc, + template_engine: TemplateEngine, + compact_config: Compact, +} + +impl LlmSummarizer { + pub async fn summarize( + &self, + context: &Context, + history: &CompactionHistory, + ) -> anyhow::Result { + // Render summarization prompt + let prompt = self.template_engine.render( + "forge-summarization-prompt.md", + &serde_json::json!({ + "conversation": self.extract_conversation_text(context), + "history_summary": self.summarize_history(history), + }), + )?; + + // Create summary context + let summary_context = Context::default() + .add_message(ContextMessage::user(prompt, None)); + + // Use compact model if configured, otherwise agent model + let model = self.compact_config.model.as_ref() + .cloned() + .unwrap_or_else(|| ModelId::new("claude-3-5-haiku")); + + // Generate summary + let response = self.provider.chat(&model, summary_context).await?; + self.collect_content(response).await + } + + fn extract_conversation_text(&self, context: &Context) -> String { + // Convert context to readable text format + context.messages.iter() + .map(|msg| format_message(msg)) + .collect::>() + .join("\n\n") + } +} +``` + +#### Task 9: Integrate summarization into Compactor + +**Files:** `crates/forge_app/src/compact.rs` + +```rust +impl Compactor { + pub fn compact(&self, context: Context, max: bool) -> anyhow::Result { + let strategy = self.build_strategy(&context, max); + + match strategy.eviction_range(&context) { + Some(sequence) => { + match self.compact.summarization_strategy { + SummarizationStrategy::Extract => { + self.compress_single_sequence(context, sequence) + } + SummarizationStrategy::Llm => { + self.compress_with_llm(context, sequence).await + } + SummarizationStrategy::Hybrid => { + // Extract first, then refine with LLM + let extracted = self.compress_single_sequence(context.clone(), sequence)?; + self.refine_summary(&extracted).await + } + } + } + None => Ok(context), + } + } + + async fn compress_with_llm( + &self, + mut context: Context, + sequence: (usize, usize), + ) -> anyhow::Result { + let (start, end) = sequence; + + // Extract the sequence for summarization + let sequence_context = context + .messages + .get(start..=end) + .map(|slice| slice.to_vec()) + .unwrap_or_default(); + + // Create temporary context for LLM + let temp_context = Context::default().messages(sequence_context); + + // Get LLM summary + let llm_summary = self.summarizer.summarize(&temp_context, &self.history).await?; + + // Apply transformers to the extracted summary + let summary = self.transform(ContextSummary::from(&temp_context)); + + // Combine LLM summary with structured summary + let combined_summary = format!( + "{}\n\n## Structured Operations\n{}", + llm_summary, + self.render_structured_summary(&summary) + ); + + // Replace range with summary + let summary_entry = MessageEntry::from(ContextMessage::user(combined_summary, None)); + context.messages.splice(start..=end, std::iter::once(summary_entry)); + + // Update history + self.history.record_compaction(&context); + + Ok(context) + } + + async fn refine_summary(&self, context: &Context) -> anyhow::Result { + // Light LLM refinement of already-extracted summary + // (Implementation details) + Ok(context.clone()) + } +} +``` + +--- + +### Phase 4 — Pre-Compaction Filtering (`forge_app`) + +#### Task 10: Implement pre-compaction filters + +**Files:** `crates/forge_app/src/transformers/prefilter.rs` + +```rust +pub struct PreCompactionFilter { + /// Minimum length for tool results (shorter = likely empty/error) + pub min_tool_result_length: usize, + /// Patterns for debug output to strip + pub debug_patterns: Vec, +} + +impl PreCompactionFilter { + pub fn filter(&self, context: &mut Context) { + context.messages.retain(|msg| { + match msg.deref() { + ContextMessage::Tool(r) => { + // Keep tool results above minimum length + r.output.text_len() >= self.min_tool_result_length + } + ContextMessage::Text(t) => { + // Filter out debug output patterns + !self.debug_patterns.iter().any(|p| p.is_match(&t.content)) + } + _ => true + } + }); + } + + /// Collapse duplicate consecutive tool calls (same tool, same args) + pub fn collapse_duplicates(&self, context: &mut Context) { + let mut deduped = Vec::new(); + let mut prev_call: Option<(String, String)> = None; + + for msg in context.messages.drain(..) { + if let ContextMessage::Text(t) = msg { + if let Some(calls) = &t.tool_calls { + for call in calls { + let key = (call.name.to_string(), call.arguments.to_string()); + if prev_call.as_ref() != Some(&key) { + prev_call = Some(key); + deduped.push(ContextMessage::Text(t)); + } + } + } else { + deduped.push(ContextMessage::Text(t)); + } + } else { + deduped.push(msg); + } + } + + context.messages = deduped; + } +} +``` + +--- + +### Phase 5 — Enhanced Summary Template (`forge_app`) + +#### Task 11: Create enhanced summary frame + +**Files:** `templates/forge-partial-summary-frame-v2.md` + +```markdown +{{#if structured}} +## Prior Context Summary + +**Files Modified:** +{{#each files}} +- `{{path}}`: {{description}} +{{/each}} + +**Operations:** +- **Reads**: {{read_count}} files +- **Writes/Modifies**: {{write_count}} files +- **Executions**: {{executions}} +- **Searches**: {{searches}} + +{{#if decisions}} +**Key Decisions:** +{{#each decisions}} +- {{this}} +{{/each}} +{{/if}} + +{{#if constraints}} +**Constraints Discovered:** +{{#each constraints}} +- {{this}} +{{/each}} +{{/if}} + +**Progress:** {{completed_tasks}}/{{total_tasks}} tasks completed +{{/if}} + +{{#if llm_summary}} +{{llm_summary}} +{{/if}} + +--- +*This summary was generated from {{compaction_count}} previous compaction(s).* +{{/if}} + +Proceed with implementation based on this context. +``` + +--- + +### Phase 6 — Metrics & Observability + +#### Task 12: Add compaction metrics collection + +**Files:** `crates/forge_domain/src/compact/metrics.rs` + +```rust +#[derive(Default, Clone, Serialize, Deserialize)] +pub struct CompactionMetrics { + /// Number of times compaction triggered + pub compaction_count: usize, + /// Total tokens reduced + pub total_tokens_reduced: usize, + /// Average token reduction per compaction + pub avg_token_reduction: f64, + /// Total messages reduced + pub total_messages_reduced: usize, + /// Compaction strategies used + pub strategies_used: HashMap, + /// Errors encountered + pub errors: Vec, +} + +impl CompactionMetrics { + pub fn record(&mut self, result: &CompactionResult, strategy: &str) { + self.compaction_count += 1; + self.total_tokens_reduced += + result.original_tokens.saturating_sub(result.compacted_tokens); + self.total_messages_reduced += + result.original_messages.saturating_sub(result.compacted_messages); + *self.strategies_used.entry(strategy.to_string()).or_insert(0) += 1; + } +} +``` + +--- + +## Verification Criteria + +1. **Functional correctness:** + - [ ] Compaction triggers at configured thresholds + - [ ] Tool calls remain atomic after compaction + - [ ] Extended thinking reasoning preserved + - [ ] Usage accumulation works correctly + - [ ] Droppable messages removed + +2. **Enhanced features:** + - [ ] Adaptive eviction adjusts based on context ratio + - [ ] Importance scoring protects high-value messages + - [ ] LLM summarization produces coherent summaries + - [ ] Pre-filter removes noise before compaction + - [ ] History tracking prevents redundant summaries + +3. **Performance:** + - [ ] Structural extraction: <5ms + - [ ] LLM summarization: <2s with timeout + - [ ] No memory leaks from history accumulation + +4. **Backward compatibility:** + - [ ] Existing `compact` config remains valid + - [ ] Default behavior unchanged (structural extraction) + - [ ] Migration path for existing conversations + +--- + +## Potential Risks and Mitigations + +| Risk | Impact | Mitigation | +|------|--------|------------| +| LLM summarization adds latency | Medium | Use cheaper models (haiku), cache summaries, timeout after 3s | +| Poor LLM summary quality | High | Fallback to structural extraction, validate summary format | +| History accumulation memory growth | Low | Limit history size, compress older entries | +| Importance scoring misclassification | Medium | Allow configuration of thresholds, provide defaults | +| Adaptive eviction too aggressive | Low | Provide conservative defaults, allow tuning | + +--- + +## Alternative Approaches + +1. **Pure LLM Approach**: Use LLM for all summarization, skip structural extraction + - Pros: Higher semantic fidelity + - Cons: Slower, more expensive, less deterministic + +2. **Semantic Embedding Approach**: Use embeddings to find and preserve semantically important messages + - Pros: Better relevance scoring + - Cons: Requires embedding service, more complex + +3. **Streaming Compaction**: Compact incrementally as context grows, not at threshold + - Pros: More predictable latency, smoother context growth + - Cons: More complex state management + +4. **Multi-Model Cascade**: Start with extraction, escalate to LLM for complex contexts + - Pros: Balances cost and quality + - Cons: Most complex implementation + +--- + +## Phased Rollout + +| Phase | Features | Risk Level | Duration | +|-------|----------|------------|----------| +| Phase 1 | Config extensions, adaptive eviction | Low | 1 week | +| Phase 2 | Importance scoring, pre-filtering | Low | 1 week | +| Phase 3 | LLM summarization (opt-in) | Medium | 2 weeks | +| Phase 4 | Metrics, observability | Low | 1 week | +| Phase 5 | Template improvements | Low | 1 week | + +--- + +## References + +- Anthropic Context Windows Documentation +- OpenAI Conversation State Management +- Microsoft Copilot Context Management +- LangChain Context Management Strategies diff --git a/trufflehog.yml b/trufflehog.yml new file mode 100644 index 0000000000..99787ed523 --- /dev/null +++ b/trufflehog.yml @@ -0,0 +1,14 @@ +version: 1 +roots: + - path: . + scan_depth: 4 + exclude_paths: + - "*.lock" + - "**/node_modules/**" + - "**/__pycache__/**" + - "**/.venv/**" + - "**/target/**" + - "**/.git/**" + detectors: + - allowlist: false +