diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 000000000..73d83521e --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,65 @@ +name: Publish documentation site + +# Builds the docToolchain microsite (./dtcw generateSite) and deploys it +# to GitHub Pages. Requires repository Settings -> Pages -> Source set to +# "GitHub Actions". + +on: + push: + branches: [main] + paths: + - 'src/docs/**' + - 'src/site/**' + - 'docToolchainConfig.groovy' + - 'dtcw' + - '.github/workflows/publish-docs.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment; let an in-progress run finish. +concurrency: + group: github-pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + env: + # Used by the microsite for "edit on GitHub" links. + DTC_PROJECT_BRANCH: ${{ github.ref_name }} + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + # docToolchain reads git history for page metadata. + fetch-depth: 0 + + - name: Generate microsite with docToolchain + # dtcw auto-detects Docker on the runner and uses the + # doctoolchain/doctoolchain image — no local JDK needed. + run: | + chmod +x ./dtcw + ./dtcw generateSite + + - name: Configure GitHub Pages + uses: actions/configure-pages@v5 + + - name: Upload site artifact + uses: actions/upload-pages-artifact@v3 + with: + path: build/microsite/output + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 0c1ec6378..5429709da 100644 --- a/.gitignore +++ b/.gitignore @@ -206,3 +206,5 @@ tests/playground/* # Snapshot test report (generated by pytest-textual-snapshot) snapshot_report.html + +.gradle/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..3a2fb341b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,182 @@ + +## Specification + +When we talk about a "specification" or "spec", we mean: +- Persona Use Cases in Cockburn's Fully Dressed format (Primary Actor, Trigger, Main Success Scenario, Extensions, Postconditions) at User Goal level, with Business Rules (BR-IDs) +- System Use Cases for each technical interface (API endpoint, CLI command, event, file format): input/validation, processing, output/status codes, error responses +- Activity Diagrams for all flows (not just the happy path) +- Acceptance criteria in Gherkin format (Given/When/Then) +- Individual requirements in EARS syntax where applicable (When/While/If/Shall) +- Supplementary Specifications as needed: Entity Model, State Machines, Interface Contracts, Validation Rules + +## Requirements Discovery + +Clarify requirements using the Socratic Method: +- Ask at most 3 questions at a time, challenge assumptions +- Use MECE to ensure questions cover all areas without overlap +- Keep asking until you fully understand the requirements + +Frame the scope before writing it down: +- Impact Mapping connects deliverables to business goals and actors — so you build what moves a goal, not just what was asked. +- User Story Mapping lays stories along the user's journey and exposes a coherent first slice. + +Document the result as a PRD (problem, goals, personas, success criteria, scope). + +## Architecture Documentation + +Architecture documentation follows arc42. Scaffold the arc42 "with-help" template into the project's `src/docs/` via docToolchain `downloadTemplate` rather than restating chapter structure here — each chapter's help text is its structural spec, which the process fills and then replaces. + +Every context, building-block and runtime chapter carries at least one diagram. Diagrams are PlantUML, not Mermaid; building blocks use C4 via PlantUML's bundled C4-PlantUML standard library — the `!include ` stdlib form (angle brackets), never the remote `https://` URL and never vendored file copies. Not generic boxes. + +Decisions are ADRs (Nygard) with a 3-point Pugh Matrix (-1/0/+1). When the rationale is unconfirmed, ADR Status is "Accepted (inferred)" and Pugh cells needing team judgment are marked `?` rather than guessed. Each ADR's Consequences name the risks the decision creates, referencing the Chapter 11 risk IDs (R-NNN); a decision that creates a risk not yet in Chapter 11 either adds it there or records the consequence as explicitly accepted without a tracked risk. Conversely, Chapter 8 concepts back-reference the ADR that decided them. + +Cross-section traceability — arc42 templates do not enforce these, so the contract does: +- Every Chapter 1.2 quality goal maps to a named approach in Chapter 4. +- The external systems in Chapter 3 (context) and the Chapter 5 Level-1 building-block view are the same set — one system boundary in both. +- Every Chapter 5 building block appears in at least one Chapter 6 runtime scenario; Chapter 6 includes at least one error/recovery scenario, not only the happy path. +- Chapter 9 carries an in-document ADR index (ADR | Title | Status), even when the ADRs live in a separate register. +- Each Chapter 5 building block states responsibility, interface, and source location. + +Chapter 1.2 lists only the top 3-5 quality goals — the ones that drive architecture decisions. Chapter 10 may elaborate further quality characteristics beyond those top goals; that is correct arc42, not a defect. The Chapter 10 quality tree marks each characteristic as either concretising a Chapter 1.2 top goal or as a derived quality requirement, and each Chapter 10 quality scenario cross-links back to the Chapter 1.2 goal it concretises (or is marked "derived"). Each Chapter 10 scenario is written in the six-part quality attribute scenario form (Source, Stimulus, Artifact, Environment, Response, Response Measure); the Response Measure carries a literal figure, so the requirement is testable rather than an adjective. + +Chapter 11 separates Risks from Technical Debt into two subsections. Each Risk carries probability, impact, a derived priority, and a mitigation/action cross-referencing an existing mitigation in Chapter 8 or a quality scenario where one exists; risks are ordered by priority. Each Technical Debt item references the specific Chapter 5 building block it burdens. + +## Crosscutting Concepts + +arc42 leaves Chapter 8 open. We require five baseline crosscutting concepts, in this order: + +- 8.1 Threat Model — STRIDE; threats get IDs (T-001…). +- 8.2 Security — every mitigation references the T-IDs it closes. +- 8.3 Test — testing pyramid; tests trace to Use Cases and Business Rules. +- 8.4 Observability — logs, metrics, traces, audit trails. +- 8.5 Error Handling — retry, circuit breaker, fallback, recovery. + +Add further Chapter 8.x concepts (persistence, i18n, accessibility, configuration, performance) only when the system actually has that concern. + +## Layer Boundaries + +At every layer boundary: +- Expose only well-defined DTOs and contracts — never domain entities +- Use explicit mapping at every seam +- Apply Anti-Corruption Layers when integrating external systems +- Dependency direction points inward (DIP) + +## Backlog Management + +Create EPICs and User Stories as GitHub issues from the specification. +- User Stories follow INVEST criteria (Independent, Negotiable, Valuable, Estimable, Small, Testable) +- Prioritize with MoSCoW (Must/Should/Could/Won't) +- Mark dependencies between issues +- Groom the backlog regularly as the project evolves + +## Vertical Slicing + +Build the first increment as a walking skeleton: a deployable end-to-end slice that wires every architectural layer together and does almost nothing else. + +Grow the system as thin vertical slices — each slice cuts through all layers and delivers one small piece of user value. Slices are tracer bullets: kept and refined, never thrown away. + +When a technical unknown blocks a slice, run a spike solution first — a timeboxed, throwaway experiment that removes the risk. Spike code is discarded; only its lesson carries into the slice. + +## Implement Next + +For each issue: +- Create a feature branch for the EPIC +- Select next issue from backlog (respect dependencies) +- Analyze and document analysis as a comment on the issue +- Implement using TDD (London or Chicago School as appropriate) +- Each test references its Use Case ID for traceability +- Commit with Conventional Commits, reference issue number +- Check if spec or architecture docs need updating +- When EPIC is complete, create a Pull Request + +## Refactoring + +Refactoring targets are named code smells, not a vague urge to "clean up". + +For any refactoring that does not complete in one step, use the Mikado Method: attempt the change, note what breaks, revert, and do the prerequisites first — never leave the build broken while you dig. + +Refactoring commits change structure only. Behaviour changes go in separate commits, and the test suite stays green at every commit. + +## Code Quality + +Our code follows: +- SOLID principles +- DRY, KISS +- Ubiquitous Language from Domain-Driven Design (same terms in code as in the specification) + +## Quality Review + +Quality assurance follows three layers: +- Code review using Fagan Inspection (structured, systematic, with defined phases) +- Security review based on OWASP Top 10 +- Architecture review using ATAM (scenario-based tradeoff analysis against quality goals) +- Use a different AI model or fresh session for reviews to avoid blind spots + +## Docs-as-Code + +Documentation follows Docs-as-Code according to Ralf D. Müller: +- AsciiDoc as format, PlantUML for inline diagrams, built by docToolchain +- Version-controlled, peer-reviewed, and built automatically +- Plain English according to Strunk & White (or Gutes Deutsch nach Wolf Schneider) +- Projects following this contract include the `dtcw` wrapper and `docToolchainConfig.groovy` so PlantUML / AsciiDoc actually render. + +## Socratic Code Theory Recovery + +Recover a program's "theory" (Naur 1985) from source code through recursive question refinement. + +- Start with 5 root questions: Q1 Problem/Users, Q2 Specification, Q3 Architecture, Q4 Quality Goals, Q5 Risks. + +- The second level of the tree is FIXED, not free. Every run emits exactly these nodes, in this order, even when a node's only leaf is [OPEN] or [ANSWERED: not applicable]: + - Q1.1-Q1.6: product identity, primary users, channels, why-built, success metrics, segment priority + - Q2.1-Q2.6: actors, use-case catalog, per-interface system specs, data/entity model, acceptance criteria, cross-cutting business rules + - Q3.1-Q3.12: the twelve arc42 chapters, in arc42 order + - Q4.1-Q4.8: the eight ISO/IEC 25010 characteristics; plus Q4.9: which characteristic has priority + - Q5.1-Q5.5: technical debt, security risks, operational risks, dependency/supply-chain risks, scaling/performance risks + +- Below the fixed second level, decompose adaptively and code-driven; a node is a leaf only when it can be answered from one specific file:line evidence (a directory is too coarse — decompose further) or definitively marked [OPEN]. Depth tracks code density: a small bounded context yields a shallow tree, a large one a deep tree, capped at four levels below a fixed node. Depth varies between runs — expected. + +- Q-IDs are stable: Q3.7 is always Deployment View, in every run, so trees from different runs can be diffed node-by-node. + +- Each leaf is [ANSWERED] (with file:line evidence) or [OPEN] (with Category, Ask role, and why it is unanswerable from code). + +- Quality is not wholly team knowledge. Derive quality scenarios for the Q4 branch and arc42 Chapter 10 from measurable code behaviour — literal thresholds, timeouts, budgets, the threat catalogue and test concept from Q3.8 — as [ANSWERED] with file:line; never invent target numbers. Only the quality-goal ranking (Q4.9) is [OPEN]. arc42 Chapter 10 carries the derivable scenarios, never just an [OPEN] pointer. Chapter 1.2 names only the top 3-5 quality goals; Chapter 10 covers all eight characteristics — mark each Chapter 10 entry as concretising a Chapter 1.2 top goal or as derived. + +- Open Questions are the handoff document: always emit one section per role (Product Owner, Architect, Developer, Domain Expert, Operations), even when a section is empty ("No open questions for this role"). + +- Two-phase workflow: Phase 1 builds the tree; the team answers the Open Questions; Phase 2 synthesizes documentation from the answered tree. + +## Concise Response (TLDR) + +Responses lead with the conclusion first (BLUF). Keep to essential points. No filler, no preamble. Use short sentences, active voice, and no unnecessary words (Strunk & White). + +## Simple Explanation (ELI5) + +Explain complex concepts using simple language and everyday analogies. When the explanation feels hard to write, that reveals gaps in understanding — study those areas first (Feynman Technique). + +## Writing Style + +Writing follows Gutes Deutsch nach Wolf Schneider (or Plain English according to Strunk & White). + +Additionally: +- Technical terms stay in English (LLM, Prompt, Token, Spec, etc.) +- Address the reader directly, use first person sparingly but deliberately +- Use analogies to human thinking to explain technical concepts +- One thought per paragraph (5-8 sentences is fine) +- Section headings are statements, not topic announcements +- First sentence says what the paragraph is about +- Show code and prompts, don't just claim things work +- Conclusions make a clear statement — never end with 'it remains exciting' + +--- +Generated from https://llm-coding.github.io/Semantic-Anchors/#/contracts + +## Docs-as-Code +- docToolchain reads its sources from src/docs/. Write all + documentation there. +- Fill the arc42 template at src/docs/arc42/; do not create a + standalone arc42 file. Write each chapter into its section + file, set the document title to the system name, and remove + the generic "About arc42" help chapter. +- PRD and specification go directly under src/docs/. +- Cross-references must resolve inside src/docs/; do not point + at the Question Tree or the repo-root docs/ folder. diff --git a/OPEN_QUESTIONS.adoc b/OPEN_QUESTIONS.adoc new file mode 100644 index 000000000..5ad2cc382 --- /dev/null +++ b/OPEN_QUESTIONS.adoc @@ -0,0 +1,230 @@ += Open Questions — Vibe +:doctype: book +:toc: left + +// Socratic Code-Theory Recovery — Phase 1 handoff +// These are the [OPEN] leaves of QUESTION_TREE.adoc — the questions the +// code cannot answer. Answer them inline under each *Your answer:* block. +// Mark a deliberate non-answer as: (deferred — ). +// Phase 2 runs only after every question has an answer or a deferral. + +== For Product Owner + +=== Q1.2.2: Which user segment is the priority? +Category: business-context + +Code treats the interactive developer, the programmatic scripter and the +ACP/IDE integrator as peer entry points. Which segment drives the roadmap? + +*Your answer:* +_(write here)_ + +=== Q1.4: Why was Vibe built? +Category: business-context + +The code shows what Vibe does, not why Mistral built an open-source CLI +agent — the strategic goal and its relation to the paid Mistral Code / +Vibe Code offering. + +*Your answer:* +_(write here)_ + +=== Q1.5.2: What business KPI defines success? +Category: business-context + +Telemetry collects usage events, but the code does not say which metric +is the target (adoption, retention, task-completion rate, conversion to +paid plans). + +*Your answer:* +_(write here)_ + +=== Q1.6: How are user segments prioritised for features? +Category: business-context + +The plan-offer subsystem distinguishes free vs. paid users, but no +feature-prioritisation order across segments is derivable from code. + +*Your answer:* +_(write here)_ + +=== Q2.2.UC.completeness: Is the recovered use-case catalog complete? +Category: business-context + +The catalog was recovered from entrypoints and tools. Is it the intended +complete set of user goals, and which use cases are core vs. experimental? + +*Your answer:* +_(write here)_ + +=== Q2.5.2: What are the acceptance criteria per use case? +Category: business-context + +No Given/When/Then criteria exist in code; tests are not traced to use +cases. What defines "done" for each user goal? + +*Your answer:* +_(write here)_ + +=== Q2.6.BR.intent: Are the cross-cutting rules requirements or choices? +Category: business-context + +Trust gate, tool approval, agent-safety tiers and telemetry gating are +recovered as mechanisms. Which are hard product requirements vs. +implementation choices (e.g. is the workdir boundary a security +guarantee)? + +*Your answer:* +_(write here)_ + +=== Q4.9: Which quality characteristic has priority? +Category: quality-goals + +The "Safety first" theme suggests Security/Usability lead, but the code +cannot rank the eight ISO 25010 characteristics. Please order them. + +*Your answer:* +_(write here)_ + +== For Architect + +=== Q3.4.2: Why were the core architectural strategies chosen? +Category: design-rationale + +Hexagonal ports, an LLM backend factory, a middleware pipeline and a +layered config merge are visible — but not the rationale or the +alternatives weighed. + +*Your answer:* +_(write here)_ + +=== Q3.8.1: What is the STRIDE threat model? +Category: design-rationale + +No threat model or threat IDs exist. Security mechanisms are present but +not mapped to enumerated threats. Please supply (or commission) a STRIDE +model so mitigations can reference it. + +*Your answer:* +_(write here)_ + +=== Q3.9.ADR.HexagonalPorts: Why hexagonal architecture? +Category: design-rationale + +`*_port.py` ports/adapters are used pervasively, with no ADR recording +why or what alternatives were considered. + +*Your answer:* +_(write here)_ + +=== Q3.9.ADR.LLMBackendAbstraction: Why support non-Mistral providers? +Category: design-rationale + +The backend factory supports Mistral, OpenAI-compatible, Anthropic and +Vertex. Why support non-Mistral providers in a Mistral-branded tool, and +what are the trade-offs? + +*Your answer:* +_(write here)_ + +=== Q3.9.ADR.MiddlewarePipeline: Why a bespoke middleware pipeline? +Category: design-rationale + +A custom pipeline enforces turn/price limits, auto-compaction and +read-only modes. Why a bespoke pipeline rather than SDK callbacks or +inline checks? + +*Your answer:* +_(write here)_ + +=== Q3.9.ADR.SessionsAsFiles: Why folder + JSONL session storage? +Category: design-rationale + +Sessions persist as a folder with `meta.json` + `messages.jsonl`, with a +v1→v2 migration. Why flat files over a database, and what triggered the +format change? + +*Your answer:* +_(write here)_ + +=== Q4.9: Which quality characteristic has priority? +Category: quality-goals + +(Also routed to Product Owner.) Please rank the eight ISO 25010 +characteristics from the architecture's standpoint. + +*Your answer:* +_(write here)_ + +=== Q5.1: How should the technical-debt items be ranked? +Category: design-rationale + +Code-visible debt: rough `session_cost` estimate, `acp/tools/` duplicating +`core/tools/` builtins, the v1→v2 session migration, the large untiered +TUI widget layer, experimental voice mode. Which are urgent? + +*Your answer:* +_(write here)_ + +== For Developer + +=== Q2.5.2: What are the acceptance criteria per use case? +Category: business-context + +(Also routed to Product Owner.) Tests exist but are not traced to use +cases or business rules. Should they be, and what is the canonical +"done" definition per user goal? + +*Your answer:* +_(write here)_ + +=== Q5.1: How should the technical-debt items be ranked? +Category: design-rationale + +(Also routed to Architect.) From a maintenance standpoint, which debt +items most slow down day-to-day work? + +*Your answer:* +_(write here)_ + +== For Domain Expert + +=== Q2.2.UC.completeness: Is the recovered use-case catalog complete? +Category: business-context + +(Also routed to Product Owner.) Does the catalog match the real user +workflows, and is any user goal missing? + +*Your answer:* +_(write here)_ + +=== Q2.6.BR.intent: Are the cross-cutting rules requirements or choices? +Category: business-context + +(Also routed to Product Owner.) From the domain's standpoint, which of +the trust / approval / safety rules are non-negotiable invariants? + +*Your answer:* +_(write here)_ + +== For Operations + +=== Q3.8.1: What is the STRIDE threat model? +Category: design-rationale + +(Also routed to Architect.) From an operations standpoint, what threats +matter for a tool that runs shell commands and fetches URLs on a +developer's machine? + +*Your answer:* +_(write here)_ + +=== Q5.3: How should LLM / service outages behave? +Category: design-rationale + +The LLM backend retries with backoff and the experiments client fails +open, but the intended behaviour on a sustained Mistral API outage +(beyond retry exhaustion) is not specified in code. + +*Your answer:* +_(write here)_ diff --git a/QUESTION_TREE.adoc b/QUESTION_TREE.adoc new file mode 100644 index 000000000..a36ff0db5 --- /dev/null +++ b/QUESTION_TREE.adoc @@ -0,0 +1,772 @@ += Question Tree — Vibe +:doctype: book +:toc: left +:toclevels: 3 + +// Socratic Code-Theory Recovery — Phase 1 +// Bounded context: vibe/ (the mistral-vibe Python package) +// Human-readable name: Vibe +// Generated: 2026-05-22 + +[NOTE] +==== +*Bounded-context scope warning.* `vibe/` is a large bounded context +(~190 Python modules) that internally spans four sub-contexts: the +`core` engine (agent loop, tools, LLM backends, config), the `cli` +Textual TUI, the `acp` Agent Client Protocol bridge, and `setup` +(first-run wizards). It was recovered in one pass at the user's explicit +request. The fixed second level (Q1.1–Q5.5) below is complete; adaptive +depth is uneven because the engine carries far more recoverable theory +than the UI shell. A future run per sub-context would yield deeper, +more even trees. Several Q3 chapters note this explicitly. +==== + +== Q1: What problem does this bounded context solve, and for whom? + +=== Q1.1: Product identity — what is this bounded context? +[ANSWERED] +Evidence: pyproject.toml:5, pyproject.toml:143-144, README.md:20-22, vibe/core/skills/builtins/vibe.py:5-8 +Vibe is a "Minimal CLI coding agent by Mistral" — a terminal coding +assistant powered by Mistral models. It ships two entry points: +`vibe` (interactive Textual TUI / programmatic mode) and `vibe-acp` +(an Agent Client Protocol server for editors/IDEs). + +=== Q1.2: Who are the primary users? + +==== Q1.2.1: Which actor roles does the code serve? +[ANSWERED] +Evidence: README.md:228-262, vibe/core/programmatic.py:27, vibe/acp/entrypoint.py:78-108 +Three operator roles are visible in code paths: an interactive developer +(Textual TUI), a scripter/automation author (programmatic `-p` mode and +the GitHub Action in action.yml), and an IDE/editor integrator (the +`vibe-acp` ACP server). + +==== Q1.2.2: Which user segment is the priority? +[OPEN] +Category: business-context +Ask role: Product Owner +Code treats interactive, programmatic and ACP paths as peers — each has +its own entrypoint and feature set. The code cannot reveal which segment +drives the roadmap or where investment is concentrated. + +=== Q1.3: Through what channels is it delivered? +[ANSWERED] +Evidence: README.md:27-55, pyproject.toml:143-144, action.yml:1-60, distribution/zed/extension.toml, .github/workflows/build-and-upload.yml +Delivered via PyPI (`mistral-vibe`), a one-line `install.sh`, `uv tool +install`, `pip install`, a packaged GitHub Action (action.yml), and a +Zed editor extension (distribution/zed/). Standalone binaries are built +with PyInstaller (vibe-acp.spec, pyinstaller/). + +=== Q1.4: Why was this bounded context built? +[OPEN] +Category: business-context +Ask role: Product Owner +The code shows *what* Vibe does but not the business rationale — why +Mistral built an open-source CLI agent, what competitive or strategic +goal it serves, or how it relates to the paid Mistral Code / Vibe Code +offering referenced by the teleport feature. + +=== Q1.5: What are the success metrics? + +==== Q1.5.1: What does the code measure? +[ANSWERED] +Evidence: vibe/core/telemetry/send.py:58-95, vibe/core/telemetry/types.py:26-39, vibe/core/agent_loop.py:1454-1462, vibe/core/types.py:102-115 +The code emits usage telemetry to `api.mistral.ai/v1/datalake/events` +and tracks per-session stats: turns, tool-call outcomes (agreed / +rejected / failed / succeeded), token counts, tokens-per-second and an +estimated session cost. + +==== Q1.5.2: What business KPIs define success? +[OPEN] +Category: business-context +Ask role: Product Owner +Telemetry collects events, but the code does not state which metric is +the success target (adoption, retention, task completion rate, +conversion to paid plans). The instrumentation is a means, not a goal. + +=== Q1.6: How are user segments prioritised? +[OPEN] +Category: business-context +Ask role: Product Owner +See Q1.2.2 — no segment ranking is derivable from code. The plan-offer +subsystem (vibe/cli/plan_offer/) distinguishes free vs. paid users, but +which segment is prioritised for features is a product decision. + +== Q2: What is the specification of this bounded context? + +=== Q2.1: Who are the actors? +[ANSWERED] +Evidence: vibe/cli/textual_ui/app.py:322, vibe/core/programmatic.py:27, vibe/acp/acp_agent_loop.py:267, vibe/core/tools/builtins/task.py:107-150, vibe/core/agents/models.py:152 +Human actors: the interactive Developer, the programmatic Scripter, the +ACP Client (editor/IDE). System actors: the LLM backend (Mistral or +OpenAI-compatible), the `explore` subagent spawned by the `task` tool, +MCP servers, hooks, and the remote Vibe Code / Nuage workflow backend. + +=== Q2.2: Use-case catalog + +==== Q2.2.PUC.InteractiveChat: Run an interactive coding session +===== Q2.2.PUC.InteractiveChat.Actor +[ANSWERED] +Evidence: vibe/cli/entrypoint.py:166, vibe/cli/cli.py:277-285, vibe/cli/textual_ui/app.py:322 +Primary Actor: Developer, via the `vibe` command launching the Textual TUI. + +===== Q2.2.PUC.InteractiveChat.Trigger +[ANSWERED] +Evidence: vibe/cli/entrypoint.py:35-40, vibe/cli/cli.py:197 +Developer runs `vibe` (optionally with a positional prompt) in a +trusted working directory. + +===== Q2.2.PUC.InteractiveChat.MainSuccess +[ANSWERED] +Evidence: vibe/core/agent_loop.py:632-649, vibe/core/agent_loop.py:855-937, vibe/core/agent_loop.py:969-994, vibe/core/agent_loop.py:1189-1206 +1. Developer submits a message. 2. `_conversation_loop` appends it and +runs middleware. 3. `_perform_llm_turn` calls the LLM backend. 4. Tool +calls are parsed and executed (with approval). 5. The loop repeats until +the assistant produces no tool call. 6. The final assistant message is +displayed. + +===== Q2.2.PUC.InteractiveChat.Extensions +[ANSWERED] +Evidence: vibe/core/middleware.py:49-62, vibe/core/middleware.py:81-96, vibe/core/agent_loop.py:897-906, vibe/core/agent_loop.py:1681-1749 +Turn/price limits stop the loop (TurnLimit/PriceLimit middleware); +context near threshold triggers auto-compaction; the user can interrupt +mid-turn (Escape) which cancels in-flight tool tasks. + +===== Q2.2.PUC.InteractiveChat.Postconditions +[ANSWERED] +Evidence: vibe/core/session/session_logger.py:36-72, vibe/core/session/last_session_pointer.py:62-98 +When session logging is enabled, the conversation is persisted as +`meta.json` + `messages.jsonl` under `~/.vibe/logs/session/`, and the +TTY's last-session pointer is updated for `--continue`. + +==== Q2.2.PUC.ProgrammaticRun: Run Vibe non-interactively +===== Q2.2.PUC.ProgrammaticRun.Actor +[ANSWERED] +Evidence: vibe/core/programmatic.py:27, README.md:254-262 +Primary Actor: Scripter / CI automation, via `vibe -p ""`. + +===== Q2.2.PUC.ProgrammaticRun.MainSuccess +[ANSWERED] +Evidence: vibe/cli/cli.py:235-250, vibe/core/programmatic.py:27-91, vibe/core/output_formatters.py:46-119 +Vibe runs headless with the `auto-approve` agent, enforces `--max-turns` +and `--max-price`, and emits results via the selected output formatter +(text / json / streaming-json). + +===== Q2.2.PUC.ProgrammaticRun.Extensions +[ANSWERED] +Evidence: vibe/core/programmatic.py:91, vibe/core/middleware.py:65-78 +If `session_cost` exceeds `--max-price`, `PriceLimitMiddleware` stops the +run; `--enabled-tools` restricts the toolset to an explicit allowlist. + +==== Q2.2.PUC.ResumeSession: Continue or resume a past session +[ANSWERED] +Evidence: vibe/cli/cli.py:128-190, vibe/core/session/session_loader.py:64-116, vibe/core/session/resume_sessions.py:27-95 +`--continue`/`-c` reloads the latest session for the cwd; `--resume +[ID]` resumes a specific session or opens a picker. Requires +`session_logging.enabled`. Remote (Vibe Code) sessions can also be listed. + +==== Q2.2.PUC.PlanMode: Plan before acting +[ANSWERED] +Evidence: vibe/core/agents/models.py:117, vibe/core/plan_session.py:11-42, vibe/core/middleware.py:128-161, vibe/core/tools/builtins/exit_plan_mode.py:62-137 +The `plan` agent is read-only; it writes a live markdown plan file and +the `exit_plan_mode` tool prompts the user, then switches to +`accept-edits` or `default` to execute. + +==== Q2.2.PUC.DelegateToSubagent: Delegate work to a subagent +[ANSWERED] +Evidence: vibe/core/tools/builtins/task.py:37-48, vibe/core/tools/builtins/task.py:107-150, vibe/core/agents/models.py:152 +The `task` tool spawns a nested `AgentLoop` running the `explore` +(read-only) subagent or another configured subagent, isolating context. + +==== Q2.2.PUC.VoiceMode: Dictate input by voice +[ANSWERED] +Evidence: vibe/cli/voice_manager/voice_manager.py:40-69, vibe/core/transcribe/mistral_transcribe_client.py:28-73, vibe/cli/narrator_manager/narrator_manager.py:33, vibe/core/tts/mistral_tts_client.py:15-46 +`/voice` toggles microphone capture; audio is streamed to Mistral's +realtime transcription API, and assistant turns can be narrated back via +the TTS client. README marks this experimental (README.md:284-285). + +==== Q2.2.PUC.Onboard: Complete first-run setup +[ANSWERED] +Evidence: vibe/setup/onboarding/base.py:6-14, vibe/setup/onboarding/screens/welcome.py:46-80, vibe/setup/onboarding/screens/auth_method.py:18-80, vibe/setup/auth/api_key_persistence.py:38-64 +The first-run wizard walks Welcome → auth-method → (browser sign-in or +manual API key), persisting the key to `~/.vibe/.env` or keyring. + +==== Q2.2.PUC.Teleport: Move a session to the cloud +[ANSWERED] +Evidence: vibe/core/teleport/teleport.py:46-150, vibe/core/teleport/git.py:27-100, vibe/core/teleport/types.py:6-61 +`/teleport` (gated by plan eligibility) validates the git repo, ensures +the commit is pushed, and starts a remote Vibe Code / Nuage workflow that +continues the session in the cloud. + +==== Q2.2.PUC.ManageExtensions: Configure MCP servers, skills, agents +[ANSWERED] +Evidence: vibe/core/tools/mcp/registry.py:21-150, vibe/core/skills/manager.py:20-117, vibe/core/agents/manager.py:21-99, README.md:340-579 +The user extends Vibe via `config.toml`: MCP servers, SKILL.md skills, +custom agent TOML profiles, and (experimental) hooks — discovered from +project `.vibe/` and `~/.vibe/`. + +==== Q2.2.UC.completeness +[OPEN] +Category: business-context +Ask role: Product Owner, Domain Expert +The catalog above is recovered from entrypoints and tools; the code +cannot confirm it is the *intended* complete set of user goals, nor +which use cases are core vs. experimental beyond the README's hints. + +=== Q2.3: Per-interface system specifications + +==== Q2.3.SUC.VibeCLI: The `vibe` command-line interface +[ANSWERED] +Evidence: vibe/cli/entrypoint.py:19-134, vibe/cli/entrypoint.py:166-211 +Flags: `-v/--version`, positional `PROMPT`, `-p/--prompt`, `--max-turns`, +`--max-price`, `--enabled-tools` (repeatable, glob/regex), `--output` +(text|json|streaming), `--agent`, `--setup`, `--workdir`, `--add-dir` +(repeatable), `--trust`, `--teleport` (hidden), `-c/--continue`, +`--resume [ID]`. Env: `VIBE_HOME`, `LOG_LEVEL`, `LOG_MAX_BYTES`, `VIBE_*`. + +==== Q2.3.SUC.SlashCommands: In-session slash commands +[ANSWERED] +Evidence: vibe/cli/commands.py:39-177 +The `CommandRegistry` registers 23 commands: help, config, model, +thinking, reload, clear, copy, log, debug, compact, exit, status, +teleport, proxy-setup, resume(/continue), rename, mcp(/connectors), +voice, leanstall, unleanstall, rewind, loop, data-retention. + +==== Q2.3.SUC.AcpServer: The `vibe-acp` Agent Client Protocol server +[ANSWERED] +Evidence: vibe/acp/entrypoint.py:78-108, vibe/acp/acp_agent_loop.py:1728-1770, vibe/acp/exceptions.py:1-141, vibe/acp/commands/registry.py:45-89 +`vibe-acp` runs an ACP agent over stdio. Errors follow JSON-RPC 2.0 +(UnauthenticatedError, InvalidRequestError, SessionNotFoundError, +ContextTooLongError, ConfigurationError). It advertises a reduced +command set (help, compact, reload, log, proxy-setup, leanstall, +unleanstall, data-retention). + +==== Q2.3.SUC.ToolInterface: The built-in tool contract +[ANSWERED] +Evidence: vibe/core/tools/base.py:122-152, vibe/core/tools/base.py:83-113, vibe/core/tools/base.py:61-80 +Each tool subclasses `BaseTool` with a Pydantic args model, a result +model, a `BaseToolConfig` (permission ALWAYS/ASK/NEVER, allow/deny lists, +sensitive patterns), and an `async run(args, ctx)` generator. Tools raise +`ToolError` (user-facing) or `ToolPermissionError`. + +===== Q2.3.SUC.ToolInterface.bash +[ANSWERED] +Evidence: vibe/core/tools/builtins/bash.py:220-257, vibe/core/tools/builtins/bash.py:480-530 +`bash` runs a shell command; default timeout 300 s (overridable), +stdout+stderr captured to a 16 000-byte cap, `sudo` always asks. + +===== Q2.3.SUC.ToolInterface.read_write +[ANSWERED] +Evidence: vibe/core/tools/builtins/read_file.py:58-67, vibe/core/tools/builtins/write_file.py:41-48, vibe/core/tools/builtins/search_replace.py:70-77 +`read_file` (permission ALWAYS, 64 000-byte read cap), `write_file` (ASK, +64 000-byte write cap), `search_replace` (100 000-byte content cap, +fuzzy threshold 0.9). All treat `**/.env*` as sensitive. + +===== Q2.3.SUC.ToolInterface.search +[ANSWERED] +Evidence: vibe/core/tools/builtins/grep.py:36-147 +`grep` (permission ALWAYS) caps at 100 matches, 64 000 output bytes, +60 s timeout; honours `.vibeignore` and a default exclude list. + +===== Q2.3.SUC.ToolInterface.web +[ANSWERED] +Evidence: vibe/core/tools/builtins/webfetch.py:62-77, vibe/core/tools/builtins/websearch.py:49-55 +`webfetch` (ASK) default 30 s / max 120 s timeout, 120 000-byte content +cap; `websearch` (ASK) 120 s timeout via Mistral conversations API. + +===== Q2.3.SUC.ToolInterface.agent_tools +[ANSWERED] +Evidence: vibe/core/tools/builtins/todo.py:54-56, vibe/core/tools/builtins/task.py:51-53, vibe/core/tools/builtins/ask_user_question.py:27-75, vibe/core/tools/builtins/skill.py:20-36, vibe/core/tools/builtins/exit_plan_mode.py:35-36 +`todo` (max 100 items), `task` (subagent allowlist defaults to +`explore`), `ask_user_question` (1–4 questions, 2–4 options each), +`skill`, `exit_plan_mode` — all permission ALWAYS except `task` (ASK). + +==== Q2.3.SUC.ConfigFile: The `config.toml` interface +[ANSWERED] +Evidence: vibe/core/config/_settings.py:495-644, vibe/core/config/layers/user.py:11-32, vibe/core/paths/_local_config_walk.py:78-168 +`config.toml` (project `.vibe/` then `~/.vibe/`) is a 60+-field +`VibeConfig`: active_model, providers/models, enabled/disabled tools and +agents, default_agent, skill_paths, agent_paths, session_logging, +enable_telemetry, mcp_servers, experiment_overrides, and more. + +==== Q2.3.SUC.McpInterface: MCP server configuration +[ANSWERED] +Evidence: vibe/core/config/_settings.py:226-243, vibe/core/tools/mcp/registry.py:80-150, README.md:505-579 +MCP servers declare transport http / streamable-http / stdio; default +`startup_timeout_sec` 10.0, `tool_timeout_sec` 60.0. Tools are named +`{server}_{tool}`. + +==== Q2.3.SUC.HookInterface: Lifecycle hooks +[ANSWERED] +Evidence: vibe/core/hooks/config.py:26-100, vibe/core/hooks/models.py:13-66, vibe/core/hooks/manager.py:28-100 +Hooks (experimental, gated by `enable_experimental_hooks`) are TOML- +declared shell commands with a default 30 s timeout, fire on the +`POST_AGENT_TURN` event, and retry up to 3 times. + +=== Q2.4: Data / entity model + +==== Q2.4.Session: The Session entity +[ANSWERED] +Evidence: vibe/core/session/session_loader.py:16-17, vibe/core/session/session_logger.py:62-97, vibe/core/session/session_id.py:8-34 +A Session is a folder `{prefix}_{YYYYMMDD_HHMMSS}_{short-id}` holding +`meta.json` (metadata, title, working_directory, parent_session_id) and +`messages.jsonl` (the conversation). Session IDs are 36-char UUID-shaped +strings with a stable 12-hex suffix. + +==== Q2.4.Conversation: Messages and events +[ANSWERED] +Evidence: vibe/core/types.py:219-329, vibe/core/types.py:366-450, vibe/core/types.py:480-556 +`LLMMessage` (role, content, reasoning_content, tool_calls, +tool_call_id, message_id) is the persisted unit; `MessageList` is the +observable conversation container; events (UserMessage, Assistant, +Reasoning, ToolCall, ToolResult, ToolStream, CompactStart/End, +PlanReview*) are the streamed unit. + +==== Q2.4.AgentStats: Per-session statistics +[ANSWERED] +Evidence: vibe/core/types.py:47-138 +`AgentStats` holds steps, tool-call counters, session/last-turn token +totals, context_tokens, pricing, and a computed `session_cost`. + +==== Q2.4.Config: Configuration entities +[ANSWERED] +Evidence: vibe/core/config/schema.py:19-158, vibe/core/config/_settings.py:495-644 +Config is a layered, merge-aware `ConfigSchema`/`VibeConfig` with +per-field merge strategies (replace, concat, union, shallow, conflict). + +==== Q2.4.AgentProfile: Agent profiles +[ANSWERED] +Evidence: vibe/core/agents/models.py:48-208 +An `AgentProfile` has name, display_name, safety (SAFE/NEUTRAL/ +DESTRUCTIVE/YOLO), agent_type (AGENT/SUBAGENT), config overrides, and +`install_required`. Seven builtins plus custom TOML profiles. + +==== Q2.4.Checkpoint: Rewind checkpoints +[ANSWERED] +Evidence: vibe/core/rewind/manager.py:16-49 +A `Checkpoint` ties a message index to a list of `FileSnapshot`s, +enabling rewind-to-message with file restoration. + +=== Q2.5: Acceptance criteria + +==== Q2.5.1: Are acceptance criteria expressed as tests? +[ANSWERED] +Evidence: AGENTS.md:84-92, pyproject.toml:155-162 +A `tests/` suite (pytest + pytest-asyncio + respx + textual-snapshot) +exists with test doubles in `tests/stubs/`; it is the de-facto +executable acceptance criteria. + +==== Q2.5.2: Are there Gherkin-style acceptance criteria per use case? +[OPEN] +Category: business-context +Ask role: Product Owner, Developer +No Given/When/Then acceptance criteria exist in code. Tests assert +behaviour but are not traced to use cases or business rules. The +canonical "done" definition per user goal must come from the team. + +=== Q2.6: Cross-cutting business rules + +==== Q2.6.BR.TrustGate: Folders must be trusted before use +[ANSWERED] +Evidence: vibe/core/trusted_folders.py:75-122, vibe/core/trusted_folders.py:19-32 +A folder containing `.vibe/`/`AGENTS.md` triggers a trust prompt; trust +decisions persist in `~/.vibe/trusted_folders.toml` and propagate to +descendants. Untrusted folders do not load project config. + +==== Q2.6.BR.ToolApproval: Tool execution honours permission tiers +[ANSWERED] +Evidence: vibe/core/tools/base.py:83-113, vibe/core/tools/permissions.py:47-68, vibe/core/tools/utils.py:64-125 +Every tool call resolves to ALWAYS / ASK / NEVER; ASK prompts the user, +file tools enforce a workdir boundary, and approved patterns are +remembered in a `PermissionStore` for the session. + +==== Q2.6.BR.AgentSafety: The active agent profile bounds tool autonomy +[ANSWERED] +Evidence: vibe/core/agents/models.py:110-199, vibe/core/middleware.py:174-216 +`plan`/`chat` are read-only (enforced by `ReadOnlyAgentMiddleware`), +`accept-edits` auto-approves edits only, `auto-approve` approves +everything; programmatic mode forces `auto-approve`. + +==== Q2.6.BR.TelemetryGate: Telemetry is gated by config and API key +[ANSWERED] +Evidence: vibe/core/telemetry/send.py:88-95, vibe/core/config/_settings.py:508 +Telemetry is on by default but only active when `enable_telemetry` is +true *and* a Mistral API key is present. + +==== Q2.6.BR.intent +[OPEN] +Category: business-context +Ask role: Product Owner, Domain Expert +The rules above are recovered as mechanisms. Whether each is a hard +product requirement or an implementation choice (e.g. is the workdir +boundary a security guarantee or a convenience?) needs team confirmation. + +== Q3: What is the architecture of this bounded context? + +=== Q3.1: Introduction and goals +[ANSWERED] +Evidence: pyproject.toml:5, README.md:92-109, vibe/core/agents/models.py:26-35 +Vibe's stated goal is a *minimal* CLI coding agent: a conversational +agent over a codebase with a safe tool suite, project-aware context, and +configurable agent profiles. "Safety first" (tool approval) is an +explicit theme. The *ranking* of quality goals, however, is not in code +— see Q4.9. + +=== Q3.2: Constraints +[ANSWERED] +Evidence: pyproject.toml:6, .python-version, AGENTS.md:1-12, AGENTS.md:24-46, README.md:24-26, LICENSE +Constraints: Python >=3.12; `uv`-managed; strict pyright + ruff gate CI; +no relative imports, no `# type: ignore`; UNIX is the officially +supported target (Windows works but is not targeted); Apache-2.0 licence. + +=== Q3.3: Context and scope +[ANSWERED] +Evidence: vibe/core/llm/backend/factory.py:1-7, vibe/core/telemetry/send.py:30-31, vibe/core/experiments/_constants.py:5-15, vibe/core/tools/mcp/registry.py:21, vibe/core/nuage/client.py:22-39, vibe/core/auth/github.py:14-22 +External systems Vibe depends on: the LLM backend (Mistral API or any +OpenAI-compatible / Anthropic / Vertex endpoint), the Mistral datalake +telemetry endpoint, a GrowthBook experiments service, MCP servers, the +Vibe Code / Nuage remote workflow backend, GitHub (device-flow auth), +and PyPI/GitHub (update checks). + +=== Q3.4: Solution strategy + +==== Q3.4.1: What architectural patterns are used? +[ANSWERED] +Evidence: AGENTS.md:13-23, vibe/core/llm/backend/base.py:13-126, vibe/core/llm/backend/factory.py:7, vibe/core/middleware.py:218-247, vibe/core/config/builder.py:24-122 +Hexagonal-style ports (`*_port.py` interfaces), an LLM backend +abstraction selected by a factory, a middleware pipeline around the +agent loop, and a layered merge-aware config builder. + +==== Q3.4.2: Why were these strategies chosen? +[OPEN] +Category: design-rationale +Ask role: Architect +The patterns are visible but their rationale is not — e.g. why a custom +middleware pipeline rather than the LLM SDK's own hooks, or why a +hand-rolled config merge layer. See Q3.9. + +=== Q3.5: Building-block view + +==== Q3.5.1: Level-1 — the four sub-contexts +[ANSWERED] +Evidence: AGENTS.md:5, vibe/__init__.py, vibe/cli/entrypoint.py, vibe/acp/entrypoint.py, vibe/setup/onboarding/base.py +`vibe.core` (engine: agent loop, tools, LLM, config, sessions), +`vibe.cli` (Textual TUI + programmatic), `vibe.acp` (ACP bridge), +`vibe.setup` (first-run wizards). + +==== Q3.5.2: core — the agent loop +[ANSWERED] +Evidence: vibe/core/agent_loop.py:632-649, vibe/core/agent_loop.py:855-937, vibe/core/loop.py +Responsibility: orchestrate a conversation turn — call the LLM, parse +and run tool calls concurrently, apply middleware, manage stats. +Interface: `AgentLoop.act()`. Source: vibe/core/agent_loop.py. + +==== Q3.5.3: core — the LLM backend layer +[ANSWERED] +Evidence: vibe/core/llm/backend/base.py:13-126, vibe/core/llm/backend/mistral.py:182-335, vibe/core/llm/backend/generic.py:188-447, vibe/core/llm/format.py:58-185 +Responsibility: a uniform `BackendLike` protocol over Mistral and +OpenAI-compatible providers, with API-style adapters (OpenAI, Anthropic, +Vertex, reasoning). Interface: `complete` / `complete_streaming` / +`count_tokens`. Source: vibe/core/llm/backend/. + +==== Q3.5.4: core — the tool subsystem +[ANSWERED] +Evidence: vibe/core/tools/base.py:122-152, vibe/core/tools/manager.py:72-447, vibe/core/tools/permissions.py:47-68 +Responsibility: discover, filter, instantiate and permission-check +tools (builtin, MCP, connector). Interface: `ToolManager.available_tools` +/ `.get()`. Source: vibe/core/tools/. + +==== Q3.5.5: core — the config subsystem +[ANSWERED] +Evidence: vibe/core/config/orchestrator.py:10-46, vibe/core/config/builder.py:24-122, vibe/core/config/_settings.py:495-644 +Responsibility: load and merge config layers into a validated +`VibeConfig`. Interface: `ConfigOrchestrator`. Source: vibe/core/config/. + +==== Q3.5.6: core — session persistence +[ANSWERED] +Evidence: vibe/core/session/session_logger.py:36-97, vibe/core/session/session_loader.py:64-116, vibe/core/rewind/manager.py:35-165 +Responsibility: persist, load, migrate, resume and rewind sessions. +Interface: `SessionLogger`, `SessionLoader`, `RewindManager`. Source: +vibe/core/session/, vibe/core/rewind/. + +==== Q3.5.7: cli — the Textual UI +[ANSWERED] +Evidence: vibe/cli/textual_ui/app.py:322-558, vibe/cli/commands.py:39 +Responsibility: render the chat TUI, route key bindings, dispatch slash +commands. Interface: `VibeApp`. Source: vibe/cli/textual_ui/. +NOTE: this branch is shallow — the ~70 widget files were not decomposed +individually; a UI-focused recovery run is recommended. + +==== Q3.5.8: acp — the ACP bridge +[ANSWERED] +Evidence: vibe/acp/acp_agent_loop.py:267-289, vibe/acp/session.py:11-70, vibe/acp/tools/base.py:13-54 +Responsibility: expose the agent over the Agent Client Protocol, with +ACP-specific tools that delegate file/terminal ops to the ACP client. +Interface: `VibeAcpAgentLoop`. Source: vibe/acp/. + +==== Q3.5.9: core — remote (teleport + nuage) +[ANSWERED] +Evidence: vibe/core/teleport/teleport.py:46-150, vibe/core/nuage/client.py:22-100, vibe/core/nuage/remote_events_source.py:33-120 +Responsibility: hand a session off to a remote Vibe Code workflow and +stream its events back. Interface: `TeleportService`, `WorkflowsClient`, +`RemoteEventsSource`. Source: vibe/core/teleport/, vibe/core/nuage/. + +=== Q3.6: Runtime view + +==== Q3.6.SCN.Turn: A single conversation turn (happy path) +[ANSWERED] +Evidence: vibe/core/agent_loop.py:883-937, vibe/core/agent_loop.py:969-994, vibe/core/agent_loop.py:1217-1259 +Middleware `before_turn` → LLM completion (streaming or not) → parse +tool calls → resolve permissions → run tools concurrently via asyncio +tasks → append results → repeat until no tool call. + +==== Q3.6.SCN.Approval: Tool approval interception +[ANSWERED] +Evidence: vibe/core/agent_loop.py:1071-1167, vibe/core/tools/permissions.py:47-68 +`_execute_tool_call` calls `_should_execute_tool`; an ASK permission +invokes the approval callback; rejection records `tool_calls_rejected` +and feeds a rejection result back to the LLM. + +==== Q3.6.SCN.Compaction: Context auto-compaction (recovery scenario) +[ANSWERED] +Evidence: vibe/core/middleware.py:81-96, vibe/core/agent_loop.py:776-821, vibe/core/agent_loop.py:1681-1749 +When `context_tokens` crosses the model's `auto_compact_threshold`, +`AutoCompactMiddleware` returns COMPACT; the loop summarises history +into `[system, summary]`, resets to a new session id (parent linked), +and recounts tokens. + +==== Q3.6.SCN.Interrupt: User interruption (error/recovery scenario) +[ANSWERED] +Evidence: vibe/core/agent_loop.py:897-906, vibe/core/agent_loop.py:1170-1178, vibe/core/agent_loop.py:1244-1259 +Escape raises a user-cancellation event; in-flight tool tasks receive +`asyncio.CancelledError`, the cancellation is recorded as a tool result, +and the loop breaks cleanly. + +==== Q3.6.SCN.RateLimit: LLM rate-limit / context-too-long handling +[ANSWERED] +Evidence: vibe/core/agent_loop.py:1365-1374, vibe/core/llm/exceptions.py, vibe/acp/exceptions.py:107 +`_chat` catches rate-limit and context-too-long errors from the backend; +ACP surfaces `ContextTooLongError` as a JSON-RPC error. + +=== Q3.7: Deployment view +[ANSWERED] +Evidence: README.md:27-55, pyproject.toml:143-152, vibe-acp.spec, pyinstaller/runtime_hook_truststore.py, action.yml, .github/workflows/build-and-upload.yml, .github/workflows/release.yml +Vibe deploys as a Python package run under `uv`/`pip` on the developer's +machine; standalone binaries are built with PyInstaller; a GitHub Action +packages it for CI; the Zed extension wires it into the editor. It is a +client-side tool — there is no server deployment except the external +Mistral / Nuage services it calls. + +=== Q3.8: Crosscutting concepts + +==== Q3.8.1: Threat model (STRIDE) +[OPEN] +Category: design-rationale +Ask role: Architect, Operations +No STRIDE threat model or T-IDs exist in the code. Security *mechanisms* +are present (Q3.8.2) but no enumerated threat catalogue. The team must +author one; Phase 2 will derive candidate threats from the mechanisms. + +==== Q3.8.2: Security mechanisms +[ANSWERED] +Evidence: vibe/core/tools/base.py:83-113, vibe/core/tools/utils.py:64-125, vibe/core/trusted_folders.py:75-122, vibe/core/auth/crypto.py:13-137, vibe/core/auth/github.py:14-22, vibe/setup/auth/browser_sign_in.py:173-184 +Mechanisms in code: tiered tool permissions + workdir boundary, +sensitive-pattern (`**/.env*`) gating, the trust-folder gate, RSA-OAEP + +AES-256-GCM payload encryption, keyring-stored secrets, and PKCE in the +browser sign-in flow. + +==== Q3.8.3: Test concept +[ANSWERED] +Evidence: AGENTS.md:84-92, pyproject.toml:155-167, .github/workflows/ci.yml +Pytest-based suite with async, HTTP-mock (respx) and Textual-snapshot +layers; CI runs pytest, pyright (strict) and ruff. The pyramid shape and +per-use-case traceability are not enforced. + +==== Q3.8.4: Observability +[ANSWERED] +Evidence: vibe/core/tracing.py:23-137, vibe/core/logger.py, vibe/core/telemetry/send.py:58-95, AGENTS.md:71-79, vibe/acp/acp_logger.py:17-59 +OpenTelemetry tracing (agent + tool spans, conversation-id baggage), +structured stdlib logging to `~/.vibe/logs/vibe.log`, usage telemetry to +the Mistral datalake, and optional ACP message logging. + +==== Q3.8.5: Error handling +[ANSWERED] +Evidence: vibe/core/utils/retry.py, vibe/core/llm/backend/mistral.py:212-222, vibe/core/agent_loop.py:1365-1374, vibe/core/hooks/manager.py:28-53, AGENTS.md:71-79 +Module-local exception hierarchies chained with `from`; the Mistral +backend retries with exponential backoff (500 ms initial, 1.5x, 300 s +cap); hooks retry up to 3 times; the loop catches rate-limit / +context-too-long / cancellation distinctly. + +==== Q3.8.6: Configuration concept +[ANSWERED] +Evidence: vibe/core/config/builder.py:24-122, vibe/core/paths/_local_config_walk.py:132-168, vibe/core/config/layers/user.py:11-32 +Config is layered (project `.vibe/` walked BFS up to 4 levels, then +`~/.vibe/`) and merged field-by-field with declared merge strategies. + +=== Q3.9: Architecture decisions + +==== Q3.9.ADR.HexagonalPorts +[OPEN] +Category: design-rationale +Ask role: Architect +The codebase uses `*_port.py` ports and adapters pervasively, but no ADR +records why hexagonal architecture was chosen or what alternatives were +weighed. Status would be "Accepted (inferred)". + +==== Q3.9.ADR.LLMBackendAbstraction +[OPEN] +Category: design-rationale +Ask role: Architect +A backend factory supports Mistral + OpenAI-compatible + Anthropic + +Vertex. The decision to support non-Mistral providers in a +Mistral-branded tool, and its trade-offs, is not documented in code. + +==== Q3.9.ADR.MiddlewarePipeline +[OPEN] +Category: design-rationale +Ask role: Architect +A custom middleware pipeline enforces turn/price limits, auto-compaction +and read-only modes. Why a bespoke pipeline (vs. SDK callbacks or +inline checks) is not recorded. + +==== Q3.9.ADR.SessionsAsFiles +[OPEN] +Category: design-rationale +Ask role: Architect +Sessions persist as folder + JSONL on local disk (with a v1→v2 migration +in session_migration.py). The choice of flat files over a database, and +the migration trigger, are undocumented. + +==== Q3.9.index +[ANSWERED] +Evidence: (repository search) docs/, src/docs/ +No ADR register exists in the bounded context; the four ADRs above are +candidates Phase 2 should author once the team answers the rationale. + +=== Q3.10: Quality requirements +[ANSWERED] +Evidence: vibe/core/tools/builtins/bash.py:225-226, vibe/core/tools/builtins/grep.py:46-51, vibe/core/config/_settings.py:231-237, vibe/core/llm/backend/mistral.py:212-222, vibe/core/middleware.py:81-96 +Code-derived quality scenarios carry literal figures: bash timeout 300 s, +grep 60 s / 100 matches, MCP startup 10 s / tool 60 s, LLM retry cap +300 s, auto-compaction at the model's token threshold. Full six-part +scenarios belong in Phase 2 arc42 Chapter 10. The quality-goal *ranking* +is Q4.9. + +=== Q3.11: Risks and technical debt +[ANSWERED] +Evidence: vibe/core/types.py:102-115, README.md:284-285, vibe/cli/textual_ui/app.py (~70 widget files), vibe/acp/acp_agent_loop.py:650-666 +Code-visible debt: `session_cost` is an explicit rough estimate (ignores +prompt caching); voice mode is marked experimental; the `acp/tools/` +builtins partly duplicate `core/tools/` builtins; the TUI widget layer is +large and untiered. Probability/impact ranking is Q5 / team input. + +=== Q3.12: Glossary +[ANSWERED] +Evidence: vibe/core/skills/builtins/vibe.py:5-8, vibe/core/agents/models.py:38-45, vibe/core/teleport/teleport.py:46, vibe/core/nuage/client.py:22, README.md:111-156 +Key terms with code anchors: Agent / Subagent / Agent Profile, Tool, +Skill, MCP server, Connector, Hook, Session, Compaction, Plan mode, +Teleport, Nuage (cloud backend), Trust folder, Harness files. + +== Q4: What quality goals drive the design? + +=== Q4.1: Functional suitability +[ANSWERED] +Evidence: README.md:92-109, vibe/core/tools/builtins/, vibe/core/agent_loop.py:632-649 +The tool suite + agent loop deliver the stated function (explore, modify, +run code conversationally); functional completeness is exercised by the +test suite. + +=== Q4.2: Performance efficiency +[ANSWERED] +Evidence: vibe/core/agent_loop.py:1217-1259, vibe/core/agent_loop.py:1376-1452, vibe/core/types.py:58-61, vibe/cli/profiler.py +Concurrent tool execution via asyncio tasks, streaming LLM responses, +tokens-per-second tracking, and an opt-in pyinstrument profiler. No +stated latency budget — see Q4.9. + +=== Q4.3: Compatibility +[ANSWERED] +Evidence: vibe/core/llm/backend/factory.py:7, vibe/core/tools/mcp/registry.py:21, vibe/acp/entrypoint.py:78, README.md:344 +Compatible with multiple LLM providers, the MCP protocol, the Agent +Client Protocol, and the agentskills.io skill standard. + +=== Q4.4: Usability +[ANSWERED] +Evidence: vibe/cli/textual_ui/app.py:327-348, vibe/setup/onboarding/screens/welcome.py:46-80, vibe/cli/autocompletion/, vibe/core/data_retention.py:3-7 +Rich TUI with key bindings, autocompletion, a first-run wizard, and a +data-retention notice. Measured usability targets are not in code. + +=== Q4.5: Reliability +[ANSWERED] +Evidence: vibe/core/utils/retry.py, vibe/core/llm/backend/mistral.py:212-222, vibe/core/hooks/manager.py:28-53, vibe/core/agent_loop.py:1365-1374 +Retry/backoff, hook retry limits, distinct handling of rate-limit and +context-too-long failures, fail-open experiments client. + +=== Q4.6: Security +[ANSWERED] +Evidence: vibe/core/tools/base.py:83-113, vibe/core/trusted_folders.py:75-122, vibe/core/auth/crypto.py:13-137 +See Q3.8.2 — tiered permissions, trust gate, encrypted-payload and PKCE +auth. Note: no threat model backs these (Q3.8.1). + +=== Q4.7: Maintainability +[ANSWERED] +Evidence: AGENTS.md:24-69, pyproject.toml:155-167, vibe/core/llm/backend/base.py:13-126 +Strict pyright + ruff gates, ports/adapters seams, explicit `__all__`, +test doubles — a maintainability-oriented codebase. + +=== Q4.8: Portability +[ANSWERED] +Evidence: README.md:24-26, vibe/core/utils/platform.py, vibe/cli/terminal_detect.py:8-64, vibe/cli/clipboard.py:59-63 +Cross-platform with UNIX as the officially supported target; terminal +and clipboard code branches per OS/terminal. + +=== Q4.9: Which quality characteristic has priority? +[OPEN] +Category: quality-goals +Ask role: Product Owner, Architect +The README theme "Safety first" and the permission system suggest +Security/Usability are prioritised, but the code cannot rank the eight +ISO 25010 characteristics. The team must supply the ordering. + +== Q5: What risks and technical debt exist? + +=== Q5.1: Technical debt +[ANSWERED] +Evidence: vibe/core/types.py:102-115, vibe/acp/acp_agent_loop.py:650-666, vibe/core/session/session_migration.py:16-41, README.md:284-285 +Burdened building blocks: `AgentStats` (cost is a self-described rough +estimate) — Q3.5; `acp/tools/` duplicates several `core/tools/` builtins +— Q3.5.8; `session_migration.py` carries a v1→v2 format migration — +Q3.5.6; voice mode is experimental — Q3.5.7. Severity ranking is OPEN +(Ask role: Architect, Developer). + +=== Q5.2: Security risks +[ANSWERED] +Evidence: vibe/core/tools/builtins/bash.py:480-530, vibe/core/tools/builtins/webfetch.py:62-119, vibe/core/tools/utils.py:64-125, vibe/core/hooks/executor.py:11-55 +Code-visible risk surfaces: arbitrary shell execution via `bash` (gated +only by permissions/allowlists), SSRF via `webfetch`, file access +outside the workdir, and hook commands run as shell subprocesses. The +mitigations exist; whether they are *sufficient* is a security-review +question — see Q3.8.1. + +=== Q5.3: Operational risks +[ANSWERED] +Evidence: vibe/core/telemetry/send.py:30-31, vibe/core/experiments/client.py:43-76, vibe/core/llm/backend/mistral.py:268-299, vibe/cli/update_notifier/update.py:82 +Operational dependencies: the Mistral API, the datalake telemetry +endpoint, the GrowthBook experiments service, and PyPI/GitHub for +updates. The experiments client fails open; the LLM call's outage +behaviour beyond retry is partly OPEN (Ask role: Operations). + +=== Q5.4: Dependency / supply-chain risks +[ANSWERED] +Evidence: pyproject.toml:30-141, uv.lock, pyproject.toml:147-149, .pre-commit-config.yaml +~80 pinned direct dependencies with a full `uv.lock`; `exclude-newer` +constrains new releases. Risk: a large dependency surface (cryptography, +httpx, mcp SDK, mistralai, textual). No SBOM or scanning config is in the +bounded context. + +=== Q5.5: Scaling / performance risks +[ANSWERED] +Evidence: vibe/core/agent_loop.py:1681-1749, vibe/core/tools/builtins/read_file.py:65-66, vibe/core/autocompletion/file_indexer/indexer.py +Single-user, single-process tool — "scaling" means large repos and long +sessions: context growth is bounded by auto-compaction, file reads are +byte-capped, and a file indexer backs `@`-completion. Performance on very +large repos has no stated budget — see Q4.9 / Q3.10. diff --git a/docToolchainConfig.groovy b/docToolchainConfig.groovy new file mode 100644 index 000000000..f2ea94c48 --- /dev/null +++ b/docToolchainConfig.groovy @@ -0,0 +1,569 @@ +outputPath = 'build' + +// If you want to use the Antora integration, set this to true. +// This requires your project to be setup as Antora module. +// You can use `downloadTemplate` task to bootstrap your project. +//useAntoraIntegration = false + +// Path where the docToolchain will search for the input files. +// This path is appended to the docDir property specified in gradle.properties +// or in the command line, and therefore must be relative to it. + +inputPath = 'src/docs'; + +// if you need to register custom Asciidoctor extensions, this is the right place +// configure the name and path to your extension, relative to the root of your project +// (relative to dtcw). For example: 'src/ruby/asciidoctor-lists.rb'. +// this is the same as the `requires`-list of the asciidoctor gradle plugin. The extensions will be +// registered for generateDeck, generateHtml, generatePdf and generateDocbook tasks, only. +// rubyExtensions = [] + +// the pdfThemeDir config in this file is outdated. +// please check http://doctoolchain.org/docToolchain/v2.0.x/020_tutorial/030_generateHTML.html#_pdf_style for further details +// pdfThemeDir = './src/docs/pdfTheme' + +inputFiles = [ + //[file: 'doctoolchain_demo.adoc', formats: ['html','pdf']], + //[file: 'arc42-template.adoc', formats: ['html','pdf']], + [file: 'arc42/arc42.adoc', formats: ['html','pdf']], + [file: 'spec/prd-vibe.adoc', formats: ['html','pdf']], + [file: 'spec/use-cases-vibe.adoc', formats: ['html','pdf']], + /** inputFiles **/ +] + +//folders in which asciidoc will find images. +//these will be copied as resources to ./images +//folders are relative to inputPath +// Hint: If you define an imagepath in your documents like +// :imagesdir: ./whatsoever +// define it conditional like +// ifndef::imagesdir[:imagesdir: ./whatsoever] +// as doctoolchain defines :imagesdir: during generation +imageDirs = [ + 'images/.', + 'images/.', + /** imageDirs **/ +] + +// whether the build should fail when detecting broken image references +// if this config is set to true all images will be embedded +failOnMissingImages = true + +// these are directories (dirs) and files which Gradle monitors for a change +// in order to decide if the docs have to be re-build +taskInputsDirs = [ + "${inputPath}", +// "${inputPath}/src", +// "${inputPath}/images", + ] + +taskInputsFiles = [] + +//****************************************************************************** + +// Configuration for customTasks +// create a new Task with ./dtcw createTask +customTasks = [ +/** customTasks **/ +] + + +//****************************************************************************** + +//Configuration for microsite: generateSite + previewSite + +microsite = [:] + +// these properties will be set as jBake properties +// microsite.foo will be site.foo in jBake and can be used as config.site_foo in a template +// see https://jbake.org/docs/2.6.4/#configuration for how to configure jBake +// other properties listed here might be used in the jBake templates and thus are not +// documented in the jBake docs but hopefully in the template docs. +microsite.with { + /** start:microsite **/ + + // is your microsite deployed with a context path? + contextPath = '/' + // the folder of a site definition (theme) relative to the docDir+inputPath + //siteFolder = '../site' + + /** end:microsite **/ + + //project theme + //site folder relative to the docs folder + //see 'copyTheme' for more details + siteFolder = '../site' + + // the title of the microsite, displayed in the upper left corner + title = '##site-title##' + // the next items configure some links in the footer + // + // contact eMail + // example: mailto:bert@example.com + footerMail = '##footer-email##' + // + // twitter account url + footerTwitter = '##twitter-url##' + // + // Stackoverflow QA + footerSO = '##Stackoverflow-url##' + // + // Github Repository + footerGithub = '##Github-url##' + // + // Slack Channel + footerSlack = '##Slack-url##' + // + // Footer Text + // example: built with docToolchain and jBake
theme: docsy
+ footerText = 'built with docToolchain and jBake
theme: docsy
' + // + // site title if no other title is given + title = 'docToolchain' + // + // the url to create an issue in github + // Example: https://github.com/docToolchain/docToolchain/issues/new + issueUrl = '##issue-url##' + // + // the base url for code files in github + // Example: https://github.com/docToolchain/docToolchain/edit/master/src/docs + branch = System.getenv("DTC_PROJECT_BRANCH")?:'-' + gitRepoUrl = '##git-repo-url##' + + // + // the location of the landing page + landingPage = 'landingpage.gsp' + // the menu of the microsite. A map of [code:'title'] entries to specify the order and title of the entries. + // the codes are autogenerated from the folder names or :jbake-menu: attribute entries from the .adoc file headers + // set a title to '-' in order to remove this menu entry. + menu = [:] + +//tag::additionalConverters[] +/** + +if you need support for additional markup converters, you can configure them here +you have three different types of script you can define: + +- groovy: just groovy code as string +- groovyFile: path to a groovy script +- bash: a bash command. It will receive the name of the file to be converted as first argument + +`groovy` and `groovyFile` will have access to the file and config object + +`dtcw:rstToHtml.py` is an internal script to convert restructuredText. +Needs `python3` and `docutils` installed. + +**/ + additionalConverters = [ + //'.one': [command: 'println "test"+file.canonicalPath', type: 'groovy'], + //'.two': [command: 'scripts/convert-md.groovy', type: 'groovyFile'], + //'.rst': [command: 'dtcw:rstToHtml.py', type: 'bash'], + ] +//end::additionalConverters[] + + // if you prefer another convention regarding the automatic generation + // of jBake headers, you can configure a script to modify them here + // the script has access to + // - file: the current object + // - sourceFolder: the copy of the docs-source on which the build operates + // default `/microsite/tmp/site/doc` + // - config: the config object (this file, but parsed) + // - headers: already parsed headers to be modified + /** + customConvention = """ + System.out.println file.canonicalPath + headers.title += " - from CustomConvention" + """.stripIndent() + **/ + + // define a custom search html + /** + search = """
+ +
+ """ + **/ +} + +//****************************************************************************** + +//Configuration for exportChangelog + +exportChangelog = [:] + +changelog.with { + + // Directory of which the exportChangelog task will export the changelog. + // It should be relative to the docDir directory provided in the + // gradle.properties file. + dir = 'src/docs' + + // Command used to fetch the list of changes. + // It should be a single command taking a directory as a parameter. + // You cannot use multiple commands with pipe between. + // This command will be executed in the directory specified by changelogDir + // it the environment inherited from the parent process. + // This command should produce asciidoc text directly. The exportChangelog + // task does not do any post-processing + // of the output of that command. + // + // See also https://git-scm.com/docs/pretty-formats + cmd = 'git log --pretty=format:%x7c%x20%ad%x20%n%x7c%x20%an%x20%n%x7c%x20%s%x20%n --date=short' + +} + +//****************************************************************************** + +//tag::confluenceConfig[] +//Configuration for publishToConfluence + +confluence = [:] + +/** +//tag::input-config[] + +*input* + +is an array of files to upload to Confluence with the ability +to configure a different parent page for each file. + +=== Attributes + +- `file`: absolute or relative path to the asciidoc generated html file to be exported +- `url`: absolute URL to an asciidoc generated html file to be exported +- `ancestorName` (optional): the name of the parent page in Confluence as string; + this attribute has priority over ancestorId, but if page with given name doesn't exist, + ancestorId will be used as a fallback +- `ancestorId` (optional): the id of the parent page in Confluence as string; leave this empty + if a new parent shall be created in the space + +The following four keys can also be used in the global section below + +- `spaceKey`: page specific variable for the key of the confluence space to write to + case sensitive! If the case is not correct, it can be that new page will be + created but can't be updated in the next run. +- `subpagesForSections` (optional): The number of nested sub-pages to create. Default is '1'. + '0' means creating all on one page. + The following migration for removed configuration can be used. +** `allInOnePage = true` is the same as `subpagesForSections = 0` +** `allInOnePage = false && createSubpages = false` is the same as `subpagesForSections = 1` +** `allInOnePage = false && createSubpages = true` is the same as `subpagesForSections = 2` +- `pagePrefix` (optional): page specific variable, the pagePrefix will be a prefix for the page title and it's sub-pages + use this if you only have access to one confluence space but need to store several + pages with the same title - a different pagePrefix will make them unique +- `pageSuffix` (optional): same usage as prefix but appended to the title and it's subpages + +only 'file' or 'url' is allowed. If both are given, 'url' is ignored + +//end::input-config[] +**/ + +confluence.with { + input = [ + [ file: "build/html5/arc42-template-de.html" ], + ] + + // endpoint of the confluenceAPI (REST) to be used + // + // For Confluence Cloud, set this to: + // https://[yourDomain].atlassian.net + // Example: https://arc42-template.atlassian.net + // + // For Confluence Server/Data Center, you may need to include a context: + // https://[yourServer]/[context] + // Example: https://confluence.company.com/wiki + // + // VERIFICATION: You can verify your endpoint is correct by browsing to: + // [your-api-endpoint]/rest/api/user/current + // This should return a JSON response describing your current user + // Working example: https://arc42-template.atlassian.net/wiki/rest/api/user/current + api = 'https://[yourServer]/[context]' + + // requests per second for confluence API calls + rateLimit = 10 + + // if true API V1 only will be used. Default is true. + // useV1Api = true + + // if true, the new editor v2 will be used. Default is false. + // enforceNewEditor = false + + // Additionally, spaceKey, subpagesForSections, pagePrefix and pageSuffix can be globally defined here. The assignment in the input array has precedence + + // the key of the confluence space to write to + spaceKey = 'asciidoc' + + // if true, all pages will be created using the new editor v2 + // enforceNewEditor = false + + // variable to determine how many layers of sub pages should be created + subpagesForSections = 1 + + // the pagePrefix will be a prefix for each page title + // use this if you only have access to one confluence space but need to store several + // pages with the same title - a different pagePrefix will make them unique + pagePrefix = '' + + pageSuffix = '' + + // the comment used for the page version + pageVersionComment = '' + + /* + WARNING: It is strongly recommended to store credentials securely instead of committing plain text values to your git repository!!! + + Tool expects credentials that belong to an account which has the right permissions to to create and edit confluence pages in the given space. + Credentials can be used in a form of: + - passed parameters when calling script (-PconfluenceUser=myUsername -PconfluencePass=myPassword) which can be fetched as a secrets on CI/CD or + - gradle variables set through gradle properties (uses the 'confluenceUser' and 'confluencePass' keys) + Often, same credentials are used for Jira & Confluence, in which case it is recommended to pass CLI parameters for both entities as + -Pusername=myUser -Ppassword=myPassword + */ + + //optional API-token to be added in case the credentials are needed for user and password exchange. + //apikey = "[API-token]" + + // HTML Content that will be included with every page published + // directly after the TOC. If left empty no additional content will be + // added + // extraPageContent = '>This is a generated page, do not edit! + extraPageContent = '' + + // enable or disable attachment uploads for local file references + enableAttachments = false + + // variable to limit number of pages retreived per REST-API call + pageLimit = 100 + + // default attachmentPrefix = attachment - All files to attach will require to be linked inside the document. + // attachmentPrefix = "attachment" + + + // Optional proxy configuration, only used to access Confluence + // schema supports http and https + // proxy = [host: 'my.proxy.com', port: 1234, schema: 'http'] + + // Optional: specify which Confluence OpenAPI Macro should be used to render OpenAPI definitions + // possible values: ["confluence-open-api", "open-api", true]. true is the same as "confluence-open-api" for backward compatibility + // useOpenApiMacro = "confluence-open-api" + + // for exportConfluence-Task + export = [ + srcDir: 'sample_data', + destDir: 'src/docs' + ] + +} +//end::confluenceConfig[] + +//****************************************************************************** +//tag::exportEAConfig[] +//Configuration for the export script 'exportEA.vbs'. +// The following parameters can be used to change the default behaviour of 'exportEA'. +// All parameter are optionally. +// Parameter 'connection' allows to select a certain database connection by using the ConnectionString as used for +// directly connecting to the project database instead of looking for EAP/EAPX files inside and below the 'src' folder. +// Parameter 'packageFilter' is an array of package GUID's to be used for export. All images inside and in all packages below the package represented by its GUID are exported. +// A packageGUID, that is not found in the currently opened project, is silently skipped. +// PackageGUID of multiple project files can be mixed in case multiple projects have to be opened. + +exportEA.with { +// OPTIONAL: Set the connection to a certain project or comment it out to use all project files inside the src folder or its child folder. +// connection = "DBType=1;Connect=Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=[THE_DB_NAME_OF_THE_PROJECT];Data Source=[server_hosting_database.com];LazyLoad=1;" +// OPTIONAL: Add one or multiple packageGUIDs to be used for export. All packages are analysed, if no packageFilter is set. +// packageFilter = [ +// "{A237ECDE-5419-4d47-AECC-B836999E7AE0}", +// "{B73FA2FB-267D-4bcd-3D37-5014AD8806D6}" +// ] +// OPTIONAL: tag used to filter diagrams to be exported - based on diagram.Stereotype. +// If not set all diagrams in the selected packages are exported. +// diagramFilter = "myProject" +// OPTIONAL: relative path to base 'docDir' to which the diagrams and notes are to be exported +// exportPath = "src/docs/" +// OPTIONAL: relative path to base 'docDir', in which Enterprise Architect project files are searched +// searchPath = "src/docs/" + +} +//end::exportEAConfig[] + +//tag::htmlSanityCheckConfig[] +htmlSanityCheck.with { + //sourceDir = "build/html5/site" + //checkingResultsDir = +} +//end::htmlSanityCheckConfig[] + +//tag::jiraConfig[] +// Configuration for Jira related tasks +jira = [:] + +jira.with { + + // endpoint of the JiraAPI (REST) to be used + api = 'https://your-jira-instance' + + // requests per second for jira API calls + rateLimit = 10 + + /* + WARNING: It is strongly recommended to store credentials securely instead of committing plain text values to your git repository!!! + + Tool expects credentials that belong to an account which has the right permissions to read the JIRA issues for a given project. + Credentials can be used in a form of: + - passed parameters when calling script (-PjiraUser=myUsername -PjiraPass=myPassword) which can be fetched as a secrets on CI/CD or + - gradle variables set through gradle properties (uses the 'jiraUser' and 'jiraPass' keys) + Often, Jira & Confluence credentials are the same, in which case it is recommended to pass CLI parameters for both entities as + -Pusername=myUser -Ppassword=myPassword + */ + + // the key of the Jira project + project = 'PROJECTKEY' + + // the format of the received date time values to parse + dateTimeFormatParse = "yyyy-MM-dd'T'H:m:s.SSSZ" // i.e. 2020-07-24'T'9:12:40.999 CEST + + // the format in which the date time should be saved to output + dateTimeFormatOutput = "dd.MM.yyyy HH:mm:ss z" // i.e. 24.07.2020 09:02:40 CEST + + // the label to restrict search to + label = + + // Legacy settings for Jira query. This setting is deprecated & support for it will soon be completely removed. Please use JiraRequests settings + //jql = "project='%jiraProject%' AND labels='%jiraLabel%' ORDER BY priority DESC, duedate ASC" + + // Base filename in which Jira query results should be stored + resultsFilename = 'JiraTicketsContent' + + saveAsciidoc = true // if true, asciidoc file will be created with *.adoc extension + saveExcel = true // if true, Excel file will be created with *.xlsx extension + + // Output folder for this task inside main outputPath + resultsFolder = 'JiraRequests' + + /* + List of requests to Jira API: + These are basically JQL expressions bundled with a filename in which results will be saved. + User can configure custom fields IDs and name those for column header, + i.e. customfield_10026:'Story Points' for Jira instance that has custom field with that name and will be saved in a column named "Story Points" + */ + exports = [ + [ + filename:"File1_Done_issues", + jql:"project='%jiraProject%' AND status='Done' ORDER BY duedate ASC", + customfields: [customfield_10026:'Story Points'] + ], + [ + filename:'CurrentSprint', + jql:"project='%jiraProject%' AND Sprint in openSprints() ORDER BY priority DESC, duedate ASC", + customfields: [customfield_10026:'Story Points'] + ], + ] +} +//end::jiraConfig[] + +//tag::openApiConfig[] +// Configuration for OpenAPI related task +openApi = [:] + +// 'specFile' is the name of OpenAPI specification yaml file. Tool expects this file inside working dir (as a filename or relative path with filename) +// 'infoUrl' and 'infoEmail' are specification metadata about further info related to the API. By default this values would be filled by openapi-generator plugin placeholders +// + +openApi.with { + specFile = 'src/docs/petstore-v2.0.yaml' // i.e. 'petstore.yaml', 'src/doc/petstore.yaml' + infoUrl = 'https://my-api.company.com' + infoEmail = 'info@company.com' +} +//end::openApiConfig[] + +//tag::sprintChangelogConfig[] +// Sprint changelog configuration generate changelog lists based on tickets in sprints of an Jira instance. +// This feature requires at least Jira API & credentials to be properly set in Jira section of this configuration +sprintChangelog = [:] +sprintChangelog.with { + sprintState = 'closed' // it is possible to define multiple states, i.e. 'closed, active, future' + ticketStatus = "Done, Closed" // it is possible to define multiple ticket statuses, i.e. "Done, Closed, 'in Progress'" + + showAssignee = false + showTicketStatus = false + showTicketType = true + sprintBoardId = 12345 // Jira instance probably have multiple boards; here it can be defined which board should be used + + // Output folder for this task inside main outputPath + resultsFolder = 'Sprints' + + // if sprintName is not defined or sprint with that name isn't found, release notes will be created on for all sprints that match sprint state configuration + sprintName = 'PRJ Sprint 1' // if sprint with a given sprintName is found, release notes will be created just for that sprint + allSprintsFilename = 'Sprints_Changelogs' // Extension will be automatically added. +} +//end::sprintChangelogConfig[] + +//tag::collectIncludesConfig[] +collectIncludes = [:] + +collectIncludes.with { + + fileFilter = "adoc" // define which files are considered. default: "ad|adoc|asciidoc" + + minPrefixLength = "3" // define what minimum length the prefix. default: "3" + + maxPrefixLength = "3" // define what maximum length the prefix. default: "" + + separatorChar = "_" // define the allowed separators after prefix. default: "-_" + + cleanOutputFolder = true // should the output folder be emptied before generation? default: false + + excludeDirectories = [] // define additional directories that should not be traversed. + +} +//end::collectIncludesConfig[] + +//tag::structurizrConfig[] +// Configuration for Structurizr related tasks +structurizr = [:] + +structurizr.with { + + // Configure where `exportStructurizr` looks for the Structurizr model. + workspace = { + // The directory in which the Structurizr workspace file is located. + // path = 'src/docs/structurizr' + + // By default `exportStructurizr` looks for a file '${structurizr.workspace.path}/workspace.dsl'. + // You can customize this behaviour with 'filename'. Note that the workspace filename is provided without '.dsl' extension. + // filename = 'workspace' + } + + export = { + // Directory for the exported diagrams. + // + // WARNING: Do not put manually created/changed files into this directory. + // If a valid Structurizr workspace file is found the directory is deleted before the diagram files are generated. + // outputPath = 'src/docs/structurizr/diagrams' + + // Format of the exported diagrams. Defaults to 'plantuml' if the parameter is not provided. + // + // Following formats are supported: + // - 'plantuml': the same as 'plantuml/structurizr' + // - 'plantuml/structurizr': exports views to PlantUML + // - 'plantuml/c4plantuml': exports views to PlantUML with https://github.com/plantuml-stdlib/C4-PlantUML + // format = 'plantuml' + } +} +//end::structurizrConfig[] + +//tag::openAIConfig[] +// Configuration for openAI related tasks +openAI = [:] + +openAI.with { + // This task requires a person access token for openAI. + // Ensure to pass this token as parameters when calling the task + // using -PopenAI.token=xx-xxxxxxxxxxxx + + //model = "text-davinci-003" + //maxToken = '500' + //temperature = '0.3' +} +//end::openAIConfig[] diff --git a/dtcw b/dtcw new file mode 100755 index 000000000..465c13972 --- /dev/null +++ b/dtcw @@ -0,0 +1,690 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT +# Copyright 2022 - 2023, Ralf D. Müller and the docToolchain contributors + +set -e +set -u +set -o pipefail + +# The main purpose of the wrapper script is to make 'docToolchain' easy to use. +# - it helps to install 'docToolchain' if not installed +# - you may manage different 'docToolchain' environments + +# See https://github.com/docToolchain/docToolchain/releases for available versions. +# Set DTC_VERSION to "latest" to get the latest, yet unreleased version. +: "${DTC_VERSION:=3.5.0}" + +# if not set, public docker hub is used +: "${DTC_DOCKER_PREFIX:=}" + +# The 'generateSite' and 'copyThemes' tasks support DTC_SITETHEME, an URL of a theme. +# export DTC_SITETHEME=https://....zip + +# The 'downloadTemplate' tasks uses DTC_TEMPLATE1, DTC_TEMPLATE2, ... +# with which you can specify the location of additional templates +# export DTC_TEMPLATE1=https://....zip +# export DTC_TEMPLATE2=https://....zip + +# docToolchain configuration file may be overruled by the user +: "${DTC_CONFIG_FILE:=docToolchainConfig.groovy}" + +# Contains the current project git branch, "-" if not available +: "${DTC_PROJECT_BRANCH:=$(git branch --show-current 2> /dev/null || echo -)}" +export DTC_PROJECT_BRANCH + +# DTC_HEADLESS is true if no STDIN is open (i.e. we have an non-interactive shell). +: "${DTC_HEADLESS:=$([ -t 0 ] && echo false || echo true)}" +export DTC_HEADLESS + +# Options passed to docToolchain +DTC_OPTS="${DTC_OPTS:-} --warning-mode=none --no-daemon -Dfile.encoding=UTF-8 " +if [ -n "${DTC_CONFIG_FILE}" ]; then + DTC_OPTS="${DTC_OPTS} -PmainConfigFile=${DTC_CONFIG_FILE}" +fi + +# Here you find the project +GITHUB_PROJECT_URL=https://github.com/docToolchain/docToolchain +GITHUB_PROJECT_URL_SSH=git@github.com:docToolchain/docToolchain + +# Bump this version up if something is changed in the wrapper script +DTCW_VERSION=0.51 +# Template replaced by the GitHub value upon releasing dtcw +DTCW_GIT_HASH=##DTCW_GIT_HASH## + +# Exit codes +ERR_DTCW=1 +ERR_ARG=2 +ERR_CODING=3 + +main() { + # For debugging purpose at the top of the script + arch=$(uname -m) + os=$(uname -s) + bash=bash + + print_version_info + + assert_argument_exists "$@" + + [ "${1}" = "--version" ] && exit 0 + + available_environments=$(get_available_environments) + echo "Available docToolchain environments:${available_environments}" + + dtc_installations=$(get_dtc_installations "${available_environments}" "${DTC_VERSION}") + echo "Environments with docToolchain [${DTC_VERSION}]:${dtc_installations}" + + if is_supported_environment "${1}" && assert_environment_available "${1}" "${DTC_VERSION}"; then + # User enforced environment + environment=${1} + shift 1 + assert_argument_exists "$@" + else + environment=$(get_environment "$@") + fi + echo "Using environment: ${environment}" + + if [ "${1}" = "install" ]; then + install_component_and_exit "${environment}" "${2-}" + elif [ "${1}" = "getJava" ]; then + # TODO: remove getJava in the next major release + echo "Warning: 'getJava' is deprecated and will be removed. Use './dtcw install java' instead." + install_component_and_exit "${environment}" "java" + fi + + # No install command, so forward call to docToolchain but first we check if + # everything is there. + docker_image_name="" + docker_extra_arguments="" + if [[ ${environment} != docker ]]; then + assert_doctoolchain_installed "${environment}" "${DTC_VERSION}" + assert_java_version_supported + + # TODO: what if 'doctoolchain' found by $PATH does not match the one from the local environment? + # The version provided by $DTC_VERSION could be a different one. + else + docker_image_name="doctoolchain/doctoolchain" + if [ "${1}" = "image" ]; then + docker_image_name="${2-}" + shift 2 + assert_argument_exists "$@" + fi + echo "Using docker image: ${docker_image_name}" + if [ "${1}" = "extra_arguments" ]; then + docker_extra_arguments="${2-}" + shift 2 + assert_argument_exists "$@" + echo "Extra arguments passed to 'docker run' ${docker_extra_arguments}" + fi + fi + + command=$(build_command "${environment}" "${DTC_VERSION}" "${docker_image_name}" "${docker_extra_arguments}" "$@") + + [[ "${DTC_HEADLESS}" = true ]] && echo "Using headless mode since there is no (terminal) interaction possible" + + show_os_related_info + + emu="" + if [ "${os}" = "Darwin" ] && [ "${arch}" = "arm64" ]; then + echo "Apple silicon detected, using x86_64 mode and os native bash" + emu="/usr/bin/arch -x86_64" + bash="/bin/bash" + fi + + # echo "Command to invoke: ${command}" + # shellcheck disable=SC2086 + # as we hope to know what we are doing here ;-) + exec ${emu} ${bash} ${DTC_SHELL_DEBUG:-} -c "${command}" +} + +assert_argument_exists() { + if [[ $# -lt 1 ]]; then + error "argument missing" + usage + exit ${ERR_ARG} + fi +} + +error() { + printf "\nError: %s\n\n" "${1}" >&2 +} + +usage() { + cat < /dev/null || echo "unknown") + echo "docToolchain ${DTC_VERSION} - ${dtc_git_hash}" + else + echo "docToolchain ${DTC_VERSION}" + fi + echo "OS/arch: ${os}/${arch}" +} + +get_available_environments() { + # The local environment is alway available - even if docToolchain is not installed + local envs=" local" + + # 'sdk' is provided a bash founction - thus command doesn't work + if [ -n "${SDKMAN_DIR:-}" ] && [ -s "${SDKMAN_DIR}/bin/sdkman-init.sh" ]; then + envs+=" sdk" + fi + + if has docker; then + envs+=" docker" + fi + echo "${envs}" +} + +has() { + command -v "$1" 1>/dev/null 2>&1 +} + +get_dtc_installations() { + local envs=${1} + local version=${2} + local installations="" + + if [ -x "${DTC_HOME}/bin/doctoolchain" ]; then + installations+=" local" + else + # maybe it is just available in the path + if command -v doctoolchain >/dev/null 2>&1; then + installations+=" local" + fi + fi + + if [[ "${envs}" =~ sdk ]] && sdk_home_doctoolchain "${version}" &> /dev/null ; then + installations+=" sdk" + fi + + if [[ "${envs}" =~ docker ]]; then + # Having docker installed means docToolchain is available + installations+=" docker" + fi + [ -z "${installations}" ] && installations=" none" + + echo "${installations}" +} + +sdk_home_doctoolchain() { + local version=${1} + local sdk_doctoolchain="${SDKMAN_DIR}/candidates/doctoolchain/${version}" + + # Don't use `sdk home doctoolchain ${version}` - see https://github.com/sdkman/sdkman-cli/issues/1196 + if [[ -x "${sdk_doctoolchain}/bin/doctoolchain" ]]; then + printf "%s" "${sdk_doctoolchain}" + return 0 + fi + printf "doctoolchain %s is not installed on your system" "${version}" 1>&2 + return 1 +} + +is_supported_environment() { + local supported_environments=("local" "sdk" "docker") + [[ "${supported_environments[*]}" =~ ${1} ]] +} + +assert_environment_available() { + local env=${1} + local version=${2} + # If environment not available, exit with error + if ! is_environment_available "${env}" ; then + error "argument error - environment '${env}' not available" + if [[ "${env}" == "sdk" ]]; then + printf "%s\n" \ + "Install SDKMAN! (https://sdkman.io) with" \ + "" \ + " $ curl -s \"https://get.sdkman.io\" | bash" \ + "" \ + "Then open a new shell and install 'docToolchain' with" \ + " $ sdk install doctoolchain ${version}" + else + echo "Install 'docker' on your host to execute docToolchain in a container." + fi + echo + exit ${ERR_ARG} + fi + if is_doctoolchain_development_version "${version}" && [ "${env}" != local ] ; then + error "argument error - invalid environment '${env}'." + echo "Development version '${version}' can only be used in a local environment." + echo + exit ${ERR_ARG} + fi +} + +is_environment_available() { + [[ "${available_environments}" =~ ${1} ]] +} + +is_doctoolchain_development_version() { + local version=${1} + # Is 'latest' a good name? It maybe interpreted as latest stable release. + # Alternatives: 'testing', 'dev' (used for development) + [ "${version}" == "latest" ] || [ "${version}" == "latestdev" ] +} + +# No environment provided - try to pick the right one +get_environment() { + # 'install' works only with 'local' environment + if [[ "${1}" == install ]] || [[ "${dtc_installations}" =~ none ]]; then + echo local + return + fi + + # Pick the first one which has an installed docToolchain. + # Note: the preference is defined by the order we searched for available environments. + for e in ${available_environments}; do + if is_doctoolchain_installed "${e}"; then + echo "${e}" + return + fi + done +} + +is_doctoolchain_installed() { + [[ "${dtc_installations}" =~ ${1} ]] +} + +install_component_and_exit() { + local env=${1} + local component=${2} + if [ -z "${component}" ] ; then + error_install_component_and_die "component missing" + fi + exit_code=1 + case ${env} in + local) + case ${component} in + doctoolchain) + local_install_doctoolchain "${DTC_VERSION}" + assert_java_version_supported + ;; + java) + local_install_java + ;; + *) + error_install_component_and_die "unknown component '${component}'" + ;; + esac + if ! is_doctoolchain_installed "${environment}"; then + how_to_install_doctoolchain "${DTC_VERSION}" + else + printf "%s\n" \ + "" \ + "Use './dtcw tasks --group doctoolchain' to see docToolchain related tasks." \ + "" + fi + exit_code=0 + ;; + sdk) + error "argument error - '${env} install' not supported." + printf "%s\n" \ + "To install docToolchain with SDKMAN! execute" \ + "" \ + " $ sdk install doctoolchain ${DTC_VERSION}" \ + "" + exit_code=${ERR_ARG} + ;; + docker) + error "argument error - '${env} install' not supported." + echo "Executing a task in the 'docker' environment will pull the docToolchain container image." + echo + exit_code=${ERR_ARG} + ;; + *) + echo "Coding error - not reachable" + exit ${ERR_CODING} + ;; + esac + exit ${exit_code} +} + +error_install_component_and_die() { + error "${1} - available components are 'doctoolchain', or 'java'" + printf "%s\n" \ + "Use './dtcw local install doctoolchain' to install docToolchain ${DTC_VERSION}." \ + "Use './dtcw local install java' to install a Java version supported by docToolchain." \ + "" + exit ${ERR_ARG} +} + +local_install_doctoolchain() { + local version=${1} + if is_doctoolchain_development_version "${version}"; then + # User decided to pick a floating version - which means a git clone into the local environment. + assert_git_installed "Please install 'git' for working with a 'doctToolchain' development version" + + if [ -d "${DTC_HOME}/.git" ]; then + git -C "${DTC_HOME}" pull + echo "Updated docToolchain in local environment to latest version" + else + local project_url + if [ "${version}" == "latest" ]; then + project_url="${GITHUB_PROJECT_URL}" + else + project_url="${GITHUB_PROJECT_URL_SSH}" + fi + git clone "${project_url}.git" "${DTC_HOME}" + echo "Cloned docToolchain in local environment to latest version" + fi + else + if [ -x "${DTC_HOME}/bin/doctoolchain" ]; then + echo "Skipped installation of docToolchain: already installed in '${DTC_HOME}'" + return + fi + if ! has unzip; then + error "no unzip program installed, exiting…" + echo "Install 'unzip' and try to install docToolchain again." + return ${ERR_DTCW} + fi + mkdir -p "${DTC_ROOT}" + local distribution_url=${GITHUB_PROJECT_URL}/releases/download/v${version}/docToolchain-${version}.zip + download_file "${distribution_url}" "${DTC_ROOT}/source.zip" + unzip -q "${DTC_ROOT}/source.zip" -d "${DTC_ROOT}" + rm -f "${DTC_ROOT}/source.zip" + echo "Installed docToolchain successfully in '${DTC_HOME}'." + fi + + # Add it to the existing installations so the output to guide the user can adjust accordingly. + dtc_installations+=" local" +} + +assert_git_installed() { + has git && return + + error "git - command not found" + echo "${1}" + echo + exit ${ERR_DTCW} +} + +download_file() { + local url=${1} + local file=${2} + + if has curl; then + cmd="curl --fail --location --output $file $url" + elif has wget; then + # '--show-progress' is not supported in all wget versions (busybox) + cmd="wget --quiet --show-progress --output-document=$file $url" + elif has fetch; then + cmd="fetch --quiet --output=$file $url" + else + error "no HTTP download program (curl, wget, fetch) found, exiting…" + echo "Install either 'curl', 'wget', or 'fetch' and try to install docToolchain again." + return ${ERR_DTCW} + fi + + $cmd && return 0 || rc=$? + + error "Command failed (exit code $rc): ${cmd}" + return "${rc}" +} + +assert_java_version_supported() { + # Defines the order in which Java is searched. + if [ -d "${DTC_JAVA_HOME}" ]; then + echo "Caution: Your JAVA_HOME setting is overriden by DTCs own JDK install (for this execution)" + if [ -d "${DTC_JAVA_HOME}/Contents" ]; then + # JDK for MacOS have a different structure + JAVA_HOME="${DTC_JAVA_HOME}/Contents/Home" + else + JAVA_HOME="${DTC_JAVA_HOME}" + fi + export JAVA_HOME + DTC_OPTS="${DTC_OPTS} '-Dorg.gradle.java.home=${JAVA_HOME}'" + JAVA_CMD="${JAVA_HOME}/bin/java" + elif [ -n "${JAVA_HOME-}" ]; then + JAVA_CMD="${JAVA_HOME}/bin/java" + else + # Don't provide JAVA_HOME if java is used by PATH. + JAVA_CMD=$(command -v java 2>/dev/null) || true + fi + if [ -z "${JAVA_CMD-}" ] || ! java_version=$("${JAVA_CMD}" -version 2>&1) ; then + error "unable to locate a Java Runtime" + java_help_and_die + fi + + # We got a Java version + java_version=$(echo "$java_version" | awk -F '"' '/version/ {print $0}' | head -1 | cut -d'"' -f2) + major_version=$(echo "$java_version" | sed '/^1\./s///' | cut -d'.' -f1) + + if [ "${major_version}" -eq 17 ]; then + echo "Using Java ${java_version} [${JAVA_CMD}]" + return + fi + + error "unsupported Java version ${major_version} [${JAVA_CMD}]" + java_help_and_die +} + +java_help_and_die() { + printf "%s\n" \ + "docToolchain supports Java version 17 only. In case that" \ + "Java version is installed make sure 'java' is found with your PATH environment" \ + "variable. As alternative you may provide the location of your Java installation" \ + "with JAVA_HOME." \ + "" \ + "Apart from installing Java with the package manager provided by your operating" \ + "system, dtcw facilitates the Java installation into a local environment:" \ + "" \ + " # Install Java in '${DTC_JAVA_HOME}'" \ + " $ ./dtcw local install java" \ + "" \ + "Alternatively you can use SDKMAN! (https://sdkman.io) to manage your Java installations" \ + "" + if ! is_environment_available sdk; then + how_to_install_sdkman "Java 17" + fi + # TODO: This will break when we change Java version + printf "%s\n" \ + " $ sdk install java 17.0.14-tem" \ + "" \ + "If you prefer not to install Java on your host, you can run docToolchain in a" \ + "docker container. For this case dtcw provides the 'docker' execution environment." \ + "" \ + "Example: ./dtcw docker generateSite"\ + "" + exit ${ERR_DTCW} +} + +how_to_install_sdkman() { + printf "%s\n" \ + " # First install SDKMAN!" \ + " $ curl -s \"https://get.sdkman.io\" | bash" \ + " # Then open a new shell and install ${1} with" +} + +local_install_java() { + version=17 + implementation=hotspot + heapsize=normal + imagetype=jdk + releasetype=ga + + case "${arch}" in + x86_64) arch=x64 ;; + arm64) arch=aarch64 ;; + esac + if [[ ${os} == MINGW* ]]; then + error "MINGW64 is not supported" + echo "Please use powershell or WSL" + exit 1 + fi + case "${os}" in + Linux) os=linux ;; + Darwin) os=mac + # Enforce usage of Intel Java as long as jbake does not work on Apple Silicon + arch=x64 ;; + Cygwin) os=linux ;; + esac + mkdir -p "${DTC_JAVA_HOME}" + echo "Downloading JDK Temurin ${version} [${os}/${arch}] from Adoptium to ${DTC_JAVA_HOME}/jdk.tar.gz" + local adoptium_java_url="https://api.adoptium.net/v3/binary/latest/${version}/${releasetype}/${os}/${arch}/${imagetype}/${implementation}/${heapsize}/eclipse?project=jdk" + download_file "${adoptium_java_url}" "${DTC_JAVA_HOME}/jdk.tar.gz" + echo "Extracting JDK from archive file." + # TODO: should we not delete a previsouly installed on? + # Otherwise we may get a mix of different Java versions. + tar -zxf "${DTC_JAVA_HOME}/jdk.tar.gz" --strip 1 -C "${DTC_JAVA_HOME}/." + rm -f "${DTC_JAVA_HOME}/jdk/jdk.tar.gz" + + echo "Successfully installed Java in '${DTC_JAVA_HOME}'." + echo +} + +assert_doctoolchain_installed() { + local env=${1} + local version=${2} + if ! is_doctoolchain_installed "${env}"; then + # We reach this point if the user executes a command in an + # environment where docToolchain is not installed. + # Note that 'docker' always has a command (no instalation required) + error "doctoolchain - command not found [environment '${env}']" + how_to_install_doctoolchain "${version}" + exit ${ERR_DTCW} + fi +} + +how_to_install_doctoolchain() { + local version=${1} + printf "%s\n" \ + "It seems docToolchain ${version} is not installed. dtcw supports the" \ + "following docToolchain environments:" \ + "" \ + "1. 'local': to install docToolchain in [${DTC_ROOT}] use" \ + "" \ + " $ ./dtcw local install doctoolchain" \ + "" \ + "2. 'sdk': to install docToolchain with SDKMAN! (https://sdkman.io)" \ + "" + if ! is_environment_available sdk; then + how_to_install_sdkman docToolchain + fi + printf "%s\n" \ + " \$ sdk install doctoolchain ${version}" \ + "" \ + "Note that running docToolchain in 'local' or 'sdk' environment needs a" \ + "Java runtime major version 17 installed on your host." \ + "" \ + "3. 'docker': pull the docToolchain image and execute docToolchain in a container environment." \ + "" \ + " \$ ./dtcw docker tasks --group doctoolchain" \ + "" +} + +build_command() { + local env=${1} + local version=${2} + local docker_image=${3} + local docker_extra_arguments=${4} + shift 4 + local cmd + if [ "${env}" = docker ]; then + # TODO: DTC_PROJECT_BRANCH is not passed into the docker environment + # See https://github.com/docToolchain/docToolchain/issues/1087 + + local container_name=doctoolchain-${version} + container_name+="-$(date '+%Y%m%d_%H%M%S')" + pwd=$(has cygpath && cygpath -w "${PWD}" || echo "${PWD}") + + docker_env_file=dtcw_docker.env + env_file_option="" + if [ -f "$docker_env_file" ]; then + env_file_option="--env-file ${docker_env_file}" + fi + + docker_args="run --rm -i --platform linux/amd64 -u $(id -u):$(id -g) --name ${container_name} \ + -e DTC_HEADLESS=true -e DTC_SITETHEME -e DTC_PROJECT_BRANCH=${DTC_PROJECT_BRANCH} \ + ${docker_extra_arguments} ${env_file_option} \ + --entrypoint /bin/bash -v '${pwd}:/project' ${DTC_DOCKER_PREFIX}${docker_image}:v${version}" + + cmd="docker ${docker_args} -c \"doctoolchain . ${*} ${DTC_OPTS} && exit\"" + else + # TODO: What is this good for? Has probably to do with Docker image creation. + if [[ "${DTC_HEADLESS}" = false ]]; then + # important for the docker file to find dependencies + DTC_OPTS="${DTC_OPTS} '-Dgradle.user.home=${DTC_ROOT}/.gradle'" + fi + if [ "${env}" = local ]; then + if [ -x "${DTC_HOME}/bin/doctoolchain" ]; then + cmd="${DTC_HOME}/bin/doctoolchain" + # is doctoolchain available on the path? + elif command -v doctoolchain >/dev/null 2>&1; then + cmd="doctoolchain" + else + echo "Could not find doctoolchain executable, neither in '${DTC_HOME}' nor on PATH ('${PATH}')" >&2 + exit 1 + fi + cmd="${cmd} . ${*} ${DTC_OPTS}" + else + cmd="$(sdk_home_doctoolchain "${version}")/bin/doctoolchain . ${*} ${DTC_OPTS}" + fi + fi + echo "${cmd}" +} + +show_os_related_info() { + + # check if we are running on WSL + if grep -qsi 'microsoft' /proc/version &> /dev/null ; then + # TODO: bug - This URL leads to now-where! + printf "%s\n" \ + "" \ + "Bash is running on WSL which might cause problems with plantUML." \ + "See https://doctoolchain.org/docToolchain/v2.0.x/010_manual/50_Frequently_asked_Questions.html#wsl for more details." \ + "" + fi +} + +# Location where local installations are placed. +DTC_ROOT="${HOME}/.doctoolchain" + +# More than one docToolchain version may be installed locally. +# This is the directory for the specific version. +DTC_HOME="${DTC_ROOT}/docToolchain-${DTC_VERSION}" + +# Directory for local Java installation +DTC_JAVA_HOME="${DTC_ROOT}/jdk" + +main "$@" diff --git a/src/docs/adrs/adr-001-hexagonal-ports.adoc b/src/docs/adrs/adr-001-hexagonal-ports.adoc new file mode 100644 index 000000000..83cdb5940 --- /dev/null +++ b/src/docs/adrs/adr-001-hexagonal-ports.adoc @@ -0,0 +1,60 @@ += ADR-001: Hexagonal Ports and Adapters +:jbake-title: ADR-001 Hexagonal Ports and Adapters +:jbake-type: page +:jbake-status: published +ifndef::imagesdir[:imagesdir: ../images] + +== Status + +*Accepted (inferred).* The decision is evident in code; the rationale was +not recoverable and is deferred to the Architect. + +== Context + +The Vibe engine integrates many external concerns: LLM providers, audio +capture and playback, text-to-speech, transcription, HTTP gateways and +the experiments service. The code base consistently expresses each as an +abstract interface with a `_port.py` suffix, with concrete adapters at +the edges [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L13-L23[AGENTS.md:13-23]]. Examples in code: +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/base.py#L13-L126[vibe/core/llm/backend/base.py:13-126] (the `BackendLike` port), +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/audio_recorder/audio_recorder_port.py[vibe/core/audio_recorder/audio_recorder_port.py], +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tts/tts_client_port.py[vibe/core/tts/tts_client_port.py], +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/plan_offer/ports/whoami_gateway.py[vibe/cli/plan_offer/ports/whoami_gateway.py]. The dependency direction +points inward toward the domain. + +== Decision + +Structure the engine as a hexagonal (ports-and-adapters) architecture: +the domain depends only on `_port.py` interfaces; concrete adapters +(SDK clients, HTTP gateways, OS audio) implement those ports and are +wired in at the boundary. + +== Consequences + +* Positive: adapters are swappable and the domain is unit-testable with + `Fake*` doubles [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L88-L92[AGENTS.md:88-92]]. +* Positive: the front ends (CLI, ACP) reuse one engine. +* Negative: more indirection — a reader must follow a port to its + adapter. This is a contributor to *TD-004* where the UI layer adds + many widget modules on top of the ports. +* Risk: none specific to this decision beyond the general maintenance + cost; no Chapter 11 risk is created. Accepted without a tracked risk. + +== Pugh Matrix + +Baseline (score 0): *Direct coupling to concrete classes.* + +[options="header",cols="2,1,1,1"] +|=== +|Criterion |Direct coupling (baseline) |Hexagonal ports (chosen) |Framework DI container + +|Maintainability |0 |+1 |+1 +|Testability |0 |+1 |+1 +|Usability (contributor onboarding) |0 |-1 |? +|Compatibility (swap providers) |0 |+1 |+1 +|Reliability |0 |0 |0 +|*Sum* |*0* |*+2* |*?* +|=== + +`?` cells need the team's weighting — whether a DI container would help +or hurt onboarding is a judgment the code cannot settle. diff --git a/src/docs/adrs/adr-002-llm-backend-abstraction.adoc b/src/docs/adrs/adr-002-llm-backend-abstraction.adoc new file mode 100644 index 000000000..95faa9395 --- /dev/null +++ b/src/docs/adrs/adr-002-llm-backend-abstraction.adoc @@ -0,0 +1,59 @@ += ADR-002: Pluggable LLM Backend Abstraction +:jbake-title: ADR-002 Pluggable LLM Backend Abstraction +:jbake-type: page +:jbake-status: published +ifndef::imagesdir[:imagesdir: ../images] + +== Status + +*Accepted (inferred).* Evident in code; rationale deferred to the +Architect. + +== Context + +Vibe is a Mistral-branded tool, but the code supports more than the +Mistral API. `BACKEND_FACTORY` maps a provider enum to a backend class — +`MistralBackend` or the OpenAI-compatible `GenericBackend` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7]]. The generic backend loads an +API-style adapter so the same HTTP path serves OpenAI, Anthropic and +Vertex request/response shapes, including reasoning-effort handling +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/generic.py#L188-L447[vibe/core/llm/backend/generic.py:188-447], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/format.py#L58-L185[vibe/core/llm/format.py:58-185]]. +All backends satisfy one `BackendLike` protocol — `complete`, +`complete_streaming`, `count_tokens` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/base.py#L13-L126[vibe/core/llm/backend/base.py:13-126]]. + +== Decision + +Define a single `BackendLike` port and select a provider-specific +backend through a factory; cover non-Mistral request shapes with +pluggable API-style adapters rather than separate backend classes. + +== Consequences + +* Positive: the agent loop is provider-agnostic; a new provider is a new + adapter, not a loop change. +* Positive: supports the Compatibility quality goal + (<<../arc42/chapters/04_solution_strategy.adoc#,Chapter 4>>). +* Negative: provider-specific quirks (reasoning effort, tool-choice + encoding, token counting) spread across adapters. +* Risk: a sustained outage of the configured provider has unspecified + end-state behaviour — this is *R-009* in Chapter 11. + +== Pugh Matrix + +Baseline (score 0): *Single-provider (Mistral SDK only).* + +[options="header",cols="2,1,1,1"] +|=== +|Criterion |Single-provider (baseline) |Backend factory + adapters (chosen) |Third-party multi-LLM library + +|Compatibility |0 |+1 |+1 +|Maintainability |0 |0 |? +|Reliability |0 |0 |? +|Security (dependency surface) |0 |0 |-1 +|Usability (config simplicity) |0 |-1 |-1 +|*Sum* |*0* |*0* |*?* +|=== + +`?` cells need the team's view — whether a third-party library would be +easier to maintain or more reliable than hand-rolled adapters depends on +that library's track record, which the code cannot tell us. diff --git a/src/docs/adrs/adr-003-middleware-pipeline.adoc b/src/docs/adrs/adr-003-middleware-pipeline.adoc new file mode 100644 index 000000000..7d09242b4 --- /dev/null +++ b/src/docs/adrs/adr-003-middleware-pipeline.adoc @@ -0,0 +1,59 @@ += ADR-003: Conversation Middleware Pipeline +:jbake-title: ADR-003 Conversation Middleware Pipeline +:jbake-type: page +:jbake-status: published +ifndef::imagesdir[:imagesdir: ../images] + +== Status + +*Accepted (inferred).* Evident in code; rationale deferred to the +Architect. + +== Context + +The agent loop must enforce several orthogonal turn-level policies: a +turn limit, a price ceiling, auto-compaction near the context limit, a +context warning, and read-only behaviour for the `plan` and `chat` +agents. The code expresses each as a `ConversationMiddleware` with a +`before_turn` method; a `MiddlewarePipeline` runs them ahead of every +LLM turn and returns an action — `CONTINUE`, `STOP`, `COMPACT` or +`INJECT_MESSAGE` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L43-L247[vibe/core/middleware.py:43-247]]. The loop assembles +the pipeline from constructor flags [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L722-L757[vibe/core/agent_loop.py:722-757]]. + +== Decision + +Enforce all turn-level policies through a composable `before_turn` +middleware pipeline, rather than inline conditionals in the loop or +provider-SDK callbacks. + +== Consequences + +* Positive: each policy is one small, independently testable class; the + loop body stays focused on the LLM-call/tool-call cycle. +* Positive: directly realises the Reliability and Security mechanisms in + <<../arc42/chapters/08_concepts.adoc#,Chapter 8>> (limits, compaction, + read-only modes). +* Negative: control flow is indirect — a `COMPACT` result is handled far + from the middleware that raised it. +* Risk: the price ceiling depends on `session_cost`, which is a rough + estimate — this is technical debt *TD-001* in Chapter 11; the + `--max-price` guarantee is therefore approximate. + +== Pugh Matrix + +Baseline (score 0): *Inline policy checks in the agent loop.* + +[options="header",cols="2,1,1,1"] +|=== +|Criterion |Inline checks (baseline) |Middleware pipeline (chosen) |Provider-SDK callbacks + +|Maintainability |0 |+1 |0 +|Testability |0 |+1 |-1 +|Reliability |0 |+1 |? +|Compatibility (provider-agnostic) |0 |+1 |-1 +|Usability (code readability) |0 |0 |0 +|*Sum* |*0* |*+4* |*?* +|=== + +The `?` cell needs the team's judgment — whether provider-SDK callbacks +would be reliable enough depends on each SDK's guarantees. diff --git a/src/docs/adrs/adr-004-file-based-sessions.adoc b/src/docs/adrs/adr-004-file-based-sessions.adoc new file mode 100644 index 000000000..5b0ee794a --- /dev/null +++ b/src/docs/adrs/adr-004-file-based-sessions.adoc @@ -0,0 +1,62 @@ += ADR-004: File-based Session Persistence +:jbake-title: ADR-004 File-based Session Persistence +:jbake-type: page +:jbake-status: published +ifndef::imagesdir[:imagesdir: ../images] + +== Status + +*Accepted (inferred).* Evident in code; rationale deferred to the +Architect. + +== Context + +Vibe persists conversations so they can be continued, resumed and +rewound. The code stores each session as a folder named +`{prefix}_{YYYYMMDD_HHMMSS}_{short-id}` containing `meta.json` +(metadata, title, working directory, parent session id) and +`messages.jsonl` (the conversation, one message per line) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_loader.py#L16-L17[vibe/core/session/session_loader.py:16-17], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L62-L97[vibe/core/session/session_logger.py:62-97]]. +A migration converts a pre-2.0 single-JSON format to this folder layout +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_migration.py#L16-L41[vibe/core/session/session_migration.py:16-41]]. Sessions live under +`~/.vibe/logs/session/` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_vibe_home.py#L30[vibe/core/paths/_vibe_home.py:30]]. + +== Decision + +Persist each session as a plain-file folder — `meta.json` plus an +append-friendly `messages.jsonl` — on the local filesystem, rather than +in an embedded database or a single JSON document. + +== Consequences + +* Positive: append-only `messages.jsonl` is cheap to write per message + and human-inspectable; no database dependency is added. +* Positive: rewind and compaction fork a session simply by writing a new + folder with a `parent_session_id`. +* Negative: a format change requires an in-code migration — the v1→v2 + migration is technical debt *TD-003* in Chapter 11. +* Negative: there is no index; locating sessions scans and sorts the + directory by mtime [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_loader.py#L64-L104[vibe/core/session/session_loader.py:64-104]]. +* Risk: no Chapter 11 risk is created; the scaling concern of long + sessions is bounded by auto-compaction and is recorded as the low + risk *R-010*. + +== Pugh Matrix + +Baseline (score 0): *Single JSON file per session.* + +[options="header",cols="2,1,1,1"] +|=== +|Criterion |Single JSON file (baseline) |Folder + JSONL (chosen) |Embedded SQLite database + +|Performance (per-message write) |0 |+1 |+1 +|Maintainability |0 |0 |-1 +|Reliability (crash safety) |0 |+1 |+1 +|Usability (inspectability) |0 |+1 |-1 +|Portability (no extra dependency) |0 |0 |0 +|*Sum* |*0* |*+3* |*0* +|=== + +No `?` cells: the comparison is decidable from the recorded behaviour. +The remaining open point is *why* a database was not chosen, which is +the deferred rationale, not a Pugh score. diff --git a/src/docs/arc42/.asciidoctorconfig.adoc b/src/docs/arc42/.asciidoctorconfig.adoc new file mode 100644 index 000000000..e0bfb9168 --- /dev/null +++ b/src/docs/arc42/.asciidoctorconfig.adoc @@ -0,0 +1,2 @@ +:imagesdir: ../images + diff --git a/src/docs/arc42/arc42.adoc b/src/docs/arc42/arc42.adoc new file mode 100644 index 000000000..28c4814be --- /dev/null +++ b/src/docs/arc42/arc42.adoc @@ -0,0 +1,73 @@ +:imagesdir: ../images +:jbake-menu: - +// header file for arc42-template, +// including all help texts +// +// ==================================== + +// configure EN settings for asciidoc +include::chapters/config.adoc[] + += Vibe — Architecture Documentation +:revnumber: {revnumber} +:revdate: {revdate} +:revremark: {revremark} +:toc-title: Table of Contents + +// Generated by Socratic Code-Theory Recovery (Phase 2) from +// QUESTION_TREE.adoc. Code-derived claims cite file:line evidence. +// Bounded context: vibe/ — the mistral-vibe Python package. + + +// numbering from here on +:numbered: + +<<<< +// 1. Introduction and Goals +include::chapters/01_introduction_and_goals.adoc[] + +<<<< +// 2. Architecture Constraints +include::chapters/02_architecture_constraints.adoc[] + +<<<< +// 3. Context and Scope +include::chapters/03_context_and_scope.adoc[] + +<<<< +// 4. Solution Strategy +include::chapters/04_solution_strategy.adoc[] + +<<<< +// 5. Building Block View +include::chapters/05_building_block_view.adoc[] + +<<<< +// 6. Runtime View +include::chapters/06_runtime_view.adoc[] + +<<<< +// 7. Deployment View +include::chapters/07_deployment_view.adoc[] + +<<<< +// 8. Concepts +include::chapters/08_concepts.adoc[] + +<<<< +// 9. Architecture Decisions +include::chapters/09_architecture_decisions.adoc[] + +<<<< +// 10. Quality Requirements +include::chapters/10_quality_requirements.adoc[] + +<<<< +// 11. Technical Risks +include::chapters/11_technical_risks.adoc[] + +<<<< +// 12. Glossary +include::chapters/12_glossary.adoc[] + + diff --git a/src/docs/arc42/chapters/.asciidoctorconfig.adoc b/src/docs/arc42/chapters/.asciidoctorconfig.adoc new file mode 100644 index 000000000..f51344152 --- /dev/null +++ b/src/docs/arc42/chapters/.asciidoctorconfig.adoc @@ -0,0 +1,2 @@ +:imagesdir: ../../images + diff --git a/src/docs/arc42/chapters/01_introduction_and_goals.adoc b/src/docs/arc42/chapters/01_introduction_and_goals.adoc new file mode 100644 index 000000000..0451ae46a --- /dev/null +++ b/src/docs/arc42/chapters/01_introduction_and_goals.adoc @@ -0,0 +1,128 @@ +:jbake-title: Introduction and Goals +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 1 +:filename: /chapters/01_introduction_and_goals.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-introduction-and-goals]] +== Introduction and Goals + +Vibe is Mistral's minimal CLI coding agent: a terminal coding assistant +that gives a conversational interface to a codebase, powered by Mistral's +models [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L5[pyproject.toml:5], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L20-L22[README.md:20-22]]. It ships two entry points — +link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] (an interactive Textual TUI, also runnable non-interactively) and +`vibe-acp` (an Agent Client Protocol server for editors and IDEs) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L143-L144[pyproject.toml:143-144]]. + +=== Requirements Overview + +The system lets a developer explore, modify and run a project through +natural language, backed by a safe tool suite [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L92-L109[README.md:92-109]]. Its +core capabilities: + +* Run an interactive coding session driven by an agent loop that calls + the LLM, then executes tool calls, until the assistant produces no + further tool call [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L855-L937[vibe/core/agent_loop.py:855-937]]. +* Run non-interactively (link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] -p) for scripting and CI, with turn and + cost limits and machine-readable output + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27-L91[vibe/core/programmatic.py:27-91], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/output_formatters.py#L46-L119[vibe/core/output_formatters.py:46-119]]. +* Provide a built-in tool suite: shell, file read/write/patch, code + search, todo, subagent delegation, interactive questions, web fetch + and search [link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins[vibe/core/tools/builtins/]]. +* Extend the agent through MCP servers, skills, custom agent profiles + and hooks [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L340-L579[README.md:340-579]]. +* Persist, resume and rewind sessions + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L36-L97[vibe/core/session/session_logger.py:36-97]]. + +The full functional specification — persona use cases and per-interface +system use cases — is in `../../use-cases-vibe.adoc`; the product framing +is in `../../prd-vibe.adoc`. + +=== Quality Goals + +The five quality goals below are derived from the emphasis the code and +README place on them. Their *priority ranking* could not be recovered +from code and is **deferred** — the Product Owner and Architect must +supply it before it can drive trade-off decisions. + +[options="header",cols="1,3,2"] +|=== +|Quality Goal |Motivation |Chapter 4 Approach + +|Security / Safety +|"Safety first" is an explicit README theme; tool execution is gated by +a tiered permission system and a trust-folder gate +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L108[README.md:108], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. +|Tiered tool permissions, trust gate, workdir boundary +(see <>). + +|Usability +|A rich Textual TUI, a first-run onboarding wizard and autocompletion +target a low-friction developer experience +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L327-L348[vibe/cli/textual_ui/app.py:327-348], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/onboarding/screens/welcome.py#L46-L80[vibe/setup/onboarding/screens/welcome.py:46-80]]. +|Textual TUI, onboarding wizard, slash-command and path autocompletion. + +|Maintainability +|Strict pyright and ruff gate CI; hexagonal ports decouple the engine +from adapters [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L24-L69[AGENTS.md:24-69], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/base.py#L13-L126[vibe/core/llm/backend/base.py:13-126]]. +|Hexagonal ports/adapters, strict static analysis, test doubles. + +|Compatibility +|The agent works against multiple LLM providers and integrates the MCP +and ACP protocols and the agentskills.io standard +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78[vibe/acp/entrypoint.py:78]]. +|LLM backend factory, MCP registry, ACP bridge. + +|Reliability +|The LLM call retries with backoff and the loop recovers from context +overflow and user interruption +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L212-L222[vibe/core/llm/backend/mistral.py:212-222], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1681-L1749[vibe/core/agent_loop.py:1681-1749]]. +|Retry/backoff, middleware limits, auto-compaction recovery. +|=== + +NOTE: Chapter 10 elaborates all eight ISO/IEC 25010 characteristics with +measurable scenarios; this section names only the top goals that drive +architecture decisions. + +=== Stakeholders + +[options="header",cols="1,2,2"] +|=== +|Role |Contact |Expectations + +|Interactive developer +|_(team to supply)_ +|A trustworthy conversational agent that explores and edits the codebase +with explicit approval [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L322[vibe/cli/textual_ui/app.py:322]]. + +|Scripter / CI automation +|_(team to supply)_ +|Deterministic non-interactive runs with turn and cost ceilings and +machine-readable output [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27[vibe/core/programmatic.py:27]]. + +|Editor / IDE integrator +|_(team to supply)_ +|A stable ACP server to embed the agent in an editor +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108]]. + +|Product Owner +|_(team to supply)_ +|Owns the deferred business questions: why Vibe was built, success KPIs, +and segment priority. +|=== + +[NOTE] +.Open issues — deferred to the team +==== +The following could not be recovered from code and are **deferred**: + +* The business rationale for building an open-source CLI agent and its + relation to the paid Mistral Code / Vibe Code offering. +* The success KPI (adoption, retention, task-completion, conversion). +* Which user segment — interactive, programmatic or ACP — is the priority. +* The priority ranking of the quality goals above. +==== diff --git a/src/docs/arc42/chapters/02_architecture_constraints.adoc b/src/docs/arc42/chapters/02_architecture_constraints.adoc new file mode 100644 index 000000000..ffdb6a070 --- /dev/null +++ b/src/docs/arc42/chapters/02_architecture_constraints.adoc @@ -0,0 +1,76 @@ +:jbake-title: Architecture Constraints +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 2 +:filename: /chapters/02_architecture_constraints.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-architecture-constraints]] +== Architecture Constraints + +=== Technical Constraints + +[options="header",cols="1,3"] +|=== +|Constraint |Evidence / Rationale + +|Python >= 3.12 +|`requires-python = ">=3.12"` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L6[pyproject.toml:6]]; pinned in +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.python-version[.python-version]. + +|`uv` is the package and run manager +|All commands go through `uv`; bare `python`/`pip` are forbidden +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L7-L12[AGENTS.md:7-12]]. + +|Strict static typing +|Pyright runs in strict mode and gates CI; no inline `# type: ignore` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L24-L46[AGENTS.md:24-46]]. + +|Ruff lint and format gate every change +|`ruff check` and `ruff format` run after every code change +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L9-L10[AGENTS.md:9-10]]. + +|No relative imports +|`ban-relative-imports = "all"`; always `from vibe.core.x import …` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L48-L50[AGENTS.md:48-50]]. + +|`asyncio` is the orchestration runtime +|The agent loop and tool execution are async; streaming surfaces return +`AsyncGenerator` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L62-L69[AGENTS.md:62-69]]. + +|UNIX is the supported target +|Vibe runs on Windows but UNIX environments are the officially supported +and targeted platform [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L24-L26[README.md:24-26]]. +|=== + +=== Organisational and Convention Constraints + +[options="header",cols="1,3"] +|=== +|Constraint |Evidence / Rationale + +|Hexagonal module conventions +|Abstract interfaces use the `_port.py` suffix; private modules are +`_`-prefixed; Pydantic models live in `models.py` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L13-L23[AGENTS.md:13-23]]. + +|Pydantic for all external data +|External data is parsed via `model_validate` / validators, never ad-hoc +`getattr`/`hasattr` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L53-L61[AGENTS.md:53-61]]. + +|Conventional Commits, no force-push +|`git commit --amend`, `git push --force` and force-with-lease are +forbidden; rebase, never merge, on rejected push [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L81-L86[AGENTS.md:81-86]]. + +|Apache-2.0 licence +|The project is licensed Apache-2.0 [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L7[pyproject.toml:7], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/LICENSE[LICENSE]]. +|=== + +=== Conventions + +Documentation follows Docs-as-Code with AsciiDoc built by docToolchain; +this architecture document is the arc42 template filled in place. Code +conventions are recorded in link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md[AGENTS.md] at the repository root and are +binding for both human and AI contributors [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L1-L12[AGENTS.md:1-12]]. diff --git a/src/docs/arc42/chapters/03_context_and_scope.adoc b/src/docs/arc42/chapters/03_context_and_scope.adoc new file mode 100644 index 000000000..4a6d8c921 --- /dev/null +++ b/src/docs/arc42/chapters/03_context_and_scope.adoc @@ -0,0 +1,108 @@ +:jbake-title: Context and Scope +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 3 +:filename: /chapters/03_context_and_scope.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-context-and-scope]] +== Context and Scope + +Vibe is a client-side tool that runs on the developer's machine. The +system boundary in this chapter is the same one used in +<>: the building blocks there are internal +to this boundary, the systems below sit outside it. + +=== Business Context + +[plantuml,context-business,svg] +---- +@startuml +!include + +Person(dev, "Interactive Developer", "Explores and edits a codebase conversationally") +Person(scripter, "Scripter / CI", "Runs the agent non-interactively") +Person(integrator, "Editor / IDE Integrator", "Embeds the agent via ACP") + +System(vibe, "Vibe", "Minimal CLI coding agent") + +System_Ext(llm, "LLM Provider", "Mistral API or an OpenAI-compatible / Anthropic / Vertex endpoint") +System_Ext(vibecode, "Vibe Code / Nuage", "Remote cloud workflow backend") + +Rel(dev, vibe, "Asks to explore / modify code") +Rel(scripter, vibe, "Runs vibe -p with a prompt") +Rel(integrator, vibe, "Drives vibe-acp over ACP") +Rel(vibe, llm, "Sends conversation, receives tool calls") +Rel(vibe, vibecode, "Teleports a session to the cloud") +@enduml +---- + +The developer states a coding goal; Vibe turns it into LLM calls and tool +executions and reports the result. A scripter drives the same engine +non-interactively. An integrator embeds the agent in an editor through +the ACP server. The LLM Provider supplies the agent's reasoning; Vibe +Code / Nuage optionally continues a session in the cloud +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27[vibe/core/programmatic.py:27], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150]]. + +=== Technical Context + +[plantuml,context-technical,svg] +---- +@startuml +!include + +System(vibe, "Vibe", "Minimal CLI coding agent") + +System_Ext(llm, "LLM Provider API", "Chat completions, streaming, token counting") +System_Ext(datalake, "Mistral Datalake", "Usage telemetry sink") +System_Ext(growthbook, "GrowthBook", "Feature-flag / experiment evaluation") +System_Ext(mcp, "MCP Servers", "External tool providers (http / stdio)") +System_Ext(vibecode, "Vibe Code / Nuage", "Remote workflow backend") +System_Ext(github, "GitHub", "Device-flow auth, update checks") +System_Ext(pypi, "PyPI", "Package distribution, update checks") + +Rel(vibe, llm, "HTTPS — chat / stream / count_tokens") +Rel(vibe, datalake, "HTTPS — usage events", "gated by enable_telemetry") +Rel(vibe, growthbook, "HTTPS — experiment eval", "fail-open") +Rel(vibe, mcp, "http / streamable-http / stdio") +Rel(vibe, vibecode, "HTTPS + SSE — workflow events") +Rel(vibe, github, "HTTPS — OAuth device flow, releases") +Rel(vibe, pypi, "HTTPS — latest version") +@enduml +---- + +[options="header",cols="1,1,2"] +|=== +|External System |Channel |Code Evidence + +|LLM Provider API +|HTTPS (Mistral SDK or generic HTTP) +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L1-L7[vibe/core/llm/backend/factory.py:1-7], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L268-L335[vibe/core/llm/backend/mistral.py:268-335] + +|Mistral Datalake +|HTTPS POST to `api.mistral.ai/v1/datalake/events` +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/telemetry/send.py#L30-L31[vibe/core/telemetry/send.py:30-31] + +|GrowthBook +|HTTPS POST to the eval endpoint; fails open on error +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/experiments/client.py#L17-L81[vibe/core/experiments/client.py:17-81] + +|MCP Servers +|`http`, `streamable-http` or `stdio` +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/mcp/registry.py#L80-L150[vibe/core/tools/mcp/registry.py:80-150] + +|Vibe Code / Nuage +|HTTPS + Server-Sent Events +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/client.py#L22-L100[vibe/core/nuage/client.py:22-100], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/remote_events_source.py#L33-L120[vibe/core/nuage/remote_events_source.py:33-120] + +|GitHub +|OAuth device flow; release lookups +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/auth/github.py#L117-L184[vibe/core/auth/github.py:117-184], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/update_notifier/adapters/github_update_gateway.py[vibe/cli/update_notifier/adapters/github_update_gateway.py] + +|PyPI +|HTTPS version lookup, cached 24 h +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/update_notifier/adapters/pypi_update_gateway.py[vibe/cli/update_notifier/adapters/pypi_update_gateway.py], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/update_notifier/update.py#L82[vibe/cli/update_notifier/update.py:82] +|=== diff --git a/src/docs/arc42/chapters/04_solution_strategy.adoc b/src/docs/arc42/chapters/04_solution_strategy.adoc new file mode 100644 index 000000000..bebe19770 --- /dev/null +++ b/src/docs/arc42/chapters/04_solution_strategy.adoc @@ -0,0 +1,72 @@ +:jbake-title: Solution Strategy +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 4 +:filename: /chapters/04_solution_strategy.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-solution-strategy]] +== Solution Strategy + +The table maps each Chapter 1 quality goal to the concrete approach the +code uses to reach it. The approaches themselves are visible in code; +the *reason each was chosen over alternatives* is **deferred** — recorded +as Architecture Decisions in <> with status +"Accepted (inferred)". + +[options="header",cols="1,3"] +|=== +|Quality Goal |Approach in the code + +|Security / Safety +|Every tool call resolves to a permission tier — `ALWAYS` / `ASK` / +`NEVER` — before it runs; file tools enforce a working-directory +boundary; a folder must be trusted before its project config loads +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/utils.py#L64-L125[vibe/core/tools/utils.py:64-125], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. +The active agent profile (`plan`, `accept-edits`, `auto-approve`) bounds +how much the agent may do without asking +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L110-L199[vibe/core/agents/models.py:110-199]]. + +|Usability +|A Textual TUI renders the conversation with key bindings, slash +commands and `@`-path / `/`-command autocompletion; a first-run wizard +handles API-key setup [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L327-L348[vibe/cli/textual_ui/app.py:327-348], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/commands.py#L39[vibe/cli/commands.py:39], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/onboarding/screens/welcome.py#L46-L80[vibe/setup/onboarding/screens/welcome.py:46-80]]. + +|Maintainability +|The engine is structured as hexagonal ports (`*_port.py`) with adapters +at the edges, so LLM providers, audio, TTS and gateways are swappable; +strict pyright and ruff gate every change +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/base.py#L13-L126[vibe/core/llm/backend/base.py:13-126], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L24-L69[AGENTS.md:24-69]]. + +|Compatibility +|An LLM backend factory selects a provider-specific backend, and +API-style adapters cover OpenAI, Anthropic and Vertex shapes; an MCP +registry and the `vibe-acp` server integrate the MCP and ACP protocols +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/format.py#L58-L185[vibe/core/llm/format.py:58-185], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/mcp/registry.py#L21[vibe/core/tools/mcp/registry.py:21]]. + +|Reliability +|The Mistral backend retries with exponential backoff; a middleware +pipeline enforces turn and price limits and triggers auto-compaction +when the context nears the model threshold; user interruption cancels +in-flight tool tasks cleanly +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L212-L222[vibe/core/llm/backend/mistral.py:212-222], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L49-L96[vibe/core/middleware.py:49-96], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1244-L1259[vibe/core/agent_loop.py:1244-1259]]. +|=== + +=== Key architectural patterns + +* *Agent loop with a middleware pipeline.* `before_turn` middlewares run + ahead of every LLM turn and can stop, inject or compact + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L218-L247[vibe/core/middleware.py:218-247]] — see ADR-003. +* *Ports and adapters (hexagonal).* `*_port.py` interfaces isolate the + domain from I/O [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L13-L23[AGENTS.md:13-23]] — see ADR-001. +* *Backend factory.* `BACKEND_FACTORY` maps a provider enum to a backend + class [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7]] — see ADR-002. +* *Layered, merge-aware configuration.* Config layers (project then + user) merge field-by-field with declared merge strategies + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/builder.py#L24-L122[vibe/core/config/builder.py:24-122]]. +* *File-based session persistence.* Sessions are folders of `meta.json` + plus `messages.jsonl` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L62-L97[vibe/core/session/session_logger.py:62-97]] — + see ADR-004. diff --git a/src/docs/arc42/chapters/05_building_block_view.adoc b/src/docs/arc42/chapters/05_building_block_view.adoc new file mode 100644 index 000000000..9ecd1a47c --- /dev/null +++ b/src/docs/arc42/chapters/05_building_block_view.adoc @@ -0,0 +1,184 @@ +:jbake-title: Building Block View +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 5 +:filename: /chapters/05_building_block_view.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-building-block-view]] +== Building Block View + +=== Whitebox Overall System + +[plantuml,bbv-level1,svg] +---- +@startuml +!include + +Person(dev, "Developer / Scripter") +Person(integrator, "Editor / IDE") + +System_Boundary(vibe, "Vibe") { + Container(cli, "CLI / Textual UI", "Python, Textual", "Interactive TUI + non-interactive runner") + Container(acp, "ACP Bridge", "Python", "Agent Client Protocol server") + Container(setup, "Setup / Onboarding", "Python, Textual", "First-run wizard") + Container(loop, "Agent Loop", "Python, asyncio", "Orchestrates a conversation turn") + Container(tools, "Tool Subsystem", "Python", "Discovers, permissions, runs tools") + Container(backend, "LLM Backend Layer", "Python, httpx", "Uniform interface over LLM providers") + Container(config, "Config Subsystem", "Python, Pydantic", "Layered merge-aware configuration") + Container(sessions, "Session Persistence", "Python", "Persist, resume, rewind sessions") + Container(remote, "Remote (Teleport + Nuage)", "Python", "Hand a session to the cloud") +} + +System_Ext(llm, "LLM Provider API") +System_Ext(mcp, "MCP Servers") +System_Ext(vibecode, "Vibe Code / Nuage") + +Rel(dev, cli, "Runs vibe") +Rel(integrator, acp, "ACP over stdio") +Rel(cli, setup, "Launches on first run") +Rel(cli, loop, "Drives") +Rel(acp, loop, "Drives") +Rel(loop, tools, "Executes tool calls") +Rel(loop, backend, "Requests completions") +Rel(loop, config, "Reads") +Rel(loop, sessions, "Logs / restores") +Rel(loop, remote, "Teleports") +Rel(backend, llm, "HTTPS") +Rel(tools, mcp, "http / stdio") +Rel(remote, vibecode, "HTTPS + SSE") +@enduml +---- + +Motivation:: +The system separates the two front ends (CLI, ACP) and the first-run +wizard (Setup) from a shared `core` engine. Inside the engine the Agent +Loop is the orchestrator; the Tool Subsystem, LLM Backend Layer, Config +Subsystem and Session Persistence are the capabilities it composes. The +Remote block is an optional path that hands a session off to the cloud. + +Contained Building Blocks:: +The nine blocks below. The external systems are the same set named in +<>. + +Important Interfaces:: +`AgentLoop.act()` is the engine's entry point; `BackendLike` is the LLM +port; `BaseTool.run()` is the tool contract; `ConfigOrchestrator` builds +configuration. + +==== Agent Loop +_Responsibility:_ Orchestrate one conversation turn — run middleware, +call the LLM, parse and execute tool calls concurrently, update stats, +repeat until the assistant emits no tool call. + +_Interface:_ `AgentLoop.act()` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L632-L649[vibe/core/agent_loop.py:632-649]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py[vibe/core/agent_loop.py], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/loop.py[vibe/core/loop.py], +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py[vibe/core/middleware.py]. + +_Runtime scenarios:_ all of <>. + +==== LLM Backend Layer +_Responsibility:_ Present a uniform `BackendLike` protocol — +`complete`, `complete_streaming`, `count_tokens` — over Mistral and +OpenAI-compatible providers, with API-style adapters for OpenAI, +Anthropic and Vertex. + +_Interface:_ `BackendLike` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/base.py#L13-L126[vibe/core/llm/backend/base.py:13-126]], +selected by `BACKEND_FACTORY` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm[vibe/core/llm/]. + +_Runtime scenarios:_ Conversation Turn, LLM Rate-Limit. + +==== Tool Subsystem +_Responsibility:_ Discover, filter, instantiate and permission-check +tools — builtin, MCP and connector — and expose them to the loop. + +_Interface:_ `ToolManager.available_tools` / `.get()` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/manager.py#L72-L447[vibe/core/tools/manager.py:72-447]]; `BaseTool.run()` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L122-L152[vibe/core/tools/base.py:122-152]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools[vibe/core/tools/]. + +_Runtime scenarios:_ Conversation Turn, Tool Approval. + +==== Config Subsystem +_Responsibility:_ Discover config layers (project `.vibe/` then +`~/.vibe/`), merge them field-by-field into a validated `VibeConfig`. + +_Interface:_ `ConfigOrchestrator` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/orchestrator.py#L10-L46[vibe/core/config/orchestrator.py:10-46]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config[vibe/core/config/], link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths[vibe/core/paths/]. + +_Runtime scenarios:_ Startup & Resume. + +==== Session Persistence +_Responsibility:_ Persist a session as `meta.json` + `messages.jsonl`, +load and migrate past sessions, support `--continue`/`--resume` and +rewind-to-message. + +_Interface:_ `SessionLogger`, `SessionLoader`, `RewindManager` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L36-L97[vibe/core/session/session_logger.py:36-97], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/rewind/manager.py#L35-L165[vibe/core/rewind/manager.py:35-165]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session[vibe/core/session/], link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/rewind[vibe/core/rewind/]. + +_Runtime scenarios:_ Startup & Resume, Context Auto-Compaction. + +==== CLI / Textual UI +_Responsibility:_ Render the interactive chat TUI, route key bindings, +dispatch the 23 slash commands; also host the non-interactive runner. + +_Interface:_ `VibeApp` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L322-L558[vibe/cli/textual_ui/app.py:322-558]]; +`run_programmatic()` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27[vibe/core/programmatic.py:27]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli[vibe/cli/]. + +_Runtime scenarios:_ Conversation Turn, Tool Approval, User Interruption. + +==== ACP Bridge +_Responsibility:_ Expose the engine over the Agent Client Protocol, with +ACP-specific tools that delegate file and terminal operations to the ACP +client. + +_Interface:_ `VibeAcpAgentLoop` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_agent_loop.py#L267-L289[vibe/acp/acp_agent_loop.py:267-289]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp[vibe/acp/]. + +_Runtime scenarios:_ ACP Session. + +==== Remote (Teleport + Nuage) +_Responsibility:_ Validate the git repo, ensure the commit is pushed, +start a remote Vibe Code workflow, and stream its events back. + +_Interface:_ `TeleportService`, `WorkflowsClient`, `RemoteEventsSource` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/remote_events_source.py#L33-L120[vibe/core/nuage/remote_events_source.py:33-120]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport[vibe/core/teleport/], link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage[vibe/core/nuage/]. + +_Runtime scenarios:_ Teleport to Cloud. + +==== Setup / Onboarding +_Responsibility:_ Walk a first-run user through Welcome → auth-method → +(browser sign-in or manual API key) and persist the key. + +_Interface:_ `OnboardingScreen` flow +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/onboarding/base.py#L6-L14[vibe/setup/onboarding/base.py:6-14]]. + +_Source:_ link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup[vibe/setup/]. + +_Runtime scenarios:_ First-Run Onboarding. + +=== Level 2 — White Box: Tool Subsystem + +[plantuml,bbv-tools,svg] +---- +@startuml +!include + +Container_Boundary(tools, "Tool Subsystem") { + Component(mgr, "ToolManager", "Discovery + filtering + instantiation") + Component(base, "BaseTool + BaseToolConfig", "Tool contract: args, result, run()") + Component(perm, "PermissionStore + resolvers", "ALWAYS / ASK / NEVER decisions") + Component(builtins, "Builtin Tools", "bash, read/write, grep, task, ...") + Component(mcpreg, "MCP Registry", "Proxy tools for MCP servers") + Component(conn, "Connector Registry", "Proxy tools for connectors") +} + +System_Ext(mcp, "MCP Servers") + +Rel(mgr, base, "Instantiates") +Rel(mgr, builtins, "Discovers") +Rel(mgr, mcpreg, "Integrates") +Rel(mgr, conn, "Integrates") +Rel(base, perm, "Resolves a PermissionContext") +Rel(mcpreg, mcp, "Lists / calls tools") +@enduml +---- + +The `ToolManager` scans search paths for `BaseTool` subclasses and merges +in MCP and connector proxy tools [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/manager.py#L120-L383[vibe/core/tools/manager.py:120-383]]. +Each tool declares a `BaseToolConfig` permission tier; before execution +the loop resolves a `PermissionContext` and consults the `PermissionStore` +for session-remembered approvals +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/permissions.py#L47-L68[vibe/core/tools/permissions.py:47-68]]. +The numeric limits each builtin tool enforces are listed as system use +cases in `../../use-cases-vibe.adoc` and as quality scenarios in +<>. diff --git a/src/docs/arc42/chapters/06_runtime_view.adoc b/src/docs/arc42/chapters/06_runtime_view.adoc new file mode 100644 index 000000000..e68dd5bff --- /dev/null +++ b/src/docs/arc42/chapters/06_runtime_view.adoc @@ -0,0 +1,160 @@ +:jbake-title: Runtime View +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 6 +:filename: /chapters/06_runtime_view.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-runtime-view]] +== Runtime View + +Nine scenarios cover the system at runtime. Three are error or recovery +scenarios: Context Auto-Compaction, User Interruption and LLM Rate-Limit. +Together the scenarios exercise every building block from +<>. + +=== Startup and Session Resume + +On launch the front end resolves the working directory and trust, builds +configuration, then either starts fresh or restores a past session. + +* The Config Subsystem walks project `.vibe/` directories (BFS, up to + four levels) then the user `~/.vibe/` layer and merges them + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_local_config_walk.py#L132-L168[vibe/core/paths/_local_config_walk.py:132-168], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/builder.py#L24-L122[vibe/core/config/builder.py:24-122]]. +* `--continue` resolves the latest session for the current TTY/working + directory; `--resume [ID]` loads a specific session or opens a picker. + Both require `session_logging.enabled` + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L128-L190[vibe/cli/cli.py:128-190], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_loader.py#L64-L116[vibe/core/session/session_loader.py:64-116]]. +* Restored non-system messages are replayed into the Agent Loop and the + session id / parent id are reattached + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L180-L190[vibe/cli/cli.py:180-190]]. + +=== Conversation Turn (happy path) + +[plantuml,rt-turn,svg] +---- +@startuml +actor Developer +participant "CLI / TUI" as UI +participant "Agent Loop" as Loop +participant "Middleware" as MW +participant "LLM Backend" as BE +participant "Tool Subsystem" as Tools + +Developer -> UI : submit message +UI -> Loop : act(message) +loop until no tool call + Loop -> MW : run_before_turn() + MW --> Loop : CONTINUE + Loop -> BE : complete / complete_streaming + BE --> Loop : assistant message (+ tool calls) + alt message has tool calls + Loop -> Tools : run tools concurrently + Tools --> Loop : tool results + else no tool calls + Loop --> UI : final assistant message + end +end +@enduml +---- + +`_conversation_loop` appends the user message, runs the middleware +pipeline, performs the LLM turn, and executes any tool calls as +concurrent `asyncio` tasks before looping again +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L855-L937[vibe/core/agent_loop.py:855-937], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1217-L1259[vibe/core/agent_loop.py:1217-1259]]. + +=== Tool Approval Interception + +Before a tool runs, `_execute_tool_call` resolves its permission. An +`ASK` tier invokes the approval callback into the UI; a rejection +increments `tool_calls_rejected` and feeds a rejection result back to the +LLM so the conversation continues +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1071-L1167[vibe/core/agent_loop.py:1071-1167], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/permissions.py#L47-L68[vibe/core/tools/permissions.py:47-68]]. +Approved patterns are remembered in the `PermissionStore` for the rest of +the session. + +=== Context Auto-Compaction (recovery scenario) + +[plantuml,rt-compact,svg] +---- +@startuml +participant "Agent Loop" as Loop +participant "AutoCompactMiddleware" as MW +participant "LLM Backend" as BE +participant "Session Persistence" as SP + +Loop -> MW : run_before_turn() +MW -> MW : context_tokens >= auto_compact_threshold ? +MW --> Loop : COMPACT +Loop -> BE : summarise history (compaction model) +BE --> Loop : summary +Loop -> Loop : reset messages to [system, summary] +Loop -> SP : new session id, parent = old session +Loop -> BE : count_tokens(new context) +@enduml +---- + +When `context_tokens` crosses the model's `auto_compact_threshold`, +`AutoCompactMiddleware` returns `COMPACT`; the loop summarises the +history into `[system, summary]`, forks to a new session id linked to its +parent, and recounts tokens +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L81-L96[vibe/core/middleware.py:81-96], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1681-L1749[vibe/core/agent_loop.py:1681-1749]]. + +=== User Interruption (error / recovery scenario) + +[plantuml,rt-interrupt,svg] +---- +@startuml +actor Developer +participant "CLI / TUI" as UI +participant "Agent Loop" as Loop +participant "Tool Subsystem" as Tools + +Developer -> UI : press Escape +UI -> Loop : user-cancellation event +Loop -> Tools : cancel in-flight tasks +Tools --> Loop : CancelledError per task +Loop -> Loop : record cancellation as tool result +Loop --> UI : turn ends cleanly +@enduml +---- + +Escape raises a user-cancellation event; in-flight tool tasks receive +`asyncio.CancelledError`, the cancellation is recorded as a tool result, +and the loop breaks without corrupting the message history +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L897-L906[vibe/core/agent_loop.py:897-906], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1170-L1178[vibe/core/agent_loop.py:1170-1178], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1244-L1259[vibe/core/agent_loop.py:1244-1259]]. + +=== LLM Rate-Limit / Context-Too-Long (error scenario) + +`_chat` catches rate-limit and context-too-long errors raised by the LLM +Backend Layer and surfaces them distinctly; the Mistral backend retries +transient failures with exponential backoff before the error propagates; +the ACP Bridge maps a context-too-long failure to a JSON-RPC +`ContextTooLongError` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1365-L1374[vibe/core/agent_loop.py:1365-1374], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L212-L222[vibe/core/llm/backend/mistral.py:212-222], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/exceptions.py#L107[vibe/acp/exceptions.py:107]]. + +=== Teleport to Cloud + +`/teleport` (gated by plan eligibility) validates the git repository, +confirms the working commit is pushed, then starts a remote Vibe Code +workflow; `RemoteEventsSource` attaches and streams the cloud workflow's +events back into the UI +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/git.py#L27-L100[vibe/core/teleport/git.py:27-100], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/remote_events_source.py#L33-L120[vibe/core/nuage/remote_events_source.py:33-120]]. + +=== ACP Session + +`vibe-acp` runs an ACP agent over stdio. `new_session` creates a fresh +`AcpSessionLoop` with the default agent; `load_session` replays a past +conversation. ACP-specific tools delegate file and terminal operations to +the connected ACP client +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_agent_loop.py#L614-L648[vibe/acp/acp_agent_loop.py:614-648], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_agent_loop.py#L869-L920[vibe/acp/acp_agent_loop.py:869-920], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/tools/base.py#L13-L54[vibe/acp/tools/base.py:13-54]]. + +=== First-Run Onboarding + +When no API key is configured, the Setup block walks the user through +Welcome → auth-method → browser sign-in or manual API key, then persists +the key to `~/.vibe/.env` (or keyring) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/onboarding/screens/welcome.py#L46-L80[vibe/setup/onboarding/screens/welcome.py:46-80], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/auth/api_key_persistence.py#L38-L64[vibe/setup/auth/api_key_persistence.py:38-64]]. diff --git a/src/docs/arc42/chapters/07_deployment_view.adoc b/src/docs/arc42/chapters/07_deployment_view.adoc new file mode 100644 index 000000000..696d341f2 --- /dev/null +++ b/src/docs/arc42/chapters/07_deployment_view.adoc @@ -0,0 +1,88 @@ +:jbake-title: Deployment View +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 7 +:filename: /chapters/07_deployment_view.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-deployment-view]] +== Deployment View + +=== Infrastructure Level 1 + +[plantuml,deploy-level1,svg] +---- +@startuml +node "Developer Machine" { + artifact "vibe / vibe-acp" as app + folder "~/.vibe" as home { + artifact "config.toml" + artifact ".env" + artifact "logs/session/" + artifact "trusted_folders.toml" + } + database "Project working dir" as proj +} +cloud "Mistral Cloud" { + artifact "LLM API" + artifact "Datalake telemetry" +} +cloud "Vibe Code / Nuage" as vibecode +cloud "GrowthBook" as gb +cloud "PyPI / GitHub" as dist + +app --> home : reads config, writes logs +app --> proj : reads / edits files +app --> "LLM API" : HTTPS +app --> "Datalake telemetry" : HTTPS +app --> vibecode : HTTPS + SSE +app --> gb : HTTPS +app --> dist : update check +@enduml +---- + +Motivation:: +Vibe is a *client-side* tool. The only "deployment" is the package +installed on the developer's machine; there is no server component owned +by this bounded context. All server-side systems are external +(<>). + +Quality and/or Performance Features:: +The process is single-user and single-process. State lives under +`~/.vibe/` (overridable with `VIBE_HOME`): `config.toml`, `.env`, session +logs, the trust list, plans and the update cache +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_vibe_home.py#L19-L36[vibe/core/paths/_vibe_home.py:19-36]]. + +Mapping of Building Blocks to Infrastructure:: +All nine building blocks run inside the single link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] (or `vibe-acp`) +process on the developer machine. The Remote block additionally talks to +the Vibe Code / Nuage cloud. + +=== Infrastructure Level 2 — Distribution channels + +[options="header",cols="1,2"] +|=== +|Channel |Evidence + +|PyPI package `mistral-vibe`, installed via `uv tool install` or `pip` +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L45-L55[README.md:45-55], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L143-L144[pyproject.toml:143-144] + +|One-line link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/scripts/install.sh[scripts/install.sh] (Linux / macOS) +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L27-L33[README.md:27-33] + +|Standalone binary built with PyInstaller +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe-acp.spec[vibe-acp.spec], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyinstaller/runtime_hook_truststore.py[pyinstaller/runtime_hook_truststore.py], +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.github/workflows/build-and-upload.yml[.github/workflows/build-and-upload.yml] + +|Packaged GitHub Action (`uv sync` + uv run link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] -p) +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/action.yml#L1-L60[action.yml:1-60] + +|Zed editor extension +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/distribution/zed/extension.toml[distribution/zed/extension.toml] +|=== + +Releases are cut by the link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.github/workflows/release.yml[.github/workflows/release.yml] workflow; CI runs in link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.github/workflows/ci.yml[.github/workflows/ci.yml] +[link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.github/workflows[.github/workflows/]]. diff --git a/src/docs/arc42/chapters/08_concepts.adoc b/src/docs/arc42/chapters/08_concepts.adoc new file mode 100644 index 000000000..e3184bb92 --- /dev/null +++ b/src/docs/arc42/chapters/08_concepts.adoc @@ -0,0 +1,172 @@ +:jbake-title: Cross-cutting Concepts +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 8 +:filename: /chapters/08_concepts.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-concepts]] +== Cross-cutting Concepts + +=== Threat Model (STRIDE) + +[WARNING] +==== +No STRIDE threat model exists in the code base. The threats below are +**candidate threats**, derived in Phase 2 from the code-visible risk +surfaces (see <>). The *completeness* of this +catalogue and the validation of each threat are **deferred** to the +Architect and Operations — see ADR review and `OPEN_QUESTIONS.adoc`. +==== + +[options="header",cols="1,1,3,1"] +|=== +|T-ID |STRIDE |Candidate Threat |Risk + +|T-001 |Tampering, Elevation +|The LLM emits a destructive shell command and `bash` runs it +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/bash.py#L480-L530[vibe/core/tools/builtins/bash.py:480-530]]. +|R-001 + +|T-002 |Information Disclosure +|A file tool reads a secret file such as `.env` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/read_file.py#L60-L66[vibe/core/tools/builtins/read_file.py:60-66]]. +|R-002 + +|T-003 |Tampering +|A file write or patch lands outside the working directory +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/utils.py#L64-L125[vibe/core/tools/utils.py:64-125]]. +|R-003 + +|T-004 |Information Disclosure +|`webfetch` is pointed at an internal/loopback URL (SSRF) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/webfetch.py#L62-L119[vibe/core/tools/builtins/webfetch.py:62-119]]. +|R-002 + +|T-005 |Elevation of Privilege +|An untrusted project's `.vibe/` config (tools, hooks, agents) executes +on first entry [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L19-L32[vibe/core/trusted_folders.py:19-32]]. +|R-004 + +|T-006 |Spoofing, Information Disclosure +|The Mistral API key leaks from disk +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/auth/api_key_persistence.py#L14-L16[vibe/setup/auth/api_key_persistence.py:14-16]]. +|R-005 + +|T-007 |Tampering +|A malicious hook command runs as a shell subprocess +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/hooks/executor.py#L11-L55[vibe/core/hooks/executor.py:11-55]]. +|R-006 +|=== + +=== Security + +Every mitigation references the T-IDs it closes. + +[options="header",cols="2,3,1"] +|=== +|Mechanism |Description |Closes + +|Tiered tool permissions +|Each tool resolves to `ALWAYS` / `ASK` / `NEVER`; `ASK` prompts the +user before executing +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113]]. +|T-001, T-004, T-007 + +|Command allow/deny lists + arity +|`bash` matches command prefixes against allow/deny lists; `sudo` always +asks [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/bash.py#L228-L243[vibe/core/tools/builtins/bash.py:228-243], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/arity.py#L145-L158[vibe/core/tools/arity.py:145-158]]. +|T-001 + +|Sensitive-pattern gating +|File and search tools treat `**/.env*` as sensitive and force an +approval prompt [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/read_file.py#L60-L63[vibe/core/tools/builtins/read_file.py:60-63]]. +|T-002 + +|Working-directory boundary +|File tools require paths inside the working directory (or an +`--add-dir` root); otherwise an extra approval is required +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/utils.py#L64-L125[vibe/core/tools/utils.py:64-125]]. +|T-003 + +|Trust-folder gate +|A folder containing `.vibe/`/link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md[AGENTS.md] triggers a trust prompt; +untrusted folders do not load project config +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. +|T-005 + +|Secret storage +|API keys are stored in `~/.vibe/.env` or the OS keyring; encrypted +payloads use RSA-OAEP-SHA256 + AES-256-GCM; browser sign-in uses PKCE +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/auth/crypto.py#L13-L137[vibe/core/auth/crypto.py:13-137], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/auth/browser_sign_in.py#L173-L184[vibe/setup/auth/browser_sign_in.py:173-184]]. +|T-006 + +|Read-only agent profiles +|The `plan` and `chat` profiles are read-only, enforced by +`ReadOnlyAgentMiddleware` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L174-L216[vibe/core/middleware.py:174-216]] — see +ADR-003. +|T-001, T-003 +|=== + +=== Test + +The test concept is a pytest suite (`pytest` + `pytest-asyncio` + +`respx` + `pytest-textual-snapshot`) with test doubles named `Fake*` in +link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/tests/stubs[tests/stubs/] [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L84-L92[AGENTS.md:84-92]]. CI runs the suite plus strict pyright +and ruff [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/.github/workflows/ci.yml[.github/workflows/ci.yml]]. + +[WARNING] +==== +Tests are not traced to use cases or business rules, and the pyramid +shape (unit / integration / end-to-end proportions) is not declared. +Establishing per-use-case traceability — each test naming the +`../use-cases-vibe.adoc` use case it covers — is **deferred** to the +Developer. +==== + +=== Observability + +* *Tracing.* OpenTelemetry spans wrap the agent invocation and each tool + execution, with the conversation id propagated as OTEL baggage; the + OTLP exporter is enabled only when telemetry and OTEL config are + present [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tracing.py#L23-L137[vibe/core/tracing.py:23-137]]. +* *Logging.* Structured stdlib logging writes to `~/.vibe/logs/vibe.log`; + level and rotation size are env-configurable [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L71-L79[AGENTS.md:71-79]]. +* *Usage telemetry.* Usage events go to the Mistral datalake, gated by + `enable_telemetry` and the presence of an API key + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/telemetry/send.py#L58-L95[vibe/core/telemetry/send.py:58-95]]. +* *ACP message log.* When `VIBE_ACP_LOGGING_ENABLED` is set, every ACP + protocol message is recorded [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_logger.py#L17-L59[vibe/acp/acp_logger.py:17-59]]. + +=== Error Handling + +* *Retry and backoff.* The Mistral backend retries transient failures — + 500 ms initial delay, 1.5x exponent, 300 s cap + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L212-L222[vibe/core/llm/backend/mistral.py:212-222]]. +* *Distinct LLM failures.* Rate-limit and context-too-long errors are + caught and surfaced distinctly rather than as generic failures + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1365-L1374[vibe/core/agent_loop.py:1365-1374]]. +* *Hook retry limit.* Hooks retry at most three times before being + reported as failed [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/hooks/manager.py#L28-L53[vibe/core/hooks/manager.py:28-53]]. +* *Fail-open experiments.* The GrowthBook client returns defaults on any + error so a flag-service outage never blocks a session + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/experiments/client.py#L43-L76[vibe/core/experiments/client.py:43-76]]. +* *Cancellation.* `asyncio.CancelledError` is handled explicitly so user + interruption ends a turn cleanly [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1170-L1178[vibe/core/agent_loop.py:1170-1178]]. +* *Exception discipline.* Module-local exception hierarchies are chained + with `raise … from e` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L71-L79[AGENTS.md:71-79]]. + +=== Configuration + +Configuration is layered and merge-aware: project `.vibe/config.toml` +directories are walked breadth-first (up to four levels) and then the +user `~/.vibe/config.toml` layer is applied; layers merge field-by-field +with declared strategies (replace, concat, union, shallow, conflict) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/builder.py#L24-L122[vibe/core/config/builder.py:24-122], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_local_config_walk.py#L132-L168[vibe/core/paths/_local_config_walk.py:132-168]]. +`VIBE_HOME` relocates the entire user state directory +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_vibe_home.py#L19-L25[vibe/core/paths/_vibe_home.py:19-25]]. This concept is listed because +the system genuinely has a configuration concern that crosses every +building block. diff --git a/src/docs/arc42/chapters/09_architecture_decisions.adoc b/src/docs/arc42/chapters/09_architecture_decisions.adoc new file mode 100644 index 000000000..2358140f0 --- /dev/null +++ b/src/docs/arc42/chapters/09_architecture_decisions.adoc @@ -0,0 +1,69 @@ +:jbake-title: Architecture Decisions +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 9 +:filename: /chapters/09_architecture_decisions.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-design-decisions]] +== Architecture Decisions + +The decisions below are recovered from code. The *rationale* — why each +was chosen over alternatives — could not be recovered, so each ADR has +status *Accepted (inferred)* and its Pugh Matrix marks team-judgment +cells with `?`. The full ADRs are in the register at `../../adrs/`. + +=== ADR Index + +[options="header",cols="1,2,1"] +|=== +|ADR |Title |Status + +|<<../../adrs/adr-001-hexagonal-ports.adoc#,ADR-001>> +|Hexagonal Ports and Adapters +|Accepted (inferred) + +|<<../../adrs/adr-002-llm-backend-abstraction.adoc#,ADR-002>> +|Pluggable LLM Backend Abstraction +|Accepted (inferred) + +|<<../../adrs/adr-003-middleware-pipeline.adoc#,ADR-003>> +|Conversation Middleware Pipeline +|Accepted (inferred) + +|<<../../adrs/adr-004-file-based-sessions.adoc#,ADR-004>> +|File-based Session Persistence +|Accepted (inferred) +|=== + +=== Summary + +ADR-001 — *Hexagonal Ports and Adapters.* The engine isolates the domain +behind `*_port.py` interfaces with adapters at the edges +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L13-L23[AGENTS.md:13-23]]. Concretised in <> (Configuration, +Observability). + +ADR-002 — *Pluggable LLM Backend Abstraction.* `BACKEND_FACTORY` maps a +provider enum to a backend, and API-style adapters cover OpenAI, +Anthropic and Vertex shapes +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/format.py#L58-L185[vibe/core/llm/format.py:58-185]]. +Concretised by the Compatibility goal in <>. + +ADR-003 — *Conversation Middleware Pipeline.* A `before_turn` pipeline +enforces turn/price limits, auto-compaction and read-only agent modes +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L218-L247[vibe/core/middleware.py:218-247]]. Concretised by the Security and +Reliability mechanisms in <>. + +ADR-004 — *File-based Session Persistence.* Sessions persist as a folder +of `meta.json` + `messages.jsonl`, with a v1→v2 migration in code +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L62-L97[vibe/core/session/session_logger.py:62-97], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_migration.py#L16-L41[vibe/core/session/session_migration.py:16-41]]. + +[NOTE] +==== +The "why" behind all four decisions is **deferred** to the Architect +(see `OPEN_QUESTIONS.adoc`). Until answered, the inferred status and the +`?` cells in each Pugh Matrix must not be read as confirmed rationale. +==== diff --git a/src/docs/arc42/chapters/10_quality_requirements.adoc b/src/docs/arc42/chapters/10_quality_requirements.adoc new file mode 100644 index 000000000..ef0345b19 --- /dev/null +++ b/src/docs/arc42/chapters/10_quality_requirements.adoc @@ -0,0 +1,146 @@ +:jbake-title: Quality Requirements +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 10 +:filename: /chapters/10_quality_requirements.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-quality-scenarios]] +== Quality Requirements + +=== Quality Requirements Overview + +The quality tree covers all eight ISO/IEC 25010 characteristics. Each is +marked either as *concretising* a Chapter 1.2 top goal or as *derived* +(a quality requirement visible in code but not among the top goals). + +[options="header",cols="1,1,2"] +|=== +|Characteristic |Relation to Chapter 1.2 |Note + +|Security +|Concretises *Security / Safety* +|Permission tiers, trust gate, secret storage. + +|Usability +|Concretises *Usability* +|TUI, onboarding, autocompletion. + +|Maintainability +|Concretises *Maintainability* +|Hexagonal ports, strict static analysis. + +|Compatibility +|Concretises *Compatibility* +|Multi-provider, MCP, ACP. + +|Reliability +|Concretises *Reliability* +|Retry, limits, recovery. + +|Functional Suitability +|Derived +|Tool suite + agent loop deliver the stated function. + +|Performance Efficiency +|Derived +|Concurrent tools, streaming, bounded I/O. + +|Portability +|Derived +|Cross-platform; UNIX is the supported target. +|=== + +[WARNING] +==== +The *priority ranking* of the quality goals is **deferred** to the +Product Owner and Architect. The scenarios below are testable on their +own, but which to favour when they conflict is an unresolved decision. +==== + +=== Quality Scenarios + +Each scenario is in the six-part form. Every response measure carries a +literal figure recovered from code, so the requirement is testable. + +[options="header",cols="1,1,1,1,1,1"] +|=== +|Source |Stimulus |Artifact |Environment |Response |Response Measure + +|LLM +|Emits a shell command +|`bash` tool +|Interactive session, `default` agent +|Tool prompts for approval, then runs the command with a timeout +|Default timeout *300 s* (overridable); stdout+stderr captured to a +*16 000-byte* cap [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/bash.py#L222-L226[vibe/core/tools/builtins/bash.py:222-226]] — *Security*. + +|LLM +|Requests a code search +|`grep` tool +|Large repository +|Search returns within the timeout, capped +|Timeout *60 s*; at most *100 matches* and *64 000 output bytes* +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/grep.py#L43-L51[vibe/core/tools/builtins/grep.py:43-51]] — *Performance Efficiency*. + +|LLM +|Reads a large file +|`read_file` tool +|Any session +|File content is returned truncated, with `was_truncated` set +|Read cap *64 000 bytes* per call +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/read_file.py#L65-L66[vibe/core/tools/builtins/read_file.py:65-66]] — *Performance Efficiency*. + +|Conversation +|Context tokens approach the model limit +|Agent Loop + compaction +|Long interactive session +|History is summarised and the session forks +|Triggered at the model's `auto_compact_threshold`; a warning is injected +at *50 %* of that threshold +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L81-L126[vibe/core/middleware.py:81-126]] — *Reliability*. + +|LLM Provider +|Returns a transient error +|Mistral backend +|Network instability +|The request is retried with exponential backoff +|*500 ms* initial delay, *1.5x* exponent, *300 s* cap +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/mistral.py#L212-L222[vibe/core/llm/backend/mistral.py:212-222]] — *Reliability*. + +|MCP Server +|Is slow to start or respond +|MCP registry +|External tool provider configured +|Startup and per-call are bounded by timeouts +|Startup *10 s*, tool call *60 s* (defaults) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/_settings.py#L231-L237[vibe/core/config/_settings.py:231-237]] — *Compatibility*. + +|Developer +|Presses Escape mid-turn +|Agent Loop +|Tool tasks running +|In-flight tool tasks are cancelled and the turn ends cleanly +|All running tasks receive `CancelledError`; no partial message is +persisted [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1244-L1259[vibe/core/agent_loop.py:1244-1259]] — *Reliability*. + +|Hook author +|Configures a slow hook +|Hooks manager +|`POST_AGENT_TURN` event +|The hook is killed on timeout and retried a bounded number of times +|Default timeout *30 s*; at most *3* retries +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/hooks/models.py#L30[vibe/core/hooks/models.py:30], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/hooks/manager.py#L28[vibe/core/hooks/manager.py:28]] — +*Reliability*. + +|Maintainer +|Submits a code change +|CI pipeline +|Pull request +|Pyright (strict) and ruff must pass before merge +|Zero type errors, zero lint errors [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L24-L46[AGENTS.md:24-46]] — +*Maintainability*. +|=== diff --git a/src/docs/arc42/chapters/11_technical_risks.adoc b/src/docs/arc42/chapters/11_technical_risks.adoc new file mode 100644 index 000000000..4a6bfb044 --- /dev/null +++ b/src/docs/arc42/chapters/11_technical_risks.adoc @@ -0,0 +1,132 @@ +:jbake-title: Risks and Technical Debts +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 11 +:filename: /chapters/11_technical_risks.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-technical-risks]] +== Risks and Technical Debts + +=== Risks + +Risks are ordered by priority (highest first). Priority is derived from +probability × impact. The probability and impact values are *inferred* +in Phase 2 and must be confirmed by a security review — see ADR review +and `OPEN_QUESTIONS.adoc` (Q3.8.1). + +[options="header",cols="0.6,2.4,0.8,0.8,0.8,2.6"] +|=== +|ID |Risk |Prob. |Impact |Priority |Mitigation + +|R-007 +|No STRIDE threat model backs the security mechanisms, so threat +*completeness* is unverified (no code evidence — deferred). +|High |Medium |*High* +|Author a STRIDE threat model (deferred — Architect, Operations). The +candidate threats T-001..T-007 in <> are the starting +point. + +|R-001 +|The LLM emits a destructive shell command and `bash` executes it +(threat T-001). +|Medium |High |*High* +|Tiered tool permissions + command allow/deny lists +(<> Security); `bash` timeout scenario in +<>. + +|R-004 +|An untrusted project's `.vibe/` config (tools, hooks, agents) executes +on first entry (threat T-005). +|Medium |High |*High* +|Trust-folder gate (<> Security). + +|R-002 +|A secret file is read, or `webfetch` is used for SSRF (threats T-002, +T-004). +|Medium |Medium |*Medium* +|Sensitive-pattern gating and `ASK` permission on `webfetch` +(<> Security). + +|R-003 +|A file write or patch lands outside the working directory (threat +T-003). +|Low |High |*Medium* +|Working-directory boundary (<> Security). + +|R-005 +|The Mistral API key leaks from disk (threat T-006). +|Low |High |*Medium* +|Keyring storage and RSA+AES payload encryption (<> +Security). + +|R-006 +|A malicious hook command runs as a shell subprocess (threat T-007). +|Low |High |*Medium* +|Hooks are gated by `enable_experimental_hooks`; bounded 30 s timeout +and 3 retries (<>). + +|R-008 +|A large pinned dependency surface (~80 direct dependencies) carries +supply-chain exposure; no SBOM or scanning config is in the bounded +context [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L30-L141[pyproject.toml:30-141]]. +|Medium |Medium |*Medium* +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/uv.lock[uv.lock] pins every transitive dependency; `exclude-newer` constrains +new releases. Adding an SBOM / scan is unmitigated — deferred. + +|R-009 +|Behaviour on a sustained LLM-provider outage (beyond retry exhaustion) +is not specified in code (deferred). +|Medium |Low |*Low* +|Retry/backoff scenario in <>; the intended +end-state is deferred to Operations. + +|R-010 +|No performance budget exists in code for very large repositories +(deferred). +|Low |Low |*Low* +|Byte-capped file reads and auto-compaction bound growth; an explicit +budget is deferred. +|=== + +=== Technical Debt + +Each item names the Chapter 5 building block it burdens. + +[options="header",cols="0.6,1.4,2.4,1.4"] +|=== +|ID |Burdened Building Block |Debt |Evidence + +|TD-001 +|Agent Loop +|`session_cost` is a self-described rough estimate that ignores prompt +caching, so the `--max-price` ceiling is approximate. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/types.py#L102-L115[vibe/core/types.py:102-115] + +|TD-002 +|ACP Bridge +|`acp/tools/` re-implements several builtins (`bash`, `read_file`, +`write_file`, `search_replace`, `grep`, …) that already exist in +`core/tools/`, duplicating maintenance. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_agent_loop.py#L650-L666[vibe/acp/acp_agent_loop.py:650-666] + +|TD-003 +|Session Persistence +|A v1→v2 session-format migration is carried in code; it can be removed +once all users have migrated. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_migration.py#L16-L41[vibe/core/session/session_migration.py:16-41] + +|TD-004 +|CLI / Textual UI +|The TUI widget layer is large (~70 widget modules) and untiered, which +makes the front end the least diffable part of a recovery run. +|link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/widgets[vibe/cli/textual_ui/widgets/] + +|TD-005 +|CLI / Textual UI +|Voice mode is shipped but marked experimental and "may change". +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L284-L285[README.md:284-285] +|=== diff --git a/src/docs/arc42/chapters/12_glossary.adoc b/src/docs/arc42/chapters/12_glossary.adoc new file mode 100644 index 000000000..91235b9e5 --- /dev/null +++ b/src/docs/arc42/chapters/12_glossary.adoc @@ -0,0 +1,91 @@ +:jbake-title: Glossary +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: arc42 +:jbake-order: 12 +:filename: /chapters/12_glossary.adoc +ifndef::imagesdir[:imagesdir: ../../images] + +:toc: + +[[section-glossary]] +== Glossary + +[cols="e,3e" options="header"] +|=== +|Term |Definition + +|Agent +|A configured run profile of the engine; built-in profiles are +`default`, `plan`, `chat`, `accept-edits`, `auto-approve`, `explore`, +`lean` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L38-L45[vibe/core/agents/models.py:38-45]]. + +|Subagent +|An agent of type `SUBAGENT` (e.g. `explore`) spawned by the `task` tool +to do isolated work [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L152[vibe/core/agents/models.py:152]]. + +|Agent Profile +|The data record for an agent: name, safety tier, agent type and config +overrides [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L48-L94[vibe/core/agents/models.py:48-94]]. + +|Agent Loop +|The orchestrator that runs a conversation turn — LLM call plus tool +execution — until no tool call remains [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L855-L937[vibe/core/agent_loop.py:855-937]]. + +|Tool +|A `BaseTool` subclass with a Pydantic args model, a result model and an +async `run()` generator [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L122-L152[vibe/core/tools/base.py:122-152]]. + +|Tool Permission +|The tier — `ALWAYS`, `ASK` or `NEVER` — that decides whether a tool +runs without asking [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L96[vibe/core/tools/base.py:83-96]]. + +|Skill +|A reusable component declared by a `SKILL.md` file; can add slash +commands and behaviours [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/skills/manager.py#L91-L117[vibe/core/skills/manager.py:91-117]]. + +|MCP Server +|An external tool provider reached over the Model Context Protocol +(`http`, `streamable-http` or `stdio`) +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/mcp/registry.py#L80-L150[vibe/core/tools/mcp/registry.py:80-150]]. + +|Connector +|A remote tool source registered through the connector registry +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/connectors/connector_registry.py[vibe/core/tools/connectors/connector_registry.py]]. + +|Hook +|A TOML-declared shell command run on a lifecycle event such as +`POST_AGENT_TURN` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/hooks/models.py#L19-L38[vibe/core/hooks/models.py:19-38]]. + +|Session +|A persisted conversation: a folder of `meta.json` + `messages.jsonl` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_loader.py#L16-L17[vibe/core/session/session_loader.py:16-17]]. + +|Compaction +|Summarising the conversation history when the context nears the model's +token threshold [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1681-L1749[vibe/core/agent_loop.py:1681-1749]]. + +|Plan Mode +|The read-only `plan` agent that drafts a markdown plan before execution +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/plan_session.py#L11-L42[vibe/core/plan_session.py:11-42]]. + +|Teleport +|Handing a local session to a remote Vibe Code workflow +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150]]. + +|Nuage +|The remote cloud workflow backend (French for "cloud") +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/client.py#L22-L100[vibe/core/nuage/client.py:22-100]]. + +|Trust Folder +|A directory the user has explicitly trusted; only trusted folders load +project `.vibe/` config [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. + +|ACP +|Agent Client Protocol — the JSON-RPC-style protocol the `vibe-acp` +server speaks to editors [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108]]. + +|VIBE_HOME +|The user-state directory, `~/.vibe` by default, relocatable via the +`VIBE_HOME` env var [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/paths/_vibe_home.py#L19-L25[vibe/core/paths/_vibe_home.py:19-25]]. +|=== diff --git a/src/docs/arc42/chapters/config.adoc b/src/docs/arc42/chapters/config.adoc new file mode 100644 index 000000000..4c225feda --- /dev/null +++ b/src/docs/arc42/chapters/config.adoc @@ -0,0 +1,10 @@ +// asciidoc settings for EN (English) +// ================================== +:toc-title: table of contents + +// enable table-of-contents +:toc: + +// where are images located? +:imagesdir: ../images +:arc42help: diff --git a/src/docs/images/arc42-logo.png b/src/docs/images/arc42-logo.png new file mode 100644 index 000000000..88c76d063 Binary files /dev/null and b/src/docs/images/arc42-logo.png differ diff --git a/src/docs/spec/prd-vibe.adoc b/src/docs/spec/prd-vibe.adoc new file mode 100644 index 000000000..4c2d68297 --- /dev/null +++ b/src/docs/spec/prd-vibe.adoc @@ -0,0 +1,130 @@ += Vibe — Product Requirements Document +:jbake-title: Product Requirements Document +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: spec +:jbake-order: 1 +:doctype: book +:toc: left +:toclevels: 3 +ifndef::imagesdir[:imagesdir: images] + +// Generated by Socratic Code-Theory Recovery (Phase 2) from +// QUESTION_TREE.adoc. Code-derived claims cite file:line evidence; +// gaps the code cannot answer are marked "deferred" and must not be +// read as confirmed product intent. + +== Problem Statement + +Working with a codebase from the terminal means switching constantly +between reading files, searching, editing and running commands. Vibe +addresses this by putting a conversational AI agent in the terminal: the +developer states a goal in natural language and the agent explores, +edits and runs the project through a tool suite +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L20-L22[README.md:20-22], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L92-L109[README.md:92-109]]. + +[WARNING] +==== +*Why Mistral built Vibe — and how it relates to the paid Mistral Code / +Vibe Code offering — is deferred.* The code shows the capability, not +the business rationale. The Product Owner must supply this before the +PRD can claim a strategic problem statement. +==== + +== Target Users + +[options="header",cols="1,2,2"] +|=== +|User |Need |Evidence + +|Interactive developer +|Explore and edit a codebase conversationally, with explicit control +over what the agent does. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L322[vibe/cli/textual_ui/app.py:322] + +|Scripter / CI automation +|Run the agent non-interactively with deterministic limits and +machine-readable output. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27[vibe/core/programmatic.py:27] + +|Editor / IDE integrator +|Embed the agent in an editor through a stable protocol. +|link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108] +|=== + +*Segment priority is deferred.* The three users are served by peer +entry points; which segment the roadmap favours is a product decision. + +== Goals + +. Let a developer complete coding tasks conversationally without leaving + the terminal [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L92-L109[README.md:92-109]]. +. Keep the developer in control: no tool runs without a permission + decision [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113]]. +. Run unattended for scripting, bounded by turn and cost ceilings + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27-L91[vibe/core/programmatic.py:27-91]]. +. Be extensible: MCP servers, skills, custom agents and hooks + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L340-L579[README.md:340-579]]. +. Integrate with editors over the Agent Client Protocol + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108]]. + +== Success Criteria + +The code emits usage telemetry and per-session statistics — turns, tool +outcomes, token counts, estimated cost +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/telemetry/send.py#L58-L95[vibe/core/telemetry/send.py:58-95], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/types.py#L47-L138[vibe/core/types.py:47-138]]. + +[WARNING] +==== +*The business KPI that defines success is deferred.* Telemetry is the +instrument, not the target. Whether success means adoption, retention, +task-completion rate or conversion to a paid plan must come from the +Product Owner. +==== + +== Scope + +=== In scope (recovered from code) + +* Interactive and programmatic single-user sessions + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L197[vibe/cli/cli.py:197]]. +* A built-in tool suite for shell, files, search and web + [link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins[vibe/core/tools/builtins/]]. +* Session persistence, resume and rewind + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L36-L97[vibe/core/session/session_logger.py:36-97]]. +* MCP, skills, custom agents, hooks, voice mode (experimental) and + teleport to the cloud [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L282-L579[README.md:282-579], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150]]. +* The ACP server entry point [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L78-L108[vibe/acp/entrypoint.py:78-108]]. + +=== Out of scope + +* Any server-side component — Vibe runs entirely on the developer's + machine; all back ends are external (see + `arc42/chapters/03_context_and_scope.adoc`). +* Multi-user or shared-session operation — the process is single-user. + +== Constraints + +Python >= 3.12, managed with `uv`; strict pyright and ruff gate CI; +UNIX is the officially supported target; Apache-2.0 licence +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/pyproject.toml#L6[pyproject.toml:6], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/AGENTS.md#L7-L46[AGENTS.md:7-46], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L24-L26[README.md:24-26]]. The full list is +in `arc42/chapters/02_architecture_constraints.adoc`. + +== Open Questions (deferred) + +[options="header",cols="1,3"] +|=== +|Owner |Question + +|Product Owner |Why was Vibe built, and how does it relate to Mistral +Code / Vibe Code? +|Product Owner |What business KPI defines success? +|Product Owner |Which user segment is the priority? +|Product Owner, Architect |What is the priority ranking of the quality +goals? +|Product Owner, Domain Expert |Is the recovered use-case catalog +complete, and which use cases are core vs. experimental? +|=== + +These are tracked in `OPEN_QUESTIONS.adoc` at the repository root and +must be answered before the next planning cycle. diff --git a/src/docs/spec/use-cases-vibe.adoc b/src/docs/spec/use-cases-vibe.adoc new file mode 100644 index 000000000..08d91817c --- /dev/null +++ b/src/docs/spec/use-cases-vibe.adoc @@ -0,0 +1,533 @@ += Vibe — Specification +:jbake-title: Specification +:jbake-type: page_toc +:jbake-status: published +:jbake-menu: spec +:jbake-order: 2 +:doctype: book +:toc: left +:toclevels: 3 +ifndef::imagesdir[:imagesdir: images] + +// Generated by Socratic Code-Theory Recovery (Phase 2) from +// QUESTION_TREE.adoc. Code-derived claims cite file:line evidence. +// Gaps the code cannot answer are marked "deferred". + +== Actors + +[options="header",cols="1,1,2"] +|=== +|Actor |Kind |Evidence + +|Developer |Human, primary |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/textual_ui/app.py#L322[vibe/cli/textual_ui/app.py:322] +|Scripter / CI |Human, primary |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/programmatic.py#L27[vibe/core/programmatic.py:27] +|ACP Client (editor/IDE) |System, primary |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/acp_agent_loop.py#L267[vibe/acp/acp_agent_loop.py:267] +|LLM Provider |System, supporting |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/llm/backend/factory.py#L7[vibe/core/llm/backend/factory.py:7] +|Subagent (`explore`) |System, internal |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L152[vibe/core/agents/models.py:152] +|MCP Server |System, supporting |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/mcp/registry.py#L21[vibe/core/tools/mcp/registry.py:21] +|Vibe Code / Nuage |System, supporting |link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/nuage/client.py#L22[vibe/core/nuage/client.py:22] +|=== + +[plantuml,uc-overview,svg] +---- +@startuml +left to right direction +actor Developer +actor "Scripter / CI" as Scripter +actor "ACP Client" as ACP +actor "LLM Provider" as LLM + +rectangle Vibe { + usecase "UC-1 Run interactive session" as UC1 + usecase "UC-2 Run programmatically" as UC2 + usecase "UC-3 Resume a session" as UC3 + usecase "UC-4 Plan before acting" as UC4 + usecase "UC-5 Delegate to a subagent" as UC5 + usecase "UC-6 Teleport to the cloud" as UC6 + usecase "UC-7 Complete first-run setup" as UC7 +} + +Developer --> UC1 +Developer --> UC3 +Developer --> UC4 +Developer --> UC6 +Developer --> UC7 +Scripter --> UC2 +ACP --> UC1 +UC1 ..> UC5 : <> +UC4 ..> UC1 : <> +UC1 --> LLM +UC2 --> LLM +@enduml +---- + +== Persona Use Cases (Cockburn, Fully Dressed) + +=== UC-1: Run an interactive coding session + +Primary Actor:: Developer + +Trigger:: Developer runs link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] (optionally with a positional prompt) in +a trusted directory [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/entrypoint.py#L35-L40[vibe/cli/entrypoint.py:35-40]]. + +Stakeholders & Interests:: +* Developer — wants the task done with control over each tool action. +* LLM Provider — receives the conversation and returns tool calls. + +Preconditions:: +* An API key is configured, or onbarding (UC-7) runs first. +* The working directory is trusted, or the developer accepts the trust + prompt [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. + +Main Success Scenario:: +. Developer submits a message. +. System appends the message and runs the middleware pipeline + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L855-L937[vibe/core/agent_loop.py:855-937]]. +. System sends the conversation to the LLM. +. System receives an assistant message; if it contains tool calls, the + System resolves permissions and runs the tools concurrently + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1217-L1259[vibe/core/agent_loop.py:1217-1259]]. +. Steps 2–4 repeat until the assistant emits no tool call. +. System displays the final assistant message. + +Extensions:: +* 4a. A tool's permission is `ASK`: System prompts; on rejection it + records the rejection and feeds it back to the LLM + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1098-L1116[vibe/core/agent_loop.py:1098-1116]]. +* 2a. The turn or price limit is reached: middleware stops the loop + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L49-L78[vibe/core/middleware.py:49-78]]. +* 2b. Context tokens cross the model threshold: System auto-compacts + (UC extension, see `arc42` Chapter 6). +* *. Developer presses Escape: in-flight tools are cancelled and the + turn ends cleanly [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L1244-L1259[vibe/core/agent_loop.py:1244-1259]]. + +Postconditions:: +* Success: the conversation is persisted as `meta.json` + + `messages.jsonl` and the TTY last-session pointer is updated + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_logger.py#L36-L97[vibe/core/session/session_logger.py:36-97]]. + +Business Rules:: BR-1, BR-2, BR-3, BR-4 (see <>). + +=== UC-2: Run Vibe programmatically + +Primary Actor:: Scripter / CI + +Trigger:: link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] -p "" [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L254-L262[README.md:254-262]]. + +Preconditions:: An API key is configured. + +Main Success Scenario:: +. System runs headless with the `auto-approve` agent + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L235-L250[vibe/cli/cli.py:235-250]]. +. System runs the agent loop, enforcing `--max-turns` and `--max-price`. +. System emits the result through the selected output formatter — text, + json or streaming-json [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/output_formatters.py#L46-L119[vibe/core/output_formatters.py:46-119]]. +. System exits. + +Extensions:: +* 2a. `session_cost` exceeds `--max-price`: `PriceLimitMiddleware` stops + the run [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L65-L78[vibe/core/middleware.py:65-78]]. +* 2b. `--max-turns` reached: `TurnLimitMiddleware` stops the run. +* 1a. `--enabled-tools` is set: only the listed tools are available + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L270[README.md:270]]. + +Postconditions:: The result is on stdout in the chosen format; exit code +reflects success or a limit interruption. + +Business Rules:: BR-3, BR-5. + +=== UC-3: Resume a past session + +Primary Actor:: Developer + +Trigger:: link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] --continue or link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] --resume [ID] +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L128-L190[vibe/cli/cli.py:128-190]]. + +Preconditions:: `session_logging.enabled` is true; at least one saved +session exists. + +Main Success Scenario:: +. `--continue` resolves the latest session for the current TTY/working + directory; `--resume ID` resolves a specific session; `--resume` with + no ID opens a picker. +. System replays the non-system messages into the agent loop and + reattaches the session and parent ids [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L180-L190[vibe/cli/cli.py:180-190]]. +. The interactive session continues from the restored state. + +Extensions:: +* 0a. `session_logging.enabled` is false: System reports the feature is + unavailable and exits [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L134-L139[vibe/cli/cli.py:134-139]]. + +Postconditions:: The session continues; new messages append to the same +(or a forked) session folder. + +Business Rules:: BR-2. + +=== UC-4: Plan before acting + +Primary Actor:: Developer + +Trigger:: Developer selects the `plan` agent (`--agent plan` or +`Shift+Tab`) [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L113-L138[README.md:113-138]]. + +Preconditions:: None beyond UC-1. + +Main Success Scenario:: +. System runs the read-only `plan` agent; only safe tools auto-approve + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L117[vibe/core/agents/models.py:117]]. +. System writes a live markdown plan file + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/plan_session.py#L11-L42[vibe/core/plan_session.py:11-42]]. +. Developer reviews (and may edit) the plan. +. Developer (or the agent via `exit_plan_mode`) confirms; System + switches to `accept-edits` or `default` and executes + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/exit_plan_mode.py#L62-L137[vibe/core/tools/builtins/exit_plan_mode.py:62-137]]. + +Extensions:: +* 3a. Developer edits the plan file: the change is detected and the + updated plan is injected into the conversation + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L939-L956[vibe/core/agent_loop.py:939-956]]. + +Postconditions:: The agent is in an executing profile and proceeds per +the agreed plan. + +Business Rules:: BR-4. + +=== UC-5: Delegate work to a subagent + +Primary Actor:: Developer (indirectly, via the agent) + +Trigger:: The agent invokes the `task` tool +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/task.py#L107-L150[vibe/core/tools/builtins/task.py:107-150]]. + +Main Success Scenario:: +. System spawns a nested agent loop running the named subagent + (`explore` by default). +. The subagent works without user interaction and returns a response, + the turns used and a completion flag. +. The parent conversation continues with the subagent's result. + +Postconditions:: The subagent's work is summarised back without +overloading the parent context [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L142-L156[README.md:142-156]]. + +Business Rules:: BR-4 (subagent profile bounds its tools). + +=== UC-6: Teleport a session to the cloud + +Primary Actor:: Developer + +Trigger:: `/teleport` or link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] --teleport +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/commands.py#L115-L120[vibe/cli/commands.py:115-120]]. + +Preconditions:: The plan is teleport-eligible; the working directory is +a git repository [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/plan_offer/decide_plan_offer.py#L61-L62[vibe/cli/plan_offer/decide_plan_offer.py:61-62]]. + +Main Success Scenario:: +. System validates the git repository. +. System confirms the working commit is pushed + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/git.py#L27-L100[vibe/core/teleport/git.py:27-100]]. +. System starts a remote Vibe Code workflow and attaches a remote event + source [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/teleport.py#L46-L150[vibe/core/teleport/teleport.py:46-150]]. +. The session continues in the cloud; events stream back to the UI. + +Extensions:: +* 1a. Not a git repo / commit not pushed: System emits a + `TeleportPushRequiredEvent` and waits. +* 0a. Not authenticated: System emits a `TeleportAuthRequiredEvent`. + +Postconditions:: The session runs as a remote workflow; the UI shows its +events. + +Business Rules:: BR-6. + +=== UC-7: Complete first-run setup + +Primary Actor:: Developer + +Trigger:: link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] is run with no API key configured, or link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] --setup +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/README.md#L204-L209[README.md:204-209]]. + +Main Success Scenario:: +. System shows the Welcome screen, then the auth-method choice + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/onboarding/screens/welcome.py#L46-L80[vibe/setup/onboarding/screens/welcome.py:46-80]]. +. Developer chooses browser sign-in or manual API-key entry. +. System persists the key to `~/.vibe/.env` (or the OS keyring) + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/setup/auth/api_key_persistence.py#L38-L64[vibe/setup/auth/api_key_persistence.py:38-64]]. + +Postconditions:: An API key is available for all future sessions. + +Business Rules:: BR-7. + +== System Use Cases (per interface) + +=== SUC-1: The link:https://github.com/mistralai/mistral-vibe/tree/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe[vibe/] command-line interface + +Input & validation:: Flags parsed by `argparse`: `-v/--version`, +positional `PROMPT`, `-p/--prompt`, `--max-turns` (int), `--max-price` +(float), `--enabled-tools` (repeatable; glob or `re:` regex), `--output` +(`text`|`json`|`streaming`), `--agent`, `--setup`, `--workdir` (path, +validated), `--add-dir` (repeatable path), `--trust`, `--teleport` +(hidden), `-c/--continue`, `--resume [ID]`. `--continue` and `--resume` +are mutually exclusive [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/entrypoint.py#L19-L134[vibe/cli/entrypoint.py:19-134]]. + +Processing:: `main()` validates `--workdir`, resolves trust, then +delegates to `run_cli` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/entrypoint.py#L166-L211[vibe/cli/entrypoint.py:166-211]]. + +Output & status:: Interactive mode opens the TUI; programmatic mode +prints in the chosen format and exits with a status code reflecting +success or a limit interruption. + +Error responses:: An untrusted directory prompts for trust; an invalid +`--workdir` aborts with an error. + +Environment:: `VIBE_HOME`, `LOG_LEVEL`, `LOG_MAX_BYTES`, `VIBE_*` config +overrides [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/entrypoint.py[vibe/cli/entrypoint.py] epilog]. + +=== SUC-2: In-session slash commands + +The `CommandRegistry` registers 23 commands +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/commands.py#L39-L177[vibe/cli/commands.py:39-177]]: `help`, `config`, `model`, `thinking`, +`reload`, `clear`, `copy`, `log`, `debug`, `compact`, `exit`, `status`, +`teleport`, `proxy-setup`, `resume` (`/continue`), `rename`, `mcp` +(`/connectors`), `voice`, `leanstall`, `unleanstall`, `rewind`, `loop`, +`data-retention`. Input is the text after `/`; unknown commands fall +through to the autocompletion menu. + +=== SUC-3: The `vibe-acp` ACP server + +Input & validation:: ACP requests over stdio; `--setup` triggers +onboarding [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/entrypoint.py#L32-L108[vibe/acp/entrypoint.py:32-108]]. + +Processing:: `VibeAcpAgentLoop` manages ACP sessions; `new_session` and +`load_session` create or restore them. + +Output:: ACP session updates; a reduced command set is advertised +(`help`, `compact`, `reload`, `log`, `proxy-setup`, `leanstall`, +`unleanstall`, `data-retention`) [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/commands/registry.py#L45-L89[vibe/acp/commands/registry.py:45-89]]. + +Error responses:: JSON-RPC 2.0 errors — `UnauthenticatedError`, +`InvalidRequestError`, `SessionNotFoundError`, `ContextTooLongError`, +`ConfigurationError` [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/acp/exceptions.py#L1-L141[vibe/acp/exceptions.py:1-141]]. + +=== SUC-4: The built-in tool contract + +Each tool subclasses `BaseTool` with a Pydantic args model, a result +model and a `BaseToolConfig` declaring the permission tier; `run()` is an +async generator [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L122-L152[vibe/core/tools/base.py:122-152]]. Tools raise +`ToolError` (user-facing) or `ToolPermissionError`. The numeric limits: + +[options="header",cols="1,1,3"] +|=== +|Tool |Default permission |Limits / validation + +|`bash` |ASK |Timeout 300 s (overridable); stdout+stderr capped at +16 000 bytes; `sudo` always asks [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/bash.py#L220-L257[vibe/core/tools/builtins/bash.py:220-257]]. +|`read_file` |ALWAYS |Read cap 64 000 bytes; `**/.env*` sensitive +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/read_file.py#L58-L67[vibe/core/tools/builtins/read_file.py:58-67]]. +|`write_file` |ASK |Write cap 64 000 bytes; `**/.env*` sensitive +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/write_file.py#L41-L48[vibe/core/tools/builtins/write_file.py:41-48]]. +|`search_replace` |ASK |Content cap 100 000 bytes; fuzzy threshold 0.9 +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/search_replace.py#L70-L77[vibe/core/tools/builtins/search_replace.py:70-77]]. +|`grep` |ALWAYS |100 matches, 64 000 output bytes, 60 s timeout +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/grep.py#L36-L83[vibe/core/tools/builtins/grep.py:36-83]]. +|`todo` |ALWAYS |At most 100 items [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/todo.py#L54-L56[vibe/core/tools/builtins/todo.py:54-56]]. +|`task` |ASK |Subagent allowlist defaults to `explore` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/task.py#L51-L53[vibe/core/tools/builtins/task.py:51-53]]. +|`ask_user_question` |ALWAYS |1–4 questions, 2–4 options each +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/ask_user_question.py#L27-L75[vibe/core/tools/builtins/ask_user_question.py:27-75]]. +|`webfetch` |ASK |Timeout 30 s default / 120 s max; 120 000-byte content +cap [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/webfetch.py#L62-L77[vibe/core/tools/builtins/webfetch.py:62-77]]. +|`websearch` |ASK |120 s timeout [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/websearch.py#L49-L55[vibe/core/tools/builtins/websearch.py:49-55]]. +|`skill`, `exit_plan_mode` |ALWAYS |— [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/skill.py#L35-L36[vibe/core/tools/builtins/skill.py:35-36], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/builtins/exit_plan_mode.py#L35-L36[vibe/core/tools/builtins/exit_plan_mode.py:35-36]]. +|=== + +=== SUC-5: The `config.toml` file format + +Input & validation:: TOML, parsed into a 60+-field `VibeConfig` Pydantic +model; kebab-case keys via field aliases [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/_settings.py#L495-L644[vibe/core/config/_settings.py:495-644]]. + +Processing:: Project `.vibe/config.toml` directories (BFS, ≤4 levels) +then `~/.vibe/config.toml` are merged field-by-field with declared merge +strategies [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/builder.py#L24-L122[vibe/core/config/builder.py:24-122]]. + +Error responses:: Invalid TOML or schema violations surface as a config +error before the session starts. + +=== SUC-6: MCP server configuration + +`mcp_servers` entries declare `transport` (`http`|`streamable-http`| +`stdio`), `url` or `command`/`args`, headers, env, and timeouts — +`startup_timeout_sec` (default 10.0) and `tool_timeout_sec` (default +60.0). Tools are exposed as `{server}_{tool}` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/config/_settings.py#L226-L243[vibe/core/config/_settings.py:226-243], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/mcp/registry.py#L80-L150[vibe/core/tools/mcp/registry.py:80-150]]. + +== Supplementary Specifications + +=== Entity Model + +[plantuml,entity-model,svg] +---- +@startuml +hide circle +skinparam classAttributeIconSize 0 + +class Session { + session_id : str + parent_session_id : str + title : str + working_directory : str +} +class LLMMessage { + role : Role + content : Content + tool_calls : ToolCall[] + message_id : str +} +class AgentStats { + steps : int + session_prompt_tokens : int + session_completion_tokens : int + context_tokens : int + session_cost : float +} +class AgentProfile { + name : str + safety : AgentSafety + agent_type : AgentType +} +class Checkpoint { + message_index : int +} +class FileSnapshot { + path : str + content : str +} + +Session "1" o-- "many" LLMMessage : messages.jsonl +Session "1" -- "1" AgentStats +Session "1" -- "1" AgentProfile : active +Session "1" o-- "many" Checkpoint : rewind +Checkpoint "1" o-- "many" FileSnapshot +LLMMessage "1" o-- "many" ToolCall +@enduml +---- + +Evidence: link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/types.py#L47-L138[vibe/core/types.py:47-138] (AgentStats), +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/types.py#L219-L329[vibe/core/types.py:219-329] (LLMMessage), +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L48-L94[vibe/core/agents/models.py:48-94] (AgentProfile), +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/rewind/manager.py#L16-L49[vibe/core/rewind/manager.py:16-49] (Checkpoint, FileSnapshot), +link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/session/session_loader.py#L16-L17[vibe/core/session/session_loader.py:16-17] (Session). + +=== State Machine — Agent Profile + +[plantuml,sm-profile,svg] +---- +@startuml +[*] --> Default +Default --> Plan : select plan +Plan --> AcceptEdits : exit_plan_mode +Plan --> Default : exit_plan_mode +Default --> AcceptEdits : cycle +AcceptEdits --> AutoApprove : cycle +AutoApprove --> Default : cycle +Default --> [*] +@enduml +---- + +The `plan` and `chat` profiles are read-only; `accept-edits` +auto-approves edits only; `auto-approve` approves everything +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L110-L199[vibe/core/agents/models.py:110-199], link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L174-L216[vibe/core/middleware.py:174-216]]. + +=== Activity — One Conversation Turn + +[plantuml,act-turn,svg] +---- +@startuml +start +:append user message; +repeat + :run middleware before_turn; + if (STOP / COMPACT?) then (yes) + :handle middleware result; + stop + else (no) + endif + :call LLM; + if (assistant produced tool calls?) then (yes) + :resolve permissions; + :run tools concurrently; + :append tool results; + else (no) + :display final message; + stop + endif +repeat while (continue) +@enduml +---- + +Evidence: link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agent_loop.py#L855-L937[vibe/core/agent_loop.py:855-937]. + +=== Validation Rules (EARS) + +* When a tool call is resolved, the System shall determine a permission + tier of `ALWAYS`, `ASK` or `NEVER` before executing it + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113]]. +* While the active agent is `plan` or `chat`, the System shall reject + mutating tools [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L174-L216[vibe/core/middleware.py:174-216]]. +* If `session_cost` exceeds `--max-price`, the System shall stop the + conversation loop [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/middleware.py#L65-L78[vibe/core/middleware.py:65-78]]. +* When a folder containing `.vibe/` is first entered, the System shall + require a trust decision before loading its project config + [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. +* While `enable_telemetry` is false or no API key is present, the System + shall not send usage telemetry [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/telemetry/send.py#L88-L95[vibe/core/telemetry/send.py:88-95]]. + +[[business-rules]] +== Cross-cutting Business Rules + +[options="header",cols="1,3"] +|=== +|ID |Rule + +|BR-1 |A folder must be trusted before its project config is loaded +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/trusted_folders.py#L75-L122[vibe/core/trusted_folders.py:75-122]]. +|BR-2 |Session continuation and resume require `session_logging.enabled` +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L134-L139[vibe/cli/cli.py:134-139]]. +|BR-3 |Tool execution honours the `ALWAYS` / `ASK` / `NEVER` tier +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/tools/base.py#L83-L113[vibe/core/tools/base.py:83-113]]. +|BR-4 |The active agent profile bounds tool autonomy; `plan`/`chat` are +read-only [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/agents/models.py#L110-L199[vibe/core/agents/models.py:110-199]]. +|BR-5 |Programmatic mode forces the `auto-approve` agent +[link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/cli/cli.py#L47-L48[vibe/cli/cli.py:47-48]]. +|BR-6 |Teleport requires a teleport-eligible plan and a git repository +with the working commit pushed [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/teleport/git.py#L27-L100[vibe/core/teleport/git.py:27-100]]. +|BR-7 |Telemetry is sent only when `enable_telemetry` is true and an API +key is present [link:https://github.com/mistralai/mistral-vibe/blob/f71bfd3b8cdddbb4369c843da9c48d5f6c637ef7/vibe/core/telemetry/send.py#L88-L95[vibe/core/telemetry/send.py:88-95]]. +|=== + +[WARNING] +==== +Whether each business rule is a hard product requirement or an +implementation choice is *deferred* to the Product Owner and Domain +Expert (see `OPEN_QUESTIONS.adoc`). +==== + +== Acceptance Criteria (Gherkin) + +These criteria are derived from code behaviour. The *canonical* "done" +definition per use case — and per-test traceability — is *deferred* to +the Product Owner and Developer. + +[source,gherkin] +---- +Feature: Tool permission enforcement (UC-1, BR-3) + + Scenario: An ASK-tier tool is rejected + Given the active agent is "default" + And the LLM requests a "bash" command + When the developer rejects the approval prompt + Then the command is not executed + And a rejection result is sent back to the LLM + And tool_calls_rejected is incremented + +Feature: Programmatic price ceiling (UC-2, BR-5) + + Scenario: The price limit stops the run + Given vibe runs with --prompt and --max-price 1.0 + When the estimated session_cost exceeds 1.0 dollars + Then the conversation loop stops + And the partial result is emitted in the chosen output format + +Feature: Session resume requires logging (UC-3, BR-2) + + Scenario: Resume with logging disabled + Given session_logging.enabled is false + When the developer runs vibe --continue + Then vibe reports that resume is unavailable + And vibe exits without starting a session + +Feature: Trust gate (UC-1, BR-1) + + Scenario: First entry into an untrusted project + Given a directory contains a .vibe folder + And the directory is not in trusted_folders.toml + When the developer starts vibe there + Then vibe asks the developer to trust the folder + And project config is loaded only after the developer accepts +----