Kiro
- ID:
kiro - Store (CLI):
~/.kiro/sessions/cli/<sessionId>.jsonl, with the header~/.kiro/sessions/cli/<sessionId>.jsonbeside it - Store (IDE):
~/.kiro/sessions/<workspace>/sess_<uuid>/messages.jsonl, withsession.jsonbeside it - Store (headless CLI):
kiro-cli/data.sqlite3in the local data directory (~/Library/Application Supporton macOS,~/.local/shareon Linux,%LOCALAPPDATA%on Windows), tableconversations_v2 - Read override:
DEJA_KIRO_ROOTreplaces the session root,DEJA_KIRO_DBthe database - Format: JSONL, one shape per client, and a SQLite row per headless conversation
- Needs:
sqlite3for the database only
Kiro (kiro.dev) ships a CLI and an IDE — a VS Code fork — and they do not write
the same file. The CLI stores a pair per session: a header naming the session id
and the directory it ran in, and a transcript whose records are
{"kind":"Prompt"|"AssistantMessage","data":{…}} with the text in
data.content[].data and data.meta.timestamp in seconds. The IDE stores one
directory per session under the workspace, holding session.json (id, model,
workspacePaths, timestamps) and messages.jsonl, whose lines carry
payload.type and payload.content with an RFC 3339 timestamp.
kiro-cli chat --no-interactive writes neither file. Each conversation is a
row of conversations_v2 keyed by the directory it ran in, and the row's JSON
history holds the turns: a Prompt or ToolUseResults user side, a
ToolUse or Response assistant side. `kiro-cli chat --resume-id
<conversation_id>` reopens a row.
Last verified: 2026-10-08
Known quirks and drift
- Resume:
cd <cwd> && kiro-cli chat --resume-id <sessionId>, which needs
Kiro CLI 2.2.0 or newer. kiro-cli finds a session by id from any directory,
but runs it in the current one and rewrites the header's cwd to it, so the
command runs in the directory the header names (#4305). With that directory
gone the cd is left out and deja notes where it will run instead (#4460).
sess_is not only the IDE's.kiro-cli --v3(2.22.0 ships the V3
engine behind that flag) writes the same <workspace>/sess_<uuid> layout
and adds each session to ~/.kiro/session-index/<workspace>.jsonl. A
session listed there resumes with `kiro-cli --v3 chat --resume-id
sess_<uuid> in its first workspacePaths` directory; one that is not is the
IDE's, reopens from the app, and deja resume says so (#4307). On a
logged-in 2.22.0 TUI turn V3 writes the answer as assistant records with
operationType: "Say", the call as tool_call (execute_bash with
args.command, status, actionType) and its output as tool_result;
all three are read. Its thinking is an assistant record too, with
operationType: "Reasoning", "..." for content and the text encrypted
in reasoningSignature: it is not an answer and is dropped.
session_start carries the system prompt and pending_interaction /
interaction_resolved the approval prompt; neither is a turn.
- A reply arrives in pieces. Several
AssistantMessagerecords can share
one data.message_id: the CLI appends the answer as it streams, each record
carrying the next piece rather than the whole answer so far. Read one message
per record and a recall quotes a third of a sentence, so a run under one id is
joined in order. tokscale's reader sums the same records for the same reason
(crates/tokscale-core/src/sessions/kiro.rs).
- Two shapes in one IDE file. Current builds write the
payloadwrapper;
before that the same file held flat {"role":…,"content":…} lines. Both are
read, so a store that predates the change is not lost.
- The IDE file also carries the agent's own bookkeeping —
session_metadata,
usage_summary, turn_end. Those are not turns: a record whose type is not
one is dropped rather than attributed to a role, and a test pins that. A
tool_call record is not a turn either, but it holds the call: toolName
and args are read into commands (execute_bash, execute_pwsh), files,
edits and written lines (fs_write and fs_append with path and text,
str_replace with oldStr/newStr, read_file, delete_file with
targetFile). The engine writes one such line per state an action passes
through; only the one that says how it ended (completed or failed) is
read, a denied call is not, and a failed edit keeps its file but not its
sides (#4506).
- Tool calls. The TUI writes
toolUseparts inside an
AssistantMessage and the results as a ToolResults record; the database
has tool_uses and ToolUseResults. The TUI names its tools shell,
write and read (content, oldStr, newStr, operations[].path); the
headless path keeps execute_bash, fs_write and fs_read (file_text,
old_str, new_str). Both are read into commands, files, edits and tool
output (#4299). A command carries → exit N from its result's
exit_status (#4505). Only the Prompt record carries a timestamp, so the reply
and its calls take the prompt's.
- Not read yet. The IDE mirrors chats into its globalStorage
(kiro.kiroagent/<workspace>/*.chat beside extensionless execution records).
No sample is in hand, and a reader built against a guessed shape is one that
drops history without saying so.
- Wiring:
deja install kirowrites the server into
~/.kiro/settings/mcp.json, which the CLI and the IDE both read — the same
file kiro-cli mcp add --scope global writes. One thing the installer
cannot do for you: a custom agent (~/.kiro/agents/<name>.json) reads the
global servers only when it sets "includeMcpJson": true, and the install
note says so. The agent kiro-auto writes sets it.
- Guidance:
deja install kirowrites~/.kiro/steering/deja.md, the global
half of steering — scanned for every project, alongside the workspace one.
It is four lines on purpose. A steering document declares an inclusion mode,
and the only mode measured as loaded by kiro-cli is always: manual is not
loaded and cannot be invoked from a session, fileMatch was withheld
(KiroCrew's steering reference, measured against 2.19.1). So this text is in
front of every turn whether it is wanted or not, which is why it names the
tool and stops rather than carrying the full skill deja writes where a skill
is loaded on demand.
- Skill:
deja install kiroalso writes~/.kiro/skills/deja-history/SKILL.md.
On kiro-cli 2.28 a skill there is listed in disclose_context in both
engines (#4802).
- Command:
~/.kiro/prompts/deja.md, a global prompt. V3 lists it in slash
completion as /deja; V2 takes @deja or /prompts get deja. The 2.22.0
TUI lists it as /deja ("Prompt from global") and sends the file as the user
message with the arguments in it. The file has no frontmatter, since a
prompt is sent as written.
- Global hooks:
deja install kiro-autowrites~/.kiro/hooks/deja.json
({"version":"v1","hooks":[…]}): SessionStart runs hook-context --plain,
UserPromptSubmit hook-prompt --plain, SessionEnd hook-session-end. The
IDE and the kiro-cli --v3 TUI run it in every chat, whatever the agent;
--v3 --no-interactive runs none of it. Both outputs reach the model. On
2.28 SessionEnd fires on leaving the TUI; 2.22.0 maps SessionEnd to Stop,
so there it fires after every turn and not on /quit. A turn-end clear is
harmless: the next prompt marks the session live again. PreToolUse and
PostToolUse stdout is dropped
(sendStdout:false in acp-server.js), so a pre-edit line or a fix for a
failed command can only ride the next prompt, which waits on deferred
delivery. The default V2 engine ignores this file and runs the agent below.
- Auto-recall:
deja install kiro-autowrites the above and an agent of
deja's own, ~/.kiro/agents/deja.json (tools: ["*"], includeMcpJson),
with two hooks: agentSpawn runs deja hook-context --plain and
userPromptSubmit runs deja hook-prompt --plain. kiro-cli adds what those
print to the model's context, the agentSpawn output for the whole
conversation; what preToolUse and postToolUse print is not sent. So
two more hooks hold their answer for the next userPromptSubmit, which
hands it over once: preToolUse runs deja hook-tool --defer for the line
about a file write, fs_write, fs_append or str_replace edits, and
postToolUse runs deja hook-tool-after --defer for the fix pair of a
failed shell or execute_bash. Both arrive after the step they are about.
stop runs deja hook-session-end: V2 has no session end, and stop fires
after every turn, so a finished session reaches the next one's MCP recall
at once instead of staying marked live for twenty minutes (#5152). An
agent hook may name no session, so the working directory keys it. The hooks run in that agent only:
kiro-cli chat --agent deja, or kiro-cli agent set-default deja. The
built-in kiro_default takes no hooks from a file (a kiro_default.json is
ignored), and deja does not switch chat.defaultAgent, which would trade
the default agent's prompt for its own. deja doctor reads the row as
installed until chat.defaultAgent is deja and that agent file still
runs deja's hooks (kiro-cli falls back to kiro_default when it is gone,
5200), and uninstall removes that setting when it names deja's agent.
kiro-cli edits the agent in place — kiro-cli mcp add --agent deja adds a
server, kiro-cli agent upgrade turns the hooks into a list — so install
and uninstall touch only deja's keys and hook entries, in either shape, and
uninstall keeps the file while something of the reader's is in it (#5199). The IDE's .kiro/hooks/ are a
different system: per workspace, fired on file events (#4304).
- Sub-agents: 2.32's
subagenttool runs each stage as a session of its own,
whose hooks name only that session. The header beside its transcript names
the spawner in parent_session_id, which deja reads, so the sub-agent hangs
under it and its digest and recall leave the parent out (#5153).
session_created_reason says subagent on top-level sessions too and is
not read.
Measured on a live install
kiro-cli 2.22.0 with its own mock model (KIRO_MOCK_CHAT_RESPONSE), after
deja install kiro-auto: kiro-cli chat --no-interactive --agent deja sent a
request whose context carried the <deja-recall> digest naming the earlier
kiro session in that directory and its answer. A probe agent with all five
hooks echoing a marker showed the agentSpawn and userPromptSubmit markers in
the request and the preToolUse and postToolUse ones nowhere.
The 2.22.0 TUI (kiro-cli chat --agent deja, same mock, request bodies read
from its trace log): a write to a file two earlier sessions decided about
parked the pre-edit line, and the next prompt's request carried it in the
hook context ahead of the user message. A failed shell call did the same
with its fix pair. /deja retry loop sent deja.md as the user message with
the query in its command. A rule from deja rules sync reached the request
through ~/.kiro/steering/deja-rules.md. /quit ran no SessionEnd: the V2
agent has none.
kiro-cli 2.22.0 (homebrew cask), in a hermetic HOME:
deja install kirowrites~/.kiro/settings/mcp.jsonand
~/.kiro/steering/deja.md, and says the one thing a reader has to know: a
custom agent in ~/.kiro/agents/*.json reads the global MCP servers only with "includeMcpJson": true.
--resume-id <SESSION_ID>is in its own help, which is the command
deja resume prints for a CLI session. -r, --resume-picker are beside it.
- The steering file is loaded: its text is in the request.
kiro-cli 2.22.0 logged in, real model, hermetic HOME holding only the login:
kiro-cli mcp listlistsdejaforkiro_defaultand under global. In
kiro-cli chat --no-interactive --agent deja the model called the deja
tool (mode: recall) and answered from what it returned. The V3 TUI's
default agent got mcp_deja_deja in its tool list and called it too.
kiro-cli --v3 chatTUI, default agent: the first request carried the
SessionStart digest and the UserPromptSubmit recall from
~/.kiro/hooks/deja.json, and the model quoted both. `--v3 chat
--no-interactive` ran no hook from that directory at all.
kiro-cli chat --agent dejaTUI: the digest reached the model, which
quoted the earlier session's rule back before editing.
deja reads this format and 42 others, and turns what it finds into memory your agents can search. See the harness matrix for what is wired where, or install it and search your own history.
Found this useful? Star deja-vu on GitHub.