scream-code 0.19.2 → 0.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ # Architecture
2
+
3
+ Eight packages, one application. Dependencies point downward only.
4
+
5
+ ```
6
+ apps/scream-code terminal UI (TUI), CLI, commands
7
+ ├── packages/node-sdk session API over RPC (no compile-time dep on the engine)
8
+ ├── packages/agent-core engine: agent loop, tools, permissions, prompts, records
9
+ │ ├── packages/ltod LLM abstraction (providers, streaming, tokens)
10
+ │ ├── packages/jian execution environment (process, fs, env)
11
+ │ ├── packages/memory memo store (FTS5 + tags + vectors)
12
+ │ └── packages/knowledge local knowledge base
13
+ ├── packages/config platform config, model catalog, i18n
14
+ └── packages/evals evaluation harness
15
+ ```
16
+
17
+ The SDK reaches the engine through an RPC contract, not an import, so the
18
+ engine can run in-process or out-of-process without changing callers.
19
+
20
+ ## agent-core
21
+
22
+ | Area | Path | Role |
23
+ |---|---|---|
24
+ | Agent loop | `agent/index.ts` | turn lifecycle, tool dispatch, subagents |
25
+ | Tools | `tools/builtin/*` | 49 built-in tools across 15 groups |
26
+ | Permissions | `agent/permission/` | mode policy, rules, egress allowlist |
27
+ | Injection | `agent/injection/` | dynamic reminders (goal, permission mode, plan mode, user prefs, wolfpack, todo, working set, MCP guides) |
28
+ | Prompts | `profile/` | `default/system.md` template, conditional parts, self-asset map |
29
+ | Records | `agent/records/` | `wire.jsonl` persistence and replay |
30
+ | Compaction | `agent/compaction/` | summarisation and thresholds |
31
+ | Skills | `skill/` | scan, parse, registry |
32
+ | MCP | `mcp/` | config, stdio/HTTP clients, OAuth, tool naming |
33
+
34
+ ## Prompt assembly
35
+
36
+ ```
37
+ profile/default/system.md (nunjucks)
38
+ ├─ static part → conditional sections (IS_MAIN, CAN_SPAWN, HAS_WRITE_EDIT, OS)
39
+ └─ dynamic part → cwd listing, AGENTS.md, skills, self-asset map, role additions
40
+ ```
41
+
42
+ The two parts are separated so the static half stays cacheable. Mode changes do
43
+ not re-render the prompt; they are announced through the injection layer
44
+ instead.
45
+
46
+ ## Event flow
47
+
48
+ ```
49
+ user input → TUI controller → Session.prompt()
50
+ → core RPC → agent turn → LLM stream + tool calls
51
+ → records (wire.jsonl) + events
52
+ → Session event emitter → TUI controller → render
53
+ ```
54
+
55
+ Permissions are evaluated inside the tool dispatch path, before execution.
56
+ Denials return a reason to the model rather than throwing.
57
+
58
+ ## Testing
59
+
60
+ | Scope | Location |
61
+ |---|---|
62
+ | Engine, tools, prompts, sessions | `packages/*/test/` |
63
+ | TUI controllers, commands, rendering | `apps/scream-code/test/` |
64
+ | Cross-package behaviour | root vitest projects |
65
+
66
+ `pnpm exec vitest run` runs every project; `pnpm run lint:ratchet` enforces a
67
+ warning baseline that may only go down.
@@ -0,0 +1,95 @@
1
+ # Configuration
2
+
3
+ Everything persistent lives under the Scream home directory: `~/.scream-code`
4
+ (override with `SCREAM_CODE_HOME`).
5
+
6
+ ```
7
+ ~/.scream-code/
8
+ ├── config.toml # providers, models, permissions, hooks, services
9
+ ├── mcp.json # MCP servers (user level)
10
+ ├── image-config.json # image-generation endpoint
11
+ ├── skills/ # user skills
12
+ ├── memory/ # memo store
13
+ ├── knowledge/ # knowledge base
14
+ └── sessions/ # transcripts, bucketed by working directory
15
+ ```
16
+
17
+ A project can add its own layer with `.scream-code/` in the repository root
18
+ (`mcp.json`, `skills/`, `AGENTS.md`).
19
+
20
+ ## config.toml
21
+
22
+ Parsed by `packages/agent-core/src/config/schema.ts`.
23
+
24
+ | Section | Purpose |
25
+ |---|---|
26
+ | `providers` | Named endpoints: `type`, `apiKey`, `baseUrl`, `defaultModel`, `oauth`, `env`, `customHeaders`, `sessionHeader` |
27
+ | `models` | Model aliases (see below) |
28
+ | `defaultProvider` / `defaultModel` | What a new session uses |
29
+ | `defaultPermissionMode` | `yolo` \| `manual` \| `auto` \| `ask` \| `bot` |
30
+ | `defaultThinking` / `thinking` | Thinking mode and effort |
31
+ | `planMode` / `defaultPlanMode` | Plan-mode defaults |
32
+ | `yolo` | Yolo-mode defaults |
33
+ | `permission` | `rules[]` (`decision` allow/deny/ask, `scope`, `pattern`, `reason`) and `egressAllowlist` |
34
+ | `hooks` | Shell hooks fired on session events |
35
+ | `services` | Search providers (google / duckduckgo / bing / sogou / baidu) |
36
+ | `loopControl` | `maxStepsPerTurn`, retry and compaction thresholds |
37
+ | `background` | Background task limits |
38
+ | `secrets` | Redaction rules (`plain` / `regex`, `obfuscate` / `replace`, optional regex `flags`) |
39
+ | `extraSkillDirs` | Additional skill roots |
40
+ | `mergeAllAvailableSkills` | Merge every discovered skill instead of the project-scoped set |
41
+ | `raw` | Escape hatch for keys the schema does not model |
42
+
43
+ ### Provider types
44
+
45
+ `anthropic`, `openai`, `scream`, `google-genai`, `openai_responses`, `vertexai`.
46
+
47
+ `baseUrl` is optional: the built-in type supplies the default endpoint, which is
48
+ what makes "any OpenAI-compatible endpoint" work.
49
+
50
+ ### Model aliases
51
+
52
+ A model is an alias, never a raw provider model string:
53
+
54
+ ```toml
55
+ [models.my-model]
56
+ provider = "my-provider" # must match a key under [providers]
57
+ model = "vendor-model-id" # what the provider expects on the wire
58
+ maxContextSize = 200000
59
+ maxOutputSize = 16000
60
+ ```
61
+
62
+ Optional: `capabilities`, `displayName`, `reasoningKey`, `adaptiveThinking`,
63
+ `forceThinking`, `thinkingLevels`.
64
+
65
+ Aliases are what `/model`, `--model`, subagent profiles and goal judging refer
66
+ to. Removing a provider orphans every alias that pointed at it — Scream detaches
67
+ the model from a live session instead of ending the session when that happens.
68
+
69
+ ## Image generation
70
+
71
+ `~/.scream-code/image-config.json`, written by `/config image`:
72
+
73
+ ```json
74
+ { "provider": "...", "url": "https://.../images/generations", "api_key": "...", "model": "..." }
75
+ ```
76
+
77
+ `url` is the full endpoint, not a base URL. Placeholder values (empty,
78
+ `replace-with*`, `<...>`) count as unconfigured. The `image` skill and `/image`
79
+ read it through the `ImageGenerate` tool.
80
+
81
+ ## Resolution order
82
+
83
+ 1. CLI flag (`--model`, `--yolo`, `--auto`, `--plan`, `--permission-mode bot`)
84
+ 2. `config.toml` defaults
85
+ 3. Built-in defaults
86
+
87
+ `/config` writes `config.toml`; `/logout` removes a provider and clears any alias
88
+ that pointed at it.
89
+
90
+ ## Verifying a configuration
91
+
92
+ - `/config` — pick a provider, writes and reloads in one step
93
+ - `/model` — lists aliases that resolve against a live provider
94
+ - `/status`, `/usage` — what the current session is actually using
95
+ - `scream --version` — confirms which build is installed
package/docs/line.md ADDED
@@ -0,0 +1,156 @@
1
+ # Line — 原生通道(飞书 + 微信)
2
+
3
+ 用 `/line` 把 Scream Code 连到 IM:配置向导 + 后台守护进程。两个平台均为**官方接口**,无需公网 IP:
4
+ - **飞书**:开放平台 WebSocket 长连接
5
+ - **微信**:官方 ilink 机器人网关(`getupdates` 长轮询 + `sendmessage` 下发,微信官方 bot 开放接口)
6
+
7
+ ## 快速开始
8
+
9
+ 1. 在 TUI 里运行 `/line` → 选择「配置飞书(向导)」或「配置微信(向导)」
10
+ 2. 飞书:选**「跟着向导创建应用(推荐)」**——向导分步带你走完官方流程(创建应用 → 启用机器人 → 开通权限 → 长连接订阅 → 发布 → 粘贴凭证,约 3 分钟,无需公网 IP),每步都有直达链接与说明;微信:选**「扫码登录(推荐)」**——终端显示登录链接,手机微信打开确认即可,token 与白名单自动写入。
11
+ 已有现成凭证 → 直接「手动输入」粘贴(微信的现成 token 获取见下文)。
12
+ 3. 凭证实测校验通过后写入 `~/.scream-code/line/config.json`(权限 0600)。
13
+ 4. 选择「立即启动」→ 后台守护进程拉起,footer 状态段显示 `◆ Line: Lark` / `◆ Line: Wechat`;守护没跑时显示 `◆ Line: 未连接`。
14
+ 5. 在 IM 里给机器人发消息,scream 会在对应会话里处理并回复。
15
+
16
+ ## 单通道约束
17
+
18
+ `/line` 一次只服务**一个**通道:配置飞书后,微信向导不可再选(反之亦然)。要切换平台:`/line` → **卸载**(停守护 + 清 `~/.scream-code/line/`),再运行另一平台的向导。直接手改配置文件塞进两个平台会被守护进程拒绝启动(fail-fast,不会静默双跑)。切换后,旧平台的会话仍保留在 `/sessions` 里(独立命名空间,互不干扰),只是不再被新通道使用。
19
+
20
+ ## TUI 与 CLI 命令
21
+
22
+ | 命令 | 作用 |
23
+ |---|---|
24
+ | `/line` | 向导 + 启动 / 停止 / 重启 / 卸载(TUI 内) |
25
+ | `scream line` | 前台运行守护进程(调试用) |
26
+ | `scream line start` / `stop` / `status` | 后台启停与状态查询 |
27
+
28
+ ## 配置文件
29
+
30
+ `~/.scream-code/line/config.json`(0600)。飞书选项名与飞书开放平台/业界惯用命名对齐,微信选项名与 ilink 网关字段对齐:
31
+
32
+ ### 通用字段
33
+
34
+ | 字段 | 默认 | 说明 |
35
+ |---|---|---|
36
+ | `permissionMode` | `auto` | 会话权限模式(见下节),IM 默认 `auto` |
37
+ | `autoStart` | true | TUI 启动时自动拉起守护进程 |
38
+ | `workDir` | 守护启动目录 | 会话工作目录 |
39
+
40
+ ### 飞书(`feishu`)
41
+
42
+ | 字段 | 默认 | 说明 |
43
+ |---|---|---|
44
+ | `app_id` / `app_secret` | 必填 | 飞书应用凭证 |
45
+ | `allow_from` | 空 = 允许所有人 | 用户 ID 白名单(逗号分隔)。**建议配置** |
46
+ | `allow_chat` | 空 = 允许所有群 | 群聊 ID 白名单(仅群聊生效) |
47
+ | `group_only` | false | 只响应群聊 |
48
+ | `require_mention` | true | 群聊必须 @机器人(`false` 表示群内消息全部响应) |
49
+ | `share_session_in_channel` | false | 会话按「群」共享(默认按「群 + 用户」隔离) |
50
+ | `reaction_emoji` | `OnIt` | 收到消息的回应表情;`none` 关闭 |
51
+ | `done_emoji` | 关 | 完成后的表情(如 `THUMBSUP`);`none` 关闭 |
52
+ | `domain` | `https://open.feishu.cn` | Lark 国际版填 `https://open.larksuite.com` |
53
+
54
+ ### 微信(`weixin`)
55
+
56
+ | 字段 | 默认 | 说明 |
57
+ |---|---|---|
58
+ | `token` | 必填 | ilink bot Bearer token |
59
+ | `base_url` | `https://ilinkai.weixin.qq.com` | 网关根地址(运营商不一致时才改) |
60
+ | `allow_from` | 空 = 允许所有人 | ilink 用户 ID 白名单(形如 `xxx@im.wechat`)。**建议配置** |
61
+ | `route_tag` | 空 | 运营商要求的 `SKRouteTag` 请求头 |
62
+ | `long_poll_timeout_ms` | 35000 | 长轮询超时 |
63
+ | `dedup_window_seconds` | 300 | 去重窗口(ilink 可能因 ACK 延迟重发同一消息) |
64
+ | `cdn_base_url` | `https://novac2c.cdn.weixin.qq.com/c2c` | 媒体 CDN 根地址(仅网关换域名时改) |
65
+
66
+ **微信 token 获取**——扫码登录是主路径,全程在 `/line` 向导内完成,**无需安装任何其他工具**:
67
+ 1. `/line` → 「配置微信(向导)」→ **「扫码登录(推荐)」**:向导向官方网关申请登录链接 → 你在手机微信中打开并确认 → token 自动写入配置,你的微信 ID 自动加入 `allow_from` 白名单
68
+ 2. 已有现成 token(运营商提供)→ 「手动输入 token」粘贴
69
+
70
+ **首次激活**:token 有效但会话过期(errcode -14)时,启动守护进程后用允许的微信账号给机器人**发一条消息**即可完成会话关联(context_token 机制——回复必须携带对方消息里的 context_token)。
71
+
72
+ **微信能力边界**:支持文字、引用(`[引用: …]` 前缀)、语音转写文本、**图片 / 文件 / 语音 / 视频收发**(见「媒体收发」);回复按 3800 字符分块、代码围栏感知切分;typing「对方正在输入」指示自动开启/结束。
73
+
74
+ ## 媒体收发
75
+
76
+ 两个平台都支持**图片与文件双向传输**,模型能看到你发的图,也能把工作目录里的图/文件发回给你。
77
+
78
+ | 方向 | 飞书 | 微信 |
79
+ |---|---|---|
80
+ | 入站图片 | `image` 消息、富文本 `post` 内嵌图;同一次连发的多张图在 800 ms 内合并为一轮(**回复某条消息时会立即发送,不做批量**,以免丢掉引用文本) | `image_item`,CDN 下载 + AES-128-ECB 解密 |
81
+ | 入站文件 | `file` / `audio` / `media`(视频)以及富文本 `post` 的 `files[]` → 落盘后把路径交给模型 | `file_item` / `video_item` / 无转写的 `voice_item` → 同上 |
82
+ | 出站 | `image.create` / `file.create` 上传后按 `image` / `file` 消息发出 | `getuploadurl` → CDN 加密上传 → `sendmessage` 媒体 item(图片/文件/视频/**语音**) |
83
+ | 单张上限 | 入站图片 8 MiB;附件入站 32 MiB;出站 25 MiB | 同左(入站 CDN 单包 100 MiB 硬上限) |
84
+ | 大文件 | 下载走 `Range` 分块(绕过官方 SDK 在 ~2 MiB 处的截断),上限 512 MiB | 直连 CDN |
85
+
86
+ - **引用消息**:回复某条消息时会把被引用内容压成 `[引用: …]`(500 字截断)带进本轮;被引用消息里的图片(≤2 张)会一并取回,**文件只在引用者与被引用消息同一发送者时才取回**(防止群成员把别人的文件拉进自己的会话)。
87
+ - **入站附件落盘位置**:`~/.scream-code/line/inbox/<会话键>/<时间戳>-<文件名>`(0600,不写进你的项目目录)。
88
+ - **图片如何进模型**:作为 `image_url` 内容分片直接进入本轮对话,无需模型再读文件;附件则以路径形式告知,模型自行用 `ReadMediaFile` / `Read` 打开。
89
+ - **出站工具**:模型侧工具 `line_send_media`(仅在 IM 会话可见)。只允许发送**工作目录内**的普通文件(realpath 校验,防目录穿越与符号链接逃逸),单文件 ≤ 25 MiB;`.png/.jpg/.gif/.webp/.bmp` 按图片发送,`.amr/.wav/.mp3/.m4a/.aac/.ogg/.oga/.opus/.silk/.flac` 按语音发送,其余按文件发送;也可显式传 `kind`(`image` / `file` / `audio`)。
90
+ - **语音出站(微信)**:网关只接受 **AMR-NB**(8 kHz / 单声道 / 12.2 kbps)。非 AMR 音频先用 **ffmpeg** 转码(走 stdin/stdout 管道,不落临时文件);本机没有 ffmpeg、或 ffmpeg 缺少 AMR-NB 编码器时**自动降级为文件消息**,并在工具回执里说明原因,不会让整轮失败。
91
+ - macOS:`brew install ffmpeg`(注意 Homebrew 官方构建**可能不含** `libopencore_amrnb`,可用 `ffmpeg -encoders | grep amr` 自检)。
92
+ - Ubuntu/Debian:`sudo apt install ffmpeg`。
93
+ - Windows:从 ffmpeg.org 下载带 `libopencore-amr` 的构建并加入 `PATH`(`ffmpeg.exe`)。
94
+ - 仅 **AMR-NB**(`#!AMR\n`)会直接发送;AMR-WB(`#!AMR-WB`)及其他格式一律先转码。
95
+ - 飞书侧语音统一按**文件**发送(语音气泡仅微信支持),回执会注明。
96
+ - **失败回执**:图片/附件下载失败会回一句「图片下载失败,请重新发送一次。」,不会静默吞掉。
97
+
98
+ ## 在 IM 里可用的命令
99
+
100
+ 直接在对话里发斜杠命令,由守护进程拦截执行,**不会进入模型**(不消耗 token):
101
+
102
+ | 命令 | 作用 |
103
+ |---|---|
104
+ | `/status` | 当前模型、权限/计划/群狼/RLM 模式、上下文占用与思考级别 |
105
+ | `/model <名称>` | 切换模型;不带参数显示当前模型 |
106
+ | `/compact` | 压缩上下文 |
107
+ | `/stop` | 停止**正在运行**的那一轮,并丢弃该聊天还在排队的消息(IM 版的 ESC;忙时也可用),回执「已停止当前任务。」;若 10 秒内停不下来(工具挂死),会关闭在线会话,下一条消息自动恢复同一会话 |
108
+ | `/cancel` | 停止并开启新会话(等价 `/stop` + `/new`,忙时也可用) |
109
+ | `/cron` | 查看本聊天的定时任务(含「静默/静音」标记与「上次失败」);`/cron add [--silent\|--mute] <分 时 日 月 周> <要做的事>` 新建;`/cron rm <id>` 删除 |
110
+
111
+ - `--silent`:触发时不发「⏰ 已触发」,只发结果;`--mute`:什么都不发(纯后台维护任务)。模型侧 `CronCreate` 对应参数 `silent` / `mute`。
112
+ - 任务失败会在 `/cron` 列表里留一行「上次失败:…」,下次成功自动清除。
113
+ | `/new` | 丢弃当前会话,下条消息开新会话 |
114
+ | `/help` | 命令列表(含自定义命令) |
115
+ | `/自定义` | 你自己在配置里定义的命令(`prompt` 型或 `exec` 型) |
116
+
117
+ - 有任务正在处理时,`/model`、`/compact`、`/new` 会回复「请等这一轮结束(回复 /stop 可中止)」,避免与运行中的回合争用;`/stop` 本身**不受忙限制**。
118
+ - 消息排在别人后面时,会立刻回一条 `收到,前面还有 N 条在处理中。回复 /stop 可中止当前任务。`——不再静默排队。
119
+ - **定时任务现在能回到聊天**:`cron.fired` 在会话里触发后,守护进程会先发一行 `⏰ 定时任务已触发:…`,等这一轮跑完再把最终内容发到该聊天。投递靠入站时缓存的聊天目标(飞书 chat id / 微信 peer + context_token),因此**微信在守护进程重启后、你再次发消息之前无法主动推送**(与平台限制一致,日志会提示)。触发时若正有你的回合在跑,结果并入那一轮回复,不会重复推送。
120
+ - `/status` 末尾会带上启动时的语音能力探测结果(ffmpeg / AMR-NB 编码器是否可用),缺编码器意味着语音会降级成文件。
121
+ - **自定义命令**:配置项的 `commands` 数组里定义,`prompt` 型会把模板(`{args}` 会被替换,无占位符则把参数附在末尾)作为一轮输入发给模型;`exec` 型在守护进程里执行 shell(macOS/Linux 为 `/bin/sh -c`,Windows 为 `%ComSpec% /d /s /c`;30 s 超时、输出截断 8 KiB、参数通过 `LINE_ARGS` 环境变量传递、`work_dir` 必须位于工作目录内)。Windows 上出于命令注入安全考虑**不支持 `{args}` 占位符**,请在脚本里读 `%LINE_ARGS%`。命令名不能与内置命令重名、不能重复。
122
+ - **未识别的命令照常发给模型**;需要界面交互的命令(`/theme`、`/line` 向导、无参 `/model` 选择器、`/sessions`)会提示「需要桌面端」。
123
+
124
+ ## 模型如何感知当前通道
125
+
126
+ 每次建立/恢复会话时,守护进程会向该会话注入一段渠道说明(运行时 system prompt,不进入对话正文):当前是 Lark 还是 WeChat、能收什么、能发什么、**没有桌面截图能力**、超长回复会被分块。因此模型不会再说「我给你截图」,也不会声称看到了你并未发送的附件。
127
+
128
+ ## 运行边界
129
+
130
+ - **单轮上限 15 分钟**:超过即取消该轮并回复「本轮执行超时」,避免某次卡死的请求把整个会话队列永久占住。
131
+ - **`/new` 互斥**:重置进行中到达的消息会收到「会话正在重置,请稍后重发」;已有回合会先跑完再删存储。
132
+ - **关停**:先排空各会话队列(最多 10 秒),再关闭传输层与会话。
133
+
134
+ ## 权限与安全
135
+
136
+ - `permissionMode: auto`(**默认**)= 策略层在 deny 规则之后全量放行,且不产生任何交互面(不弹提问、不做计划评审)——最适合 IM。
137
+ - `permissionMode: yolo` = 同样全自动,但会保留计划评审(ExitPlanMode)这一交互路径,IM 下不推荐。
138
+ - `permissionMode: dontAsk` = 拒绝所有**需要审批**的工具调用(默认批准的只读/检索类仍会执行,属只读模式)。
139
+ - 三种模式都**不会**绕过 `deny` 规则;旧配置里显式写下的值保持原样,不会被默认值改写。
140
+ - 飞书群聊 @ 过滤是 **fail-closed**:机器人身份获取失败时宁可不响应,也不会抢答所有群消息。
141
+ - 微信 `allow_from` 未配置时任何私聊用户都能触发你的模型计费——**建议始终配置白名单**(ilink 用户 ID 形如 `xxx@im.wechat`)。
142
+ - Secret/token 以 0600 权限落盘;日志不含凭证。
143
+ - 远程会话与本地 TUI 会话互相独立,可在 `/sessions` 中查看(`source: line`)。
144
+
145
+ ## 守护进程
146
+
147
+ - 独立进程:TUI 退出不影响通道;pidfile `~/.scream-code/line/daemon.pid`(防双开 + 陈旧清理)。
148
+ - 日志:`~/.scream-code/line/daemon.log`(超 5MB 轮转为 `daemon.log.1`)。
149
+ - 微信状态:`~/.scream-code/line/weixin/`(长轮询游标 + context_token 缓存,均 0600,勿手动泄露)。
150
+ - `autoStart: true` 时随 TUI 启动自动拉起;崩溃后下次启动自动恢复。
151
+ - 优雅关停:SIGTERM/SIGINT(等待进行中的回合,最多 10 秒)。
152
+
153
+ ## 通道归属
154
+
155
+ - `/line` 是 Scream 唯一的远程通道:**飞书(Lark)与微信(WeChat)二选一**,配置向导与守护进程全部内置,不依赖任何第三方桥接工具。
156
+ - 已配置的通道会话在 `/sessions` 里标记 `line-feishu` / `line-weixin`,由通道管理(不可在 TUI 中删除,避免与守护进程争用)。
package/docs/mcp.md ADDED
@@ -0,0 +1,67 @@
1
+ # MCP Servers
2
+
3
+ Model Context Protocol servers extend the tool set with external processes or
4
+ HTTP endpoints. They are opt-in per session and configured in files, not flags.
5
+
6
+ ## Configuration files
7
+
8
+ ```
9
+ ~/.scream-code/mcp.json # user level
10
+ <project>/.scream-code/mcp.json # project level
11
+ <parent>/.scream-code/mcp.json # walked up from cwd, root → shallow
12
+ ```
13
+
14
+ On a name collision the file **nearest the working directory** wins: the project
15
+ file beats any parent, and a nearer parent beats a shallower one. Discovery
16
+ logic: `packages/agent-core/src/mcp/config-loader.ts`.
17
+
18
+ ## Shape
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "my-server": {
24
+ "command": "npx",
25
+ "args": ["-y", "some-mcp-server"],
26
+ "env": { "TOKEN": "..." }
27
+ },
28
+ "remote": {
29
+ "url": "https://example.test/mcp",
30
+ "headers": { "Authorization": "Bearer ..." }
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ - `command` + `args` → stdio transport (`client-stdio.ts`)
37
+ - `url` → HTTP transport (`client-http.ts`)
38
+ - OAuth flows live under `packages/agent-core/src/mcp/oauth/`.
39
+
40
+ ## How tools surface
41
+
42
+ MCP tool schemas are **not** in the request by default. The agent must call
43
+ `DiscoverMcpTools` first:
44
+
45
+ ```
46
+ DiscoverMcpTools({}) # server overview
47
+ DiscoverMcpTools({ server: "linear" }) # one server's tools
48
+ DiscoverMcpTools({ tools: ["mcp__linear__create_issue"] }) # enable for this session
49
+ ```
50
+
51
+ Enabled tools appear from the next step on, named
52
+ `mcp__<server>__<tool>` (`mcp/tool-naming.ts`). `list`/`info`-style reads are
53
+ free; mutating MCP tools go through the normal permission policy.
54
+
55
+ ## Managing from the TUI
56
+
57
+ - `/mcp` — list servers and their connection state
58
+ - Connection failures are reported per server and never block startup
59
+
60
+ ## Gotchas
61
+
62
+ - A server that fails to start does not remove its siblings; only its tools are
63
+ missing.
64
+ - Tools disabled with `DiscoverMcpTools` stay out of the request until enabled
65
+ again in the same session.
66
+ - `mcp.json` is project-scoped configuration; committing it shares the server
67
+ definition (and any literal secrets) with everyone who checks out the repo.
@@ -0,0 +1,69 @@
1
+ # Sessions
2
+
3
+ A session is a persisted conversation: one agent, one working directory, one
4
+ append-only record stream.
5
+
6
+ ## Storage layout
7
+
8
+ ```
9
+ ~/.scream-code/sessions/
10
+ └── <encoded-workdir>/
11
+ └── <session-id>/
12
+ ├── wire.jsonl # main agent records (append-only)
13
+ └── agents/<agent-id>/wire.jsonl
14
+ ```
15
+
16
+ - `wire.jsonl` is the source of truth: every message, tool call and tool result
17
+ in order. Corrupt lines are reported with the file path and line number
18
+ (`agent/records/persistence.ts`).
19
+ - `encoded-workdir` comes from `session/store/workdir-key.ts`, so sessions are
20
+ bucketed per project and a listing defaults to the current directory.
21
+ - An index (`session/store/session-index.ts`) caches id → metadata and is
22
+ validated by mtime + size, so another process's writes are picked up.
23
+
24
+ ## Lifecycle
25
+
26
+ | Action | Mechanism |
27
+ |---|---|
28
+ | Create | `Session` is created with a model alias; the first record opens the wire |
29
+ | Resume | `/sessions` (alias `/resume`) or `--session <id>`; the wire is replayed |
30
+ | Fork | `/fork` — copies the conversation into a new session id |
31
+ | Rename | `/title` |
32
+ | Close | `/exit`, or the TUI detaching from the session |
33
+
34
+ ## Compaction
35
+
36
+ Three layers, all automatic:
37
+
38
+ - **Micro** — keeps the most recent 20 messages (bounded to 40k tokens) and
39
+ truncates older ones.
40
+ - **Full** — at 85% of the context window, older turns are summarised and
41
+ replaced, preserving up to 24 recent messages (12 user, 30k tokens, at most
42
+ 25% of the window). At 90% the turn is blocked until compaction runs — older
43
+ context is never silently truncated away.
44
+ - **`/compact`** — triggers a full compaction manually. Useful to pick a pause
45
+ point instead of letting the engine interrupt mid-task.
46
+
47
+ The summary is marked unverified: re-check anything it asserts before acting on
48
+ it. Configurable via `loopControl.compactionTriggerRatio` (0.5–0.99).
49
+
50
+ ## Switching and concurrency
51
+
52
+ - One session is live at a time; its turns are serialised.
53
+ - Switching replays the target session's history and clears the current
54
+ transcript view, so the screen always matches the live session.
55
+ - Inputs queued in memory are not persisted. After a restart the wire replays
56
+ messages and tool results, not pending user input.
57
+
58
+ ## Export
59
+
60
+ | Command | Output |
61
+ |---|---|
62
+ | `/export-md` | Markdown transcript |
63
+ | `/export-debug-zip` | Bundle with diagnostics |
64
+ | `scream export [sessionId]` | ZIP from the CLI |
65
+
66
+ ## Deleting
67
+
68
+ `/sessions` → select → `d` (then `Enter` to confirm). The session currently open
69
+ cannot be deleted, and channel-owned (line) sessions are managed by their channel instead.
package/docs/skills.md ADDED
@@ -0,0 +1,73 @@
1
+ # Skills
2
+
3
+ A skill is a markdown package the agent loads on demand. It is prompt material,
4
+ not code: invoking a skill injects its body into the conversation.
5
+
6
+ ## Shape
7
+
8
+ Two layouts are supported:
9
+
10
+ ```
11
+ my-skill/SKILL.md # directory skill (may ship supporting files)
12
+ my-skill.md # flat skill
13
+ ```
14
+
15
+ `SKILL.md` needs YAML frontmatter:
16
+
17
+ ```markdown
18
+ ---
19
+ name: my-skill
20
+ description: One sentence shown in the skill listing.
21
+ ---
22
+
23
+ Body: the instructions the agent follows once the skill is invoked.
24
+ ```
25
+
26
+ - Both fields are required for a directory skill (`SKILL.md`): the parser throws,
27
+ and the scanner downgrades that to a warning, so the skill is skipped instead
28
+ of half-registered.
29
+ - Flat `<name>.md` skills are more forgiving — `name` falls back to the file name
30
+ and `description` to the first body line.
31
+
32
+ ## Where skills are discovered
33
+
34
+ | Root | Scope |
35
+ |---|---|
36
+ | `~/.scream-code/skills/` | user |
37
+ | `<git-root>/.scream-code/skills/` | project (the git root, not the current directory) |
38
+ | `<git-root>/.agents/skills/` | project (alternate convention) |
39
+ | `extraSkillDirs` in `config.toml` | extra roots |
40
+ | `plugins/managed/<plugin>/` | installed plugin packages |
41
+
42
+ Project skills override user skills with the same name. `mergeAllAvailableSkills`
43
+ in `config.toml` merges every discovered skill instead of the project-scoped set.
44
+
45
+ ## Invoking
46
+
47
+ - `Skill` tool — the agent calls it with `skill` and optional `args`.
48
+ - `/skill:<name>` — the user invokes one directly.
49
+ - `/skill` — pick from a list.
50
+ - A bare `/name` also works for built-in skills (e.g. `/dream`, `/image`).
51
+
52
+ ## Built-in skills
53
+
54
+ | Skill | Purpose |
55
+ |---|---|
56
+ | `image` | Generate or edit an image via the `ImageGenerate` tool; also `/image` |
57
+ | `dream` | Memory consolidation: plan → confirm → apply; also `/dream` |
58
+ | `make-skill` | Turn the current conversation into a reusable skill package |
59
+
60
+ ## Authoring a new skill
61
+
62
+ 1. Run `/make-skill` and answer the prompts, or write the `SKILL.md` yourself.
63
+ 2. `MakeSkillPlan` drafts a package (name, description, body, supporting files).
64
+ 3. `MakeSkillApply` writes it into the plugin center, where `/plugin` manages it.
65
+
66
+ Skills are listed in the system prompt when the session starts, so a skill added
67
+ mid-session needs a new session (or `/skill` refresh) to appear.
68
+
69
+ ## What a skill is not
70
+
71
+ - Not a subagent: no separate context, no separate tool set.
72
+ - Not a plugin: no executable entry point.
73
+ - Not a permission grant: invoking a skill does not widen the tool policy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.19.2",
3
+ "version": "0.19.5",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -26,7 +26,8 @@
26
26
  "scream": "dist/main.mjs"
27
27
  },
28
28
  "files": [
29
- "dist"
29
+ "dist",
30
+ "docs"
30
31
  ],
31
32
  "type": "module",
32
33
  "imports": {
@@ -56,6 +57,7 @@
56
57
  "dependencies": {
57
58
  "@earendil-works/pi-codemode": "^1.1.0",
58
59
  "@huggingface/tokenizers": "^0.2.0",
60
+ "@larksuiteoapi/node-sdk": "^1.76.0",
59
61
  "@liutod-scream/pi-tui": "^1.1.0",
60
62
  "@mariozechner/clipboard": "^0.3.2",
61
63
  "chalk": "^5.4.1",