feat: add Mistral Vibe external agent plugin#4
Conversation
… script Analyze Mistral Vibe (v2.6.2) for external agent protocol compatibility. Key findings: no native hook system, but rich session management via ~/.vibe/logs/session/ with meta.json + messages.jsonl (structured JSONL). Capabilities: transcript_analyzer=true, token_calculator=true, hooks=false. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 21de3e7235f8
Comprehensive reference for adding lifecycle hooks to Mistral Vibe's open source codebase. Covers hook config format, payload schemas, integration points in the agent loop, and how it maps to the Entire CLI external agent protocol. Designed to be used as a standalone reference by an implementing agent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 8b8486ede423
Implement the entire-agent-mistral-vibe binary with full protocol subcommand support (info, detect, sessions, transcripts, hooks, transcript analysis). Add 30 E2E tests covering all subcommands and register Mistral Vibe as an agent for shared lifecycle tests. Updated AGENT.md to reflect verified lifecycle hooks (session_start, user_prompt_submit, pre_tool_use, post_tool_use, turn_end) with captured payloads from real Vibe sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 5cdc3adc6a68
- InstallHooks now adds the repo to ~/.vibe/trusted_folders.toml so Vibe reads project-level .vibe/config.toml (required for hooks to fire) - ParseHook turn-end now globs ~/.vibe/logs/session/ to find the actual session directory matching the session ID prefix - Add comprehensive unit tests for agent, hooks, and transcript analysis using golden JSONL data from real Vibe session captures Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e108d2f670de
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
| } | ||
|
|
||
| return os.WriteFile(configPath, []byte(strings.Join(filteredLines, "\n")), 0o600) | ||
| } |
There was a problem hiding this comment.
UninstallHooks leaves orphan TOML table headers in config
High Severity
UninstallHooks only filters lines containing hookMarker ("entire hooks mistral-vibe"), but the TOML section headers like [[hooks.session_start]] don't contain that marker. After filtering, orphan [[hooks.session_start]], [[hooks.user_prompt_submit]], etc. lines remain. The remaining check only handles empty or "[hooks]", so these orphan headers cause the file to be rewritten with invalid leftover TOML instead of being deleted. This leaves .vibe/config.toml in a broken state after uninstall.
| content := strings.Join(tomlLines, "\n") | ||
| if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil { | ||
| return 0, fmt.Errorf("failed to write config.toml: %w", err) | ||
| } |
There was a problem hiding this comment.
InstallHooks overwrites existing project config file entirely
High Severity
InstallHooks builds hook TOML lines from scratch and writes them via os.WriteFile, which truncates .vibe/config.toml. Any existing Vibe project-level configuration (model settings, custom tools, other TOML sections) in that file is silently destroyed. The Kiro agent avoids this by writing hooks to separate dedicated files rather than the main config.
| return a | ||
| } | ||
| return b | ||
| } |
…ead min Extract duplicated session ID fallback above the switch in ParseHook, simplify redundant sessionRef guard in ReadSession, and remove unused min helper (builtin in Go 1.21+). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: caf6d3c50a6a
SessionPersistence: ParseHook(turn-end) returned empty SessionRef when
Vibe's native logs were absent (e.g. test envs), so the entire CLI
skipped session writing. Add ensurePlaceholderTranscript() fallback that
creates a {} file at .entire/tmp/{id}.json, matching Kiro's pattern.
RewindAfterCommit: WaitForCheckpoint only waited for checkpoint branch
advancement, but condensation runs async. Replace the single rewind-list
check with condition-based polling (30s) for the shadow point to become
logs-only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 99e4793da14a
The entire CLI does not call write-session during hook processing.
For Kiro, captureTranscriptForStop copies the transcript into
.entire/tmp/ as a side effect of parse-hook, so a session file exists.
Vibe was returning the native log path (~/.vibe/logs/session/...)
directly as SessionRef — nothing was written to .entire/tmp/.
Add cacheTranscriptForTurnEnd which copies the native Vibe transcript
into .entire/tmp/{sessionID}.json and returns the local path. Falls
back to a {} placeholder when native logs are absent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: cd955dffa249
Replace magic numbers (1, 2, 3) for event types with named constants EventTypeSessionStart, EventTypeTurnStart, EventTypeTurnEnd. Remove unused NativeToProtocolHook map and its test — the mapping was only tested, never used in production code. Remove unused VibeSessionMeta and VibeSessionStats types (placeholder definitions with no consumers). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: d140fbb39172
* Fix Windows + Kiro IDE bugs in kiro external agent Addresses entireio/cli#1054. Four user-reported symptoms on Windows + Kiro IDE were all caused by missing platform branches in the adapter. - IDE hook command form: emit `cmd /c "<cmd> <NUL"` on Windows so the parent `entire` CLI inherits a closed stdin and hook commands stop spinning. POSIX still uses `sh -c '<cmd> </dev/null'`. Trusted-commands glob and uninstall migration updated to match. - Native session identity resolved before minting a UUID: `parse-hook` now prefers raw payload session/conversation IDs, then the latest IDE `sessionId` from `sessions.json`, then the cached Entire ID, then the CLI `conversation_id`. The session cache survives across `stop` so multi-turn IDE chats stay in one Entire session. - Windows storage paths added to `kiroDataDir` (`%LOCALAPPDATA%`) and `kiroExtensionStorageDir` (`%APPDATA%`). - Workspace-sessions cwd encoding now lowercases the drive letter and converts forward slashes to backslashes before base64, matching what Kiro IDE actually writes to disk on Windows. - CLI conversation lookup no longer shells out to `sqlite3`; uses embedded modernc.org/sqlite so Windows installs without sqlite3 on PATH still work. - Hook JSON files no longer HTML-escape `<` and `>` (still valid JSON, much more readable when inspecting `.kiro/hooks/*.kiro.hook`). - `KIRO_DEBUG=1` env gate writes step-by-step capture diagnostics to `.entire/tmp/kiro-debug.log` for triaging path-resolution misses on user machines. - Tests cover Windows hook emission, drive-letter and slash normalization, sqlite-without-binary fallback, and updated session lifecycle assertions (cache no longer cleared on stop). - AGENT.md documents the new Windows form, the resolver order, the cache lifetime change, and the embedded-SQLite shift. - README adds a backup install path (`entire-agent-kiro install-hooks`) and notes the `external_agents: true` setting required for runtime discovery. - docs/investigations/ captures the multi-agent investigation that produced the diagnosis and proposed fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: c029e9e70480 * remove docs * Fix multi-chat transcript miss and session-ID rekey on Kiro IDE Addresses two issues from adversarial review: - ensureIDETranscript ignored its session-ID parameter and always read the newest entry from sessions.json. In a workspace with multiple Kiro chats, a stop event from an older tab would checkpoint the newest tab's transcript instead. Fix: take the resolved IDE session ID through to the transcript reader and prefer it when the matching file exists on disk; fall back to "latest by dateCreated" only when no IDE session ID is supplied. - ParseHook overwrote the cached Entire session ID on every turn with whatever resolveHookSessionIdentity returned, which could rekey a conversation mid-flight (e.g. when a native Kiro ID only became available by stop time) and never populated EventJSON.PreviousSessionID for downstream migration. Fix: track the IDE session ID in a separate cache file (kiro-active-ide-session) alongside the Entire session ID and reuse the cached Entire ID whenever the cached IDE session ID still matches the latest one on disk; only rekey when a genuinely new Kiro chat is observed, and emit the prior Entire ID via PreviousSessionID so consumers can merge / close out the old session. Resolver result is now a sessionIdentity struct rather than a tuple, which makes the rekey signal explicit at the call site. Regression tests: - TestEnsureIDETranscriptPrefersResolvedIDESessionOverLatest — two IDE sessions in one workspace, the resolver-supplied (older) ID wins. - TestEnsureIDETranscriptFallsBackToLatestWhenIDESessionMissing — preserves the prior fallback when no IDE session ID is supplied. - TestParseHookSessionIDStableAcrossTurnsInSameIDEChat — two prompts in the same chat share one Entire session, no PreviousSessionID. - TestParseHookRekeysAndEmitsPreviousSessionIDOnNewKiroChat — a new Kiro chat tab triggers a rekey, second event carries the prior Entire session ID in PreviousSessionID. AGENT.md updated to document the new resolver order and the two-file session cache. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 369efe4bf134 * Replace global session cache with per-chat keying Addresses both follow-ups from adversarial review #2: 1. Session resolution was based on repo-global cache files (`kiro-active-session` + `kiro-active-ide-session`) that any tab in the workspace could overwrite. With two open chats, a hook from tab A could resolve to tab B's session ID and emit the wrong previous_session_id. Replaced with deterministic per-chat resolution keyed off the most-recently-modified `<sessionId>.json` file in the IDE workspace-sessions directory: - The IDE writes the user prompt / assistant response to the active chat's transcript file before invoking the hook, so file mtime is the closest signal we have to "which tab fired". - The IDE session UUID is used directly as the Entire session ID, making each Kiro chat keyed deterministically for its lifetime. - No per-chat state file required; switching tabs naturally resolves to the other chat's existing stable ID without overwriting either. - Rekey + previous_session_id concept removed entirely — there's no rekey to migrate, each chat is its own independent stable session. 2. IDE transcript trimming shared one offset bucket across all chats (the offset file was keyed by `parsed.ConversationID`, which is always empty for IDE-derived transcripts). After capturing chat A, capturing chat B silently dropped its first N entries. Refactored `trimTranscriptHistory(raw, chatKey)` to take an explicit per-chat key; `ensureIDETranscript` passes the IDE session ID and `ensureCachedTranscript` passes the CLI conversation_id. Each chat now writes its own `kiro-transcript-offset-<key>.json` and is completely isolated from other chats' offsets. Regression tests: - TestParseHookSwitchingKiroChatTabsResolvesEachTabIndependently — three turns across two tabs (A → B → back to A) each resolve to their own stable Entire session ID with no cross-tab state. - TestParseHookSessionIDStableAcrossTurnsInSameIDEChat — unchanged contract: two turns in the same chat produce the same session ID. - TestEnsureIDETranscriptOffsetsAreIsolatedPerChat — capturing chat A then chat B preserves chat B's history (was previously dropped) and leaves chat A's offset bucket untouched. Removed `kiro-active-ide-session` cache (replaced by mtime detection), `previousSessionID` from the resolver result struct (no rekey needed), and the obsolete TestParseHookRekeysAndEmitsPreviousSessionIDOnNewKiroChat. AGENT.md updated to document the new per-chat keying. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: fdd6e3fabde7 * Bind IDE turn identity and isolate tool-calls per chat Addresses both blocking findings from adversarial review #3: 1. resolveHookSessionIdentity re-resolved the IDE chat by mtime on every hook, so a tab switch between user-prompt-submit and stop would silently rebind the turn to a different chat — wrong session_id, wrong transcript captured, original session orphaned. Fix: user-prompt-submit writes the resolved IDE session ID to a new per-turn cache (`.entire/tmp/kiro-active-turn`). A new resolveStopIdentity prefers that cached binding over fresh mtime resolution, so the turn finalizes against whichever chat fired the prompt regardless of which chat became newest by mtime in between. The cache is cleared at end of stop so the next turn discovers freshly. mtime resolution is still the entry point for the very first prompt-submit (and as a fallback when no turn binding exists). 2. post-tool-use appended to a single repo-global tool-calls jsonl, so in interleaved multi-chat flows one chat's stop would consume another chat's tool history (and the second chat's stop would see nothing). Fix: tool-calls are now written per chat (`.entire/tmp/kiro-tool-calls-<sanitized-id>.jsonl`) keyed by the active-turn cache (or, if absent, mtime). transcript capture reads only the chat that's stopping. The legacy global file is preserved for CLI-only flows that have no IDE chat ID at all. Regression tests: - TestParseHookTurnIdentityStableAcrossTabSwitchBetweenPromptAndStop — prompt-submit in tab A, bump tab B's mtime to make B newest, stop must still emit chat-A's session ID. - TestParseHookToolCallsAreIsolatedPerChat — sequential A→stop→B produces per-chat jsonl files; chat B's file does not contain chat A's tool calls and the legacy global file is never written. AGENT.md updated to document the active-turn cache and the per-chat tool-call file layout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: d2a5c28b62e7 * Stop CLI conversation_id from masquerading as an IDE session `rawSessionID` previously fell through to `conversation_id`, so a CLI hook payload with only `conversation_id` set populated `ideSessionID` with the CLI value. The stop path then tried `ensureIDETranscript` first; when no `<conversation_id>.json` existed it silently fell back to "latest IDE session" and checkpointed an unrelated IDE chat for what was actually a CLI-only turn. Fixes: - Split `rawSessionID` into `rawIDESessionID` (IDE-only fields: `session_id`, `sessionId`, `chatSessionId`) and `rawConversationID` (CLI-only: `conversation_id`). `chatSessionId` is no longer a CLI fallback either — it references a Kiro IDE execution log and would point ensureCachedTranscript at the wrong row. - `resolveHookSessionIdentity` and `resolveStopIdentity` now keep CLI and IDE identity sources cleanly separated. An explicit `conversation_id` in the payload short-circuits IDE mtime discovery (otherwise the resolver would still find an unrelated IDE chat by mtime and use it as the session). - `captureTranscriptForStop` skips `ensureIDETranscript` entirely when `identity.ideSessionID == ""`, so CLI-only turns can't silently checkpoint another chat's transcript via the "latest IDE" fallback. Regression test: - TestParseHookCLIConversationIDDoesNotMasqueradeAsIDESession plants unrelated IDE workspace data in the same repo, then fires a CLI-only stop with `conversation_id`. The captured transcript must address `cli-conv.json`, not the IDE chat, and must not contain the IDE chat's content. Addresses finding 2 from adversarial review #4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 7507f0edbb5a * Make CLI conversation_id beat IDE turn cache in stop and post-tool-use Two follow-on findings from adversarial review #5: 1. resolveStopIdentity consulted the repo-global active-turn IDE cache before honoring an explicit CLI conversation_id in the payload. With an in-flight IDE turn (cache populated) and a concurrent CLI stop in the same repo, the CLI stop would inherit the IDE turn's session ID and route through ensureIDETranscript, capturing the wrong chat. Fix: in resolveStopIdentity, short-circuit on rawConversationID before reading the active-turn cache. Explicit CLI signal in the payload now always beats any cached IDE turn binding. 2. post-tool-use resolved its tool-calls jsonl key purely via activeTurnOrLatestIDESessionID, which falls back to mtime-detected IDE chats. CLI tool calls in a repo with any IDE workspace data therefore got appended to an IDE-scoped jsonl. CLI stop only clears the global file (key=""), so the IDE-keyed file survived and would later contaminate an IDE transcript. Fix: replace activeTurnOrLatestIDESessionID with resolvePostToolUseChatKey, which mirrors the resolver precedence: explicit IDE session ID → IDE-keyed file; explicit conversation_id → global file (matching where CLI stop will clear); active-turn cache → IDE-keyed file; mtime fallback as last resort. CLI tool calls now land where the matching CLI stop will read them. Regression tests: - TestResolveStopIdentityPrefersConversationIDOverActiveTurnCache — seed kiro-active-turn with an IDE chat ID, then fire a CLI stop with conversation_id; the CLI conv must win and the captured transcript must not contain the IDE chat's content. - TestPostToolUseWithConversationIDDoesNotLeakIntoIDEChat — fire a CLI post-tool-use with conversation_id in a repo containing IDE workspace data; the IDE chat's tool-calls file must not be created and the global tool-calls file must contain the CLI tool call. Addresses both findings from adversarial review #5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 9e6c2257dee8 * Stop CLI/IDE flows from clobbering each other's stop-hook state Two follow-on findings from adversarial review #6, both about the same underlying problem: CLI and IDE stop paths shared too much state and could corrupt each other under concurrent use in one repo. 1. ParseHook(stop) cleared kiro-active-turn unconditionally. CLI stops would delete the in-flight IDE turn binding, so the IDE's later post-tool-use/stop hooks would fall back to fresh mtime resolution and possibly finalize the wrong chat. Fix: only clear the cache when the stop being handled actually owns it (`identity.ideSessionID != ""`). CLI stops leave the IDE turn cache alone. 2. resolveStopIdentity, when serving an IDE turn from the active-turn cache, queried SQLite for "the latest CLI conversation in this repo" and stuffed it into conversationID. If ensureIDETranscript later degraded for any transient reason, the CLI fallback in captureTranscriptForStop would capture an unrelated CLI transcript under the IDE session's file. Two-part fix: - resolveStopIdentity no longer auto-injects an inferred CLI conv; conversationID stays empty unless the payload supplies it. - captureTranscriptForStop skips ensureCachedTranscript entirely when the identity is IDE-bound with no proven CLI conversation link. IDE-bound stops fail closed to a placeholder rather than silently capturing an arbitrary CLI conversation. Regression tests: - TestCLIStopPreservesInFlightIDETurnCache — seed kiro-active-turn, fire a CLI stop with conversation_id, assert the cache survives. - TestIDEStopFromTurnCacheDoesNotInjectArbitraryConversationID — plant an unrelated CLI conversation in SQLite, fire an IDE stop served from the turn cache (no IDE workspace data so ensureIDETranscript fails), assert the captured transcript does NOT contain the unrelated CLI content. Addresses both findings from adversarial review #6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: b5e3658f5afc * Owner-check active-turn cache writes and clears Two surgical guards on `.entire/tmp/kiro-active-turn` to fix the remaining cache-clobber findings without restructuring the cache: 1. user-prompt-submit only writes the cache when the resolved identity actually has an IDE session ID. CLI prompts (where ideSessionID is "") used to call writeActiveTurnIDESessionID(""), which deletes the file and stranded any in-flight IDE turn's binding. 2. stop only clears the cache when the value on disk still names this stop's IDE chat. Overlapping IDE turns (A prompt → B prompt → A stop) used to let A's stop delete B's binding because the clear was gated only on `identity.ideSessionID != ""`. Regression tests: - TestCLIPromptSubmitDoesNotClearInFlightIDETurnCache — seed an IDE binding, fire a CLI prompt-submit, assert binding survives. - TestOverlappingIDEStopOnlyClearsItsOwnTurnCache — cache holds B, IDE stop A arrives with sessionId=chat-A, assert B's binding survives. AGENT.md adds a "Concurrency limitations" section documenting the fundamental limit of a single repo-global active-turn cache: under truly overlapping IDE turns, A's stop has no payload signal to distinguish it from B's, so the resolver returns whichever chat is currently cached. The owner-check prevents cross-deletion but cannot correct mis-resolution without a per-turn signal Kiro doesn't provide. This is a known limitation, not a fix target. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 51d63fea90e2 * Strip KIRO_DEBUG capture diagnostics The env-gated step-by-step logger added during the Windows path investigation has served its purpose. captureTranscriptForStop now collapses to its essential branching logic without the debug plumbing — same behavior, ~70 fewer lines, no dead code paths in normal operation. If a similar diagnostic is needed again, structured slog logging is the right place to add it rather than re-introducing this bespoke file logger. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f9b2f6086ff0 * Fix golangci-lint findings on the kiro agent Two CI lint failures from PR #24: 1. transcript.go: `"windows"` literal had 9 occurrences in production code — flagged by goconst. Extracted to a const `osWindows` near the existing `runtimeGOOS` declaration; replaced all production call sites in transcript.go (3) and hooks.go (4). Test files keep the literal so test-only changes don't depend on a non-test const. 2. transcript.go: blank import of `modernc.org/sqlite` was missing a justifying comment — flagged by revive. Added a comment explaining it registers the "sqlite" driver consumed by `sql.Open` in openSQLiteDB. `modernc.org/sqlite` is still required: openSQLiteDB calls `sql.Open("sqlite", path)` to read Kiro's `data.sqlite3` for CLI conversation lookup, and the blank import is the standard way to register a database/sql driver. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: eb59aee85c49 * Approve modernc.org/mathutil license (BSD-3-Clause) LicenseFinder couldn't auto-detect the license on `modernc.org/mathutil` v1.7.1 — its LICENSE file contains the standard BSD-3-Clause text but no SPDX identifier line, so the scanner reports it as "unknown" and fails the licenses CI gate. Confirmed manually: LICENSE file in the published module is the canonical BSD-3-Clause text. mathutil is pulled in transitively by modernc.org/sqlite (the embedded SQLite driver entire-agent-kiro now uses for Kiro CLI conversation lookup so Windows installs without a sqlite3 binary still work). Adding a `:license` decision per LicenseFinder's documented mechanism maps the package to BSD-3-Clause, which is already in the project's permitted list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: ac10cec41cfa --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>


Summary
Add Mistral Vibe as an external agent plugin for the Entire CLI, implementing the full agent binary protocol.
entire-agent-mistral-vibe) with all protocol subcommands:info,detect,get-session-id,get-session-dir,resolve-session-file,read-session,write-session,read-transcript,chunk-transcript,reassemble-transcript,format-resume-command,parse-hook,install-hooks,uninstall-hooks,are-hooks-installed,get-transcript-position,extract-modified-files,extract-prompts,extract-summary.vibe/config.tomland trusts the repo directory via~/.vibe/trusted_folders.tomlcreatePlaceholderTranscriptpattern)RewindAfterCommittest timing: condition-based polling for condensation instead of single checkTest plan
go test ./...— 40/40 passgo vet -tags=e2e ./...SessionPersistence,RewindAfterCommit(requires full Entire CLI + Vibe binary)entire enable --agent mistral-vibein a repo with.vibe/🤖 Generated with Claude Code