scream-code 0.5.4 → 0.5.6
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.
- package/LICENSE +21 -0
- package/dist/main.mjs +517 -176
- package/package.json +24 -24
package/dist/main.mjs
CHANGED
|
@@ -33,10 +33,10 @@ import * as posixPath from "node:path/posix";
|
|
|
33
33
|
import * as win32Path from "node:path/win32";
|
|
34
34
|
import { createServer } from "node:http";
|
|
35
35
|
import * as z$1 from "zod/v4";
|
|
36
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
36
37
|
import { parse as parse$1, stringify } from "smol-toml";
|
|
37
38
|
import "zod/v3";
|
|
38
39
|
import * as z4mini from "zod/v4-mini";
|
|
39
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
40
40
|
import process$1 from "node:process";
|
|
41
41
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
42
42
|
import { Command, Option } from "commander";
|
|
@@ -50331,7 +50331,7 @@ function redactCtx(ctx) {
|
|
|
50331
50331
|
};
|
|
50332
50332
|
return walk(ctx, 0);
|
|
50333
50333
|
}
|
|
50334
|
-
function truncate$
|
|
50334
|
+
function truncate$3(value, max) {
|
|
50335
50335
|
return value.length <= max ? value : value.slice(0, max - 1) + ELLIPSIS$6;
|
|
50336
50336
|
}
|
|
50337
50337
|
function serializeValue(raw) {
|
|
@@ -50355,7 +50355,7 @@ function quote(value) {
|
|
|
50355
50355
|
return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n")}"`;
|
|
50356
50356
|
}
|
|
50357
50357
|
function formatPair(key, raw) {
|
|
50358
|
-
const limited = truncate$
|
|
50358
|
+
const limited = truncate$3(serializeValue(raw), CTX_VALUE_MAX_CHARS);
|
|
50359
50359
|
return `${SAFE_KEY_RE.test(key) ? key : quote(key)}=${/[\s="\\]/.test(limited) || limited.length === 0 ? quote(limited) : limited}`;
|
|
50360
50360
|
}
|
|
50361
50361
|
function clipBytes(text, maxBytes) {
|
|
@@ -50379,7 +50379,7 @@ function indentStack(stack) {
|
|
|
50379
50379
|
function formatEntry(entry, options = {}) {
|
|
50380
50380
|
const ctx = entry.ctx ? redactCtx(entry.ctx) : void 0;
|
|
50381
50381
|
const omitContextKeys = new Set(options.omitContextKeys ?? []);
|
|
50382
|
-
const msg = truncate$
|
|
50382
|
+
const msg = truncate$3(entry.msg, 200);
|
|
50383
50383
|
const pairs = [];
|
|
50384
50384
|
if (ctx) for (const [k, v] of Object.entries(ctx)) {
|
|
50385
50385
|
if (omitContextKeys.has(k)) continue;
|
|
@@ -60353,7 +60353,7 @@ var SkillRegistry = class {
|
|
|
60353
60353
|
this.indexPluginSkill(skill);
|
|
60354
60354
|
}
|
|
60355
60355
|
});
|
|
60356
|
-
for (const skill of skills) this.
|
|
60356
|
+
for (const skill of skills) this.register(skill);
|
|
60357
60357
|
}
|
|
60358
60358
|
registerBuiltinSkill(skill) {
|
|
60359
60359
|
this.register(skill.source === "builtin" ? skill : {
|
|
@@ -60362,9 +60362,20 @@ var SkillRegistry = class {
|
|
|
60362
60362
|
});
|
|
60363
60363
|
}
|
|
60364
60364
|
register(skill, options = {}) {
|
|
60365
|
-
const
|
|
60366
|
-
|
|
60365
|
+
const pluginId = skill.plugin?.id;
|
|
60366
|
+
const resolved = resolveUniqueSkillName(skill, this.byName, pluginId);
|
|
60367
|
+
if (resolved.shadowed) {
|
|
60368
|
+
this.onWarning(`Skill "${skill.name}" from ${skill.source}${pluginId !== void 0 ? ` (plugin "${pluginId}")` : ""} is shadowed by an existing skill with the same name.`);
|
|
60369
|
+
return;
|
|
60370
|
+
}
|
|
60371
|
+
const normalizedSkill = resolved.name === skill.name ? skill : {
|
|
60372
|
+
...skill,
|
|
60373
|
+
name: resolved.name
|
|
60374
|
+
};
|
|
60375
|
+
const key = normalizeSkillName(resolved.name);
|
|
60376
|
+
if (options.replace === true || !this.byName.has(key)) this.byName.set(key, normalizedSkill);
|
|
60367
60377
|
this.indexPluginSkill(skill, options);
|
|
60378
|
+
if (resolved.renamed) this.onWarning(`Skill "${skill.name}" from plugin "${pluginId}" was renamed to "${resolved.name}" because a skill with the same name is already registered.`);
|
|
60368
60379
|
}
|
|
60369
60380
|
getSkill(name) {
|
|
60370
60381
|
return this.byName.get(normalizeSkillName(name));
|
|
@@ -60416,6 +60427,46 @@ var SkillRegistry = class {
|
|
|
60416
60427
|
function pluginSkillKey(pluginId, skillName) {
|
|
60417
60428
|
return `${pluginId}\0${normalizeSkillName(skillName)}`;
|
|
60418
60429
|
}
|
|
60430
|
+
/**
|
|
60431
|
+
* Resolve a unique skill name when registering across multiple sources.
|
|
60432
|
+
*
|
|
60433
|
+
* Non-plugin skills are never renamed; if their name collides with an existing
|
|
60434
|
+
* skill they are considered shadowed and should be reported to the caller.
|
|
60435
|
+
* Plugin skills get a "plugin-id:skill-name" prefix on collision so users can
|
|
60436
|
+
* still invoke them and distinguish them in listings. If even the prefixed
|
|
60437
|
+
* form collides, a numeric suffix is appended.
|
|
60438
|
+
*/
|
|
60439
|
+
function resolveUniqueSkillName(skill, byName, pluginId) {
|
|
60440
|
+
const originalKey = normalizeSkillName(skill.name);
|
|
60441
|
+
if (!byName.has(originalKey)) return {
|
|
60442
|
+
name: skill.name,
|
|
60443
|
+
renamed: false,
|
|
60444
|
+
shadowed: false
|
|
60445
|
+
};
|
|
60446
|
+
if (pluginId === void 0) return {
|
|
60447
|
+
name: skill.name,
|
|
60448
|
+
renamed: false,
|
|
60449
|
+
shadowed: true
|
|
60450
|
+
};
|
|
60451
|
+
const prefixed = `${pluginId}:${skill.name}`;
|
|
60452
|
+
const prefixedKey = normalizeSkillName(prefixed);
|
|
60453
|
+
if (!byName.has(prefixedKey)) return {
|
|
60454
|
+
name: prefixed,
|
|
60455
|
+
renamed: true,
|
|
60456
|
+
shadowed: false
|
|
60457
|
+
};
|
|
60458
|
+
let index = 2;
|
|
60459
|
+
while (true) {
|
|
60460
|
+
const candidate = `${pluginId}:${skill.name}-${String(index)}`;
|
|
60461
|
+
const candidateKey = normalizeSkillName(candidate);
|
|
60462
|
+
if (!byName.has(candidateKey)) return {
|
|
60463
|
+
name: candidate,
|
|
60464
|
+
renamed: true,
|
|
60465
|
+
shadowed: false
|
|
60466
|
+
};
|
|
60467
|
+
index++;
|
|
60468
|
+
}
|
|
60469
|
+
}
|
|
60419
60470
|
const SOURCE_GROUPS = [
|
|
60420
60471
|
{
|
|
60421
60472
|
source: "project",
|
|
@@ -60452,12 +60503,12 @@ function formatFullSkill(skill) {
|
|
|
60452
60503
|
];
|
|
60453
60504
|
}
|
|
60454
60505
|
function formatModelSkill(skill) {
|
|
60455
|
-
const lines = [`- ${skill.name}: ${truncate$
|
|
60506
|
+
const lines = [`- ${skill.name}: ${truncate$2(skill.description, LISTING_DESC_MAX)}`];
|
|
60456
60507
|
if (typeof skill.metadata.whenToUse === "string" && skill.metadata.whenToUse.length > 0) lines.push(` When to use: ${skill.metadata.whenToUse}`);
|
|
60457
60508
|
lines.push(` Path: ${skill.path}`);
|
|
60458
60509
|
return lines;
|
|
60459
60510
|
}
|
|
60460
|
-
function truncate$
|
|
60511
|
+
function truncate$2(value, max) {
|
|
60461
60512
|
return value.length > max ? value.slice(0, max) : value;
|
|
60462
60513
|
}
|
|
60463
60514
|
function escapeAttr$1(value) {
|
|
@@ -90398,6 +90449,88 @@ function errorResult(serverName, error, authorizationUrl) {
|
|
|
90398
90449
|
};
|
|
90399
90450
|
}
|
|
90400
90451
|
//#endregion
|
|
90452
|
+
//#region ../../packages/agent-core/src/version.ts
|
|
90453
|
+
function getCoreVersion() {
|
|
90454
|
+
try {
|
|
90455
|
+
const raw = readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf-8");
|
|
90456
|
+
const pkg = JSON.parse(raw);
|
|
90457
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
90458
|
+
} catch {
|
|
90459
|
+
return "0.0.0";
|
|
90460
|
+
}
|
|
90461
|
+
}
|
|
90462
|
+
const SCREAM_MCP_CLIENT_VERSION = getCoreVersion();
|
|
90463
|
+
/**
|
|
90464
|
+
* Build the `RequestOptions` object accepted by the MCP SDK's `callTool`,
|
|
90465
|
+
* including either the configured tool-call timeout, an in-flight abort
|
|
90466
|
+
* signal, both, or neither. Returns `undefined` when nothing needs to be
|
|
90467
|
+
* passed so the SDK falls back to its defaults.
|
|
90468
|
+
*/
|
|
90469
|
+
function buildRequestOptions(toolCallTimeoutMs, signal) {
|
|
90470
|
+
if (toolCallTimeoutMs === void 0 && signal === void 0) return void 0;
|
|
90471
|
+
return {
|
|
90472
|
+
timeout: toolCallTimeoutMs,
|
|
90473
|
+
signal
|
|
90474
|
+
};
|
|
90475
|
+
}
|
|
90476
|
+
function toMcpToolDefinition(tool) {
|
|
90477
|
+
return {
|
|
90478
|
+
name: tool.name,
|
|
90479
|
+
description: tool.description ?? "",
|
|
90480
|
+
inputSchema: tool.inputSchema
|
|
90481
|
+
};
|
|
90482
|
+
}
|
|
90483
|
+
/**
|
|
90484
|
+
* Normalise the SDK's `callTool` return into ltod's {@link MCPToolResult}.
|
|
90485
|
+
* The SDK can return either the modern `{ content, isError }` shape or a
|
|
90486
|
+
* legacy `{ toolResult }` shape; we collapse the legacy shape to a single
|
|
90487
|
+
* text content block.
|
|
90488
|
+
*/
|
|
90489
|
+
function toMcpToolResult(result) {
|
|
90490
|
+
if (typeof result === "object" && result !== null && "content" in result) {
|
|
90491
|
+
const typed = result;
|
|
90492
|
+
if (Array.isArray(typed.content)) return {
|
|
90493
|
+
content: typed.content,
|
|
90494
|
+
isError: typed.isError === true
|
|
90495
|
+
};
|
|
90496
|
+
}
|
|
90497
|
+
if (typeof result === "object" && result !== null && "toolResult" in result) {
|
|
90498
|
+
const legacy = result.toolResult;
|
|
90499
|
+
return {
|
|
90500
|
+
content: [{
|
|
90501
|
+
type: "text",
|
|
90502
|
+
text: typeof legacy === "string" ? legacy : JSON.stringify(legacy)
|
|
90503
|
+
}],
|
|
90504
|
+
isError: false
|
|
90505
|
+
};
|
|
90506
|
+
}
|
|
90507
|
+
return {
|
|
90508
|
+
content: [],
|
|
90509
|
+
isError: false
|
|
90510
|
+
};
|
|
90511
|
+
}
|
|
90512
|
+
/**
|
|
90513
|
+
* Returns true when an MCP tool-call failure looks like a dead transport rather
|
|
90514
|
+
* than an application-level error. These are the cases where tearing down and
|
|
90515
|
+
* reconnecting the server is likely to help.
|
|
90516
|
+
*/
|
|
90517
|
+
function isRetriableMcpCallError(error) {
|
|
90518
|
+
if (!(error instanceof Error)) return false;
|
|
90519
|
+
const message = error.message.toLowerCase();
|
|
90520
|
+
return [
|
|
90521
|
+
/\be?connrefused\b/,
|
|
90522
|
+
/\be?connreset\b/,
|
|
90523
|
+
/\bepipe\b/,
|
|
90524
|
+
/\be?netunreach\b/,
|
|
90525
|
+
/\be?hostunreach\b/,
|
|
90526
|
+
/fetch failed/,
|
|
90527
|
+
/transport not connected/,
|
|
90528
|
+
/transport closed/,
|
|
90529
|
+
/network error/,
|
|
90530
|
+
/maximum reconnection attempts/
|
|
90531
|
+
].some((pattern) => pattern.test(message));
|
|
90532
|
+
}
|
|
90533
|
+
//#endregion
|
|
90401
90534
|
//#region ../../packages/agent-core/src/mcp/output.ts
|
|
90402
90535
|
const MCP_MAX_OUTPUT_CHARS = 1e5;
|
|
90403
90536
|
const MCP_OUTPUT_TRUNCATED_TEXT = `\n\n[Output truncated: exceeded ${String(MCP_MAX_OUTPUT_CHARS)} character limit. Use pagination or more specific queries to get remaining content.]`;
|
|
@@ -90911,7 +91044,7 @@ const PROFILE_SOURCES = {
|
|
|
90911
91044
|
"profile/default/coder.yaml": coder_default,
|
|
90912
91045
|
"profile/default/explore.yaml": explore_default,
|
|
90913
91046
|
"profile/default/plan.yaml": "extends: agent\nname: plan\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n Before designing your implementation plan, consider whether you fully understand the codebase areas relevant to the task. If not, recommend the parent agent to use the explore agent (subagent_type=\"explore\") to investigate key questions first. In your response, clearly state:\n 1. What you already know from the information provided\n 2. What questions remain unanswered that would benefit from explore agent investigation\n 3. Your implementation plan (either preliminary if questions remain, or final if sufficient context exists)\nwhenToUse: |\n Use this agent when the parent agent needs a step-by-step implementation plan, key file identification, and architectural trade-off analysis before code changes are made.\ntools:\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - WebSearch\n - FetchURL\n",
|
|
90914
|
-
"profile/default/system.md": "You are Scream Code CLI, an interactive general AI agent running on a user's computer.\n\nYour primary goal is to help users with software engineering tasks by taking action — use the tools available to you to make real changes on the user's system. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.\n\n{{ ROLE_ADDITIONAL }}\n\n# Prompt and Tool Use\n\nThe user's messages may contain questions and/or task descriptions in natural language, code snippets, logs, file paths, or other forms of information. Read them, understand them and do what the user requested. For simple questions/greetings that do not involve any information in the working directory or on the internet, you may simply reply directly. For anything else, default to taking action with tools. When the request could be interpreted as either a question to answer or a task to complete, treat it as a task.\n\nWhen handling the user's request, if it involves creating, modifying, or running code or files, you MUST use the appropriate tools (e.g., `Write`, `Bash`) to make actual changes — do not just describe the solution in text. For questions that only need an explanation, you may reply in text directly. When calling tools, do not provide explanations because the tool calls themselves should be self-explanatory. You MUST follow the description of each tool and its parameters when calling tools.\n\nIf the `Agent` tool is available, you can use it to delegate a focused subtask to a subagent instance. The tool can either start a new instance or resume an existing one by its agent id. Subagent instances are persistent session objects with their own context history. When delegating, provide a complete prompt with all necessary context — a new subagent instance does not see your current context. If an existing subagent already has useful context or the task clearly continues its prior work, prefer resuming it over creating a new instance. Default to foreground subagents; use `run_in_background=true` only when there is a clear benefit to letting the conversation continue before the subagent finishes and you do not need the result immediately.\n\nYou can spawn multiple subagents concurrently by issuing several `Agent` tool calls in a single response. The system executes all tool calls in parallel automatically. Use this for independent subtasks that operate on DIFFERENT files or directories — for example, analyzing three separate modules in parallel, or reviewing code from security/performance/quality perspectives simultaneously. Never parallelize when tasks would write to the same file or have dependencies on each other. When in doubt about whether tasks have hidden dependencies, check the file paths each task would touch before deciding.\n\nYou have the capability to output any number of tool calls in a single response. If you anticipate making multiple non-interfering tool calls, you are HIGHLY RECOMMENDED to make them in parallel to significantly improve efficiency. This is very important to your performance.\n\nThe results of the tool calls will be returned to you in a tool message. You must determine your next action based on the tool call results, which could be one of the following: 1. Continue working on the task, 2. Inform the user that the task is completed or has failed, or 3. Ask the user for more information.\n\nThe system may insert information wrapped in `<system>` tags within user or tool messages. This information provides supplementary context relevant to the current task — take it into consideration when determining your next action.\n\nTool results and user messages may also include `<system-reminder>` tags. Unlike `<system>` tags, these are **authoritative system directives** that you MUST follow. They bear no direct relation to the specific tool results or user messages in which they appear. Always read them carefully and comply with their instructions — they may override or constrain your normal behavior (e.g., restricting you to read-only actions during plan mode).\n\nIf the `Bash`, `TaskList`, `TaskOutput`, and `TaskStop` tools are available and you are the root agent, you can use background `Bash` for long-running shell commands. Launch it via `Bash` with `run_in_background=true` and a short `description`. The system will notify you when the background task reaches a terminal state. Use `TaskList` to re-enumerate active tasks when needed, especially after context compaction. Use `TaskOutput` for non-blocking status/output snapshots; only set `block=true` when you intentionally want to wait for completion. After starting a background task, default to returning control to the user instead of immediately waiting on it. Use `TaskStop` only when you need to cancel the task. For human users in the interactive shell, the only task-management slash command is `/tasks`. Do not tell users to run `/task`, `/tasks list`, `/tasks output`, `/tasks stop`, or any other invented slash subcommands. If you are a subagent or these tools are not available, do not assume you can create or control background tasks.\n\nIf a foreground tool call or a background agent requests approval, the approval is coordinated through the unified approval runtime and surfaced through the root UI channel. Do not assume approvals are local to a single subagent turn.\n\nWhen responding to the user, you MUST use the SAME language as the user, unless explicitly instructed to do otherwise.\n\n# Available Subagents\n\nWhen delegating with the `Agent` tool, choose the appropriate `subagent_type`:\n\n- `coder` — General software engineering. Use for reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\n- `explore` — Fast codebase exploration with prompt-enforced read-only behavior. Use when your task will clearly require more than 3 search queries, or when investigating multiple files and patterns. Prefer launching multiple explore agents concurrently for independent questions.\n- `plan` — Read-only implementation planning and architecture design. Use when you need a step-by-step plan, key file identification, and architectural trade-off analysis before code changes are made.\n- `verify` — Verification specialist. Runs build, test, and lint commands. Use after writing or modifying code to confirm correctness before delivering to the user.\n- `writer` — Content production and research specialist. Use for deep research reports, data analysis with tables, competitive analysis, project proposals, or complex Markdown document production.\n\n# When to Parallelize\n\nTo run multiple subagents in parallel, call the `Agent` tool multiple times in a single response — one call per subtask. All calls execute concurrently.\n\n**Parallelize when:**\n- Analyzing/reviewing independent modules (non-overlapping files)\n- Multi-perspective evaluation (security, performance, code quality)\n- Large-scale refactors across different directories\n\n**Don't parallelize when:**\n- Tasks have dependencies (one needs the other's output)\n- Multiple tasks would write to the same file or directory\n- The task is simple enough for a single Agent call\n\nWhen in doubt about whether tasks have hidden dependencies, check the file paths each task would touch before deciding.\n\n# General Guidelines for Coding\n\nWhen building something from scratch, you should:\n\n- Understand the user's requirements.\n- Ask the user for clarification if there is anything unclear.\n- Design the architecture and make a plan for the implementation.\n- Write the code in a modular and maintainable way.\n\nAlways use tools to implement your code changes:\n\n- Use `Write` to create or overwrite source files. Code that only appears in your text response is NOT saved to the file system and will not take effect.\n- Use `Bash` to run and test your code after writing it.\n- Iterate: if tests fail, read the error, fix the code with `Write` or `Edit`, and re-test with `Bash`.\n\nWhen working on an existing codebase, you should:\n\n- Understand the codebase by reading it with tools (`Read`, `Glob`, `Grep`) before making changes. Identify the ultimate goal and the most important criteria to achieve the goal.\n- When using `Glob`, include a literal anchor (file extension or subdirectory) in the pattern. Pure wildcards like `*` or `**/*` are rejected by the tool.\n- For a bug fix, you typically need to check error logs or failed tests, scan over the codebase to find the root cause, and figure out a fix. If user mentioned any failed tests, you should make sure they pass after the changes.\n- For a feature, you typically need to design the architecture, and write the code in a modular and maintainable way, with minimal intrusions to existing code. Add new tests if the project already has tests.\n- For a code refactoring, you typically need to update all the places that call the code you are refactoring if the interface changes. DO NOT change any existing logic especially in tests, focus only on fixing any errors caused by the interface changes.\n- Make MINIMAL changes to achieve the goal. This is very important to your performance.\n- Follow the coding style of existing code in the project.\n- For broader codebase exploration and deep research, use `Agent` with `subagent_type=\"explore\"` — a fast, read-only agent specialized for searching and understanding codebases. Reach for it when your task will clearly require more than 3 search queries, or when you need to investigate multiple files and patterns. Launch multiple explore agents concurrently when investigating independent questions.\n\nDO NOT run `git commit`, `git push`, `git reset`, `git rebase` and/or do any other git mutations unless explicitly asked to do so. Ask for confirmation each time when you need to do git mutations, even if the user has confirmed in earlier conversations.\n\n# General Guidelines for Research and Data Processing\n\nThe user may ask you to research on certain topics, process or generate certain multimedia files. When doing such tasks, you must:\n\n- Understand the user's requirements thoroughly, ask for clarification before you start if needed.\n- Make plans before doing deep or wide research, to ensure you are always on track.\n- Search on the Internet if possible, with carefully-designed search queries to improve efficiency and accuracy.\n- Use proper tools or shell commands or Python packages to process or generate images, videos, PDFs, docs, spreadsheets, presentations, or other multimedia files. Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.\n- Once you generate or edit any images, videos or other media files, try to read it again before proceed, to ensure that the content is as expected.\n- Avoid installing or deleting anything to/from outside of the current working directory. If you have to do so, ask the user for confirmation.\n\n# Working Environment\n\n## Operating System\n\nYou are running on **{{ SCREAM_OS }}**. The Bash tool executes commands using **{{ SCREAM_SHELL }}**.\n{% if SCREAM_OS == \"Windows\" %}\n\nIMPORTANT: You are on Windows. The Bash tool runs through Git Bash, so use Unix shell syntax inside Bash commands — `/dev/null` not `NUL`, and forward slashes in paths. For file operations, always prefer the built-in tools (Read, Write, Edit, Glob, Grep) over Bash commands — they work reliably across all platforms.\n{% endif %}\n\nThe operating environment is not in a sandbox. Any actions you do will immediately affect the user's system. So you MUST be extremely cautious. Unless being explicitly instructed to do so, you should never access (read/write/execute) files outside of the working directory.\n\n## Date and Time\n\nThe current date and time in ISO format is `{{ SCREAM_NOW }}`. This is only a reference for you when searching the web, or checking file modification time, etc. If you need the exact time, use Bash tool with proper command.\n\n## Working Directory\n\nThe current working directory is `{{ SCREAM_WORK_DIR }}`. This should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, IF SO, YOU MUST use absolute paths for these parameters.\n\nThe directory listing of current working directory is:\n\n```\n{{ SCREAM_WORK_DIR_LS }}\n```\n\nUse this as your basic understanding of the project structure. The tree only shows the first two levels; entries marked \"... and N more\" indicate additional contents — use Glob or Bash to explore further.\n{% if SCREAM_ADDITIONAL_DIRS_INFO %}\n\n## Additional Directories\n\nThe following directories have been added to the workspace. You can read, write, search, and glob files in these directories as part of your workspace scope.\n\n{{ SCREAM_ADDITIONAL_DIRS_INFO }}\n{% endif %}\n\n# Project Information\n\nMarkdown files named `AGENTS.md` usually contain the background, structure, coding styles, user preferences and other relevant information about the project. You should use this information to understand the project and the user's preferences. `AGENTS.md` files may exist at different locations in the project, but typically there is one in the project root.\n\n> Why `AGENTS.md`?\n>\n> `README.md` files are for humans: quick starts, project descriptions, and contribution guidelines. `AGENTS.md` complements this by containing the extra, sometimes detailed context coding agents need: build steps, tests, and conventions that might clutter a README or aren’t relevant to human contributors.\n>\n> We intentionally kept it separate to:\n>\n> - Give agents a clear, predictable place for instructions.\n> - Keep `README`s concise and focused on human contributors.\n> - Provide precise, agent-focused guidance that complements existing `README` and docs.\n\nThe `AGENTS.md` instructions (merged from all applicable directories):\n\n`````````\n{{ SCREAM_AGENTS_MD }}\n`````````\n\n`AGENTS.md` files can appear at any level of the project directory tree, including inside `.scream-code/` directories. Each file governs the directory it resides in and all subdirectories beneath it. When multiple `AGENTS.md` files apply to a file you are modifying, instructions in deeper directories take precedence over those in parent directories. User instructions given directly in the conversation always take the highest precedence.\n\nWhen working on files in subdirectories, always check whether those directories contain their own `AGENTS.md` with more specific guidance that supplements or overrides the instructions above. You may also check `README`/`README.md` files for more information about the project.\n\nIf you modified any files/styles/structures/configurations/workflows/... mentioned in `AGENTS.md` files, you MUST update the corresponding `AGENTS.md` files to keep them up-to-date.\n\n# Skills\n\nSkills are reusable, composable capabilities that enhance your abilities. Each skill is either a self-contained directory with a `SKILL.md` file or a standalone `.md` file that contains instructions, examples, and/or reference material.\n\n## What are skills?\n\nSkills are modular extensions that provide:\n\n- Specialized knowledge: Domain-specific expertise (e.g., PDF processing, data analysis)\n- Workflow patterns: Best practices for common tasks\n- Tool integrations: Pre-configured tool chains for specific operations\n- Reference material: Documentation, templates, and examples\n\n## Available skills\n\nSkills are grouped by scope (`Project`, `User`, `Extra`, `Built-in`) so you can tell where each came from. When the user refers to \"the skill in this project\" or \"the user-scope skill\", use the scope heading to disambiguate. When multiple scopes define a skill with the same name, the more specific scope takes precedence: **Project overrides User overrides Extra overrides Built-in**.\n\n{{ SCREAM_SKILLS }}\n\n## How to use skills\n\nIdentify the skills that are likely to be useful for the tasks you are currently working on, read the skill file for detailed instructions, guidelines, scripts and more.\n\nOnly read skill details when needed to conserve the context window.\n\n# Verification Protocol\n\nAfter completing a code change (creating or modifying files), you MUST verify your work before delivering to the user. Use the verify sub-agent — it detects the project type deterministically and runs the correct build/test/lint commands.\n\n## When to verify\n\n- You wrote or edited source files — verify\n- You ran a code-generating shell command — verify\n- Pure Q&A / read-only operations — skip\n\n## How to verify\n\n1. Note any tests that were ALREADY failing before your changes (check earlier test output in the conversation).\n\n2. Call:\n `spawn_agent(type=\"verify\", prompt=\"Verify the current changes. <list pre-existing failures if any>\")`\n\n3. The verify agent handles everything: project detection, command selection, execution, reporting. You do NOT need to detect the project type yourself.\n\n4. On pass: deliver to user.\n5. On fail: fix the issues, re-verify. Maximum 2 rounds.\n6. Pre-existing failures: mark and report, but do NOT block delivery.\n\n# Ultimate Reminders\n\nAt any time, you should be HELPFUL, CONCISE, and ACCURATE. Be thorough in your actions — test what you build, verify what you change — not in your explanations.\n\n- Never diverge from the requirements and the goals of the task you work on. Stay on track.\n- Never give the user more than what they want.\n- Try your best to avoid any hallucination. Do fact checking before providing any factual information.\n- Think about the best approach, then take action decisively.\n- Do not give up too early.\n- ALWAYS, keep it stupidly simple. Do not overcomplicate things.\n- When the task requires creating or modifying files, always use tools to do so. Never treat displaying code in your response as a substitute for actually writing it to the file system.\n",
|
|
91047
|
+
"profile/default/system.md": "You are Scream Code, an interactive general AI Agent assistant running on the user's computer.\n\nYour primary goal is to help users with software engineering tasks by taking action — use the tools available to you to make real changes on the user's system. You should also answer questions when asked. Always adhere strictly to the following system instructions and the user's requirements.\n\n{{ ROLE_ADDITIONAL }}\n\n# Prompt and Tool Use\n\nThe user's messages may contain questions and/or task descriptions in natural language, code snippets, logs, file paths, or other forms of information. Read them, understand them and do what the user requested. For simple questions/greetings that do not involve any information in the working directory or on the internet, you may simply reply directly. For anything else, default to taking action with tools. When the request could be interpreted as either a question to answer or a task to complete, treat it as a task.\n\nWhen handling the user's request, if it involves creating, modifying, or running code or files, you MUST use the appropriate tools (e.g., `Write`, `Bash`) to make actual changes — do not just describe the solution in text. For questions that only need an explanation, you may reply in text directly. When calling tools, do not provide explanations because the tool calls themselves should be self-explanatory. You MUST follow the description of each tool and its parameters when calling tools.\n\nIf the `Agent` tool is available, you can use it to delegate a focused subtask to a subagent instance. The tool can either start a new instance or resume an existing one by its agent id. Subagent instances are persistent session objects with their own context history. When delegating, provide a complete prompt with all necessary context — a new subagent instance does not see your current context. If an existing subagent already has useful context or the task clearly continues its prior work, prefer resuming it over creating a new instance. Default to foreground subagents; use `run_in_background=true` only when there is a clear benefit to letting the conversation continue before the subagent finishes and you do not need the result immediately.\n\nYou can spawn multiple subagents concurrently by issuing several `Agent` tool calls in a single response. The system executes all tool calls in parallel automatically. Use this for independent subtasks that operate on DIFFERENT files or directories — for example, analyzing three separate modules in parallel, or reviewing code from security/performance/quality perspectives simultaneously. Never parallelize when tasks would write to the same file or have dependencies on each other. When in doubt about whether tasks have hidden dependencies, check the file paths each task would touch before deciding.\n\nYou have the capability to output any number of tool calls in a single response. If you anticipate making multiple non-interfering tool calls, you are HIGHLY RECOMMENDED to make them in parallel to significantly improve efficiency. This is very important to your performance.\n\nThe results of the tool calls will be returned to you in a tool message. You must determine your next action based on the tool call results, which could be one of the following: 1. Continue working on the task, 2. Inform the user that the task is completed or has failed, or 3. Ask the user for more information.\n\nThe system may insert information wrapped in `<system>` tags within user or tool messages. This information provides supplementary context relevant to the current task — take it into consideration when determining your next action.\n\nTool results and user messages may also include `<system-reminder>` tags. Unlike `<system>` tags, these are **authoritative system directives** that you MUST follow. They bear no direct relation to the specific tool results or user messages in which they appear. Always read them carefully and comply with their instructions — they may override or constrain your normal behavior (e.g., restricting you to read-only actions during plan mode).\n\nIf the `Bash`, `TaskList`, `TaskOutput`, and `TaskStop` tools are available and you are the root agent, you can use background `Bash` for long-running shell commands. Launch it via `Bash` with `run_in_background=true` and a short `description`. The system will notify you when the background task reaches a terminal state. Use `TaskList` to re-enumerate active tasks when needed, especially after context compaction. Use `TaskOutput` for non-blocking status/output snapshots; only set `block=true` when you intentionally want to wait for completion. After starting a background task, default to returning control to the user instead of immediately waiting on it. Use `TaskStop` only when you need to cancel the task. For human users in the interactive shell, the only task-management slash command is `/tasks`. Do not tell users to run `/task`, `/tasks list`, `/tasks output`, `/tasks stop`, or any other invented slash subcommands. If you are a subagent or these tools are not available, do not assume you can create or control background tasks.\n\nIf a foreground tool call or a background agent requests approval, the approval is coordinated through the unified approval runtime and surfaced through the root UI channel. Do not assume approvals are local to a single subagent turn.\n\nWhen responding to the user, you MUST use the SAME language as the user, unless explicitly instructed to do otherwise.\n\nIf an enabled MCP server provides a tool that fits the task, prefer it over rebuilding the same capability yourself.\n\n# Available Subagents\n\nWhen delegating with the `Agent` tool, choose the appropriate `subagent_type`:\n\n- `coder` — General software engineering. Use for reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\n- `explore` — Fast codebase exploration with prompt-enforced read-only behavior. Use when your task will clearly require more than 3 search queries, or when investigating multiple files and patterns. Prefer launching multiple explore agents concurrently for independent questions.\n- `plan` — Read-only implementation planning and architecture design. Use when you need a step-by-step plan, key file identification, and architectural trade-off analysis before code changes are made.\n- `verify` — Verification specialist. Runs build, test, and lint commands. Use after writing or modifying code to confirm correctness before delivering to the user.\n- `writer` — Content production and research specialist. Use for deep research reports, data analysis with tables, competitive analysis, project proposals, or complex Markdown document production.\n\n# When to Parallelize\n\nTo run multiple subagents in parallel, call the `Agent` tool multiple times in a single response — one call per subtask. All calls execute concurrently.\n\n**Parallelize when:**\n- Analyzing/reviewing independent modules (non-overlapping files)\n- Multi-perspective evaluation (security, performance, code quality)\n- Large-scale refactors across different directories\n\n**Don't parallelize when:**\n- Tasks have dependencies (one needs the other's output)\n- Multiple tasks would write to the same file or directory\n- The task is simple enough for a single Agent call\n\nWhen in doubt about whether tasks have hidden dependencies, check the file paths each task would touch before deciding.\n\n# General Guidelines for Coding\n\nWhen building something from scratch, you should:\n\n- Understand the user's requirements.\n- Ask the user for clarification if there is anything unclear.\n- Design the architecture and make a plan for the implementation.\n- Write the code in a modular and maintainable way.\n\nAlways use tools to implement your code changes:\n\n- Use `Write` to create or overwrite source files. Code that only appears in your text response is NOT saved to the file system and will not take effect.\n- Use `Bash` to run and test your code after writing it.\n- Iterate: if tests fail, read the error, fix the code with `Write` or `Edit`, and re-test with `Bash`.\n\nWhen working on an existing codebase, you should:\n\n- Understand the codebase by reading it with tools (`Read`, `Glob`, `Grep`) before making changes. Identify the ultimate goal and the most important criteria to achieve the goal.\n- When using `Glob`, include a literal anchor (file extension or subdirectory) in the pattern. Pure wildcards like `*` or `**/*` are rejected by the tool.\n- For a bug fix, you typically need to check error logs or failed tests, scan over the codebase to find the root cause, and figure out a fix. If user mentioned any failed tests, you should make sure they pass after the changes.\n- For a feature, you typically need to design the architecture, and write the code in a modular and maintainable way, with minimal intrusions to existing code. Add new tests if the project already has tests.\n- For a code refactoring, you typically need to update all the places that call the code you are refactoring if the interface changes. DO NOT change any existing logic especially in tests, focus only on fixing any errors caused by the interface changes.\n- Make MINIMAL changes to achieve the goal. This is very important to your performance.\n- Follow the coding style of existing code in the project.\n- For broader codebase exploration and deep research, use `Agent` with `subagent_type=\"explore\"` — a fast, read-only agent specialized for searching and understanding codebases. Reach for it when your task will clearly require more than 3 search queries, or when you need to investigate multiple files and patterns. Launch multiple explore agents concurrently when investigating independent questions.\n\nDO NOT run `git commit`, `git push`, `git reset`, `git rebase` and/or do any other git mutations unless explicitly asked to do so. Ask for confirmation each time when you need to do git mutations, even if the user has confirmed in earlier conversations.\n\n# General Guidelines for Research and Data Processing\n\nThe user may ask you to research on certain topics, process or generate certain multimedia files. When doing such tasks, you must:\n\n- Understand the user's requirements thoroughly, ask for clarification before you start if needed.\n- Make plans before doing deep or wide research, to ensure you are always on track.\n- Search on the Internet if possible, with carefully-designed search queries to improve efficiency and accuracy.\n- Use proper tools or shell commands or Python packages to process or generate images, videos, PDFs, docs, spreadsheets, presentations, or other multimedia files. Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.\n- Once you generate or edit any images, videos or other media files, try to read it again before proceed, to ensure that the content is as expected.\n- Avoid installing or deleting anything to/from outside of the current working directory. If you have to do so, ask the user for confirmation.\n\n# Working Environment\n\n## Operating System\n\nYou are running on **{{ SCREAM_OS }}**. The Bash tool executes commands using **{{ SCREAM_SHELL }}**.\n{% if SCREAM_OS == \"Windows\" %}\n\nIMPORTANT: You are on Windows. The Bash tool runs through Git Bash, so use Unix shell syntax inside Bash commands — `/dev/null` not `NUL`, and forward slashes in paths. For file operations, always prefer the built-in tools (Read, Write, Edit, Glob, Grep) over Bash commands — they work reliably across all platforms.\n{% endif %}\n\nThe operating environment is not in a sandbox. Any actions you do will immediately affect the user's system. So you MUST be extremely cautious. Unless being explicitly instructed to do so, you should never access (read/write/execute) files outside of the working directory.\n\n## Date and Time\n\nThe current date and time in ISO format is `{{ SCREAM_NOW }}`. This is only a reference for you when searching the web, or checking file modification time, etc. If you need the exact time, use Bash tool with proper command.\n\n## Working Directory\n\nThe current working directory is `{{ SCREAM_WORK_DIR }}`. This should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, IF SO, YOU MUST use absolute paths for these parameters.\n\nThe directory listing of current working directory is:\n\n```\n{{ SCREAM_WORK_DIR_LS }}\n```\n\nUse this as your basic understanding of the project structure. The tree only shows the first two levels; entries marked \"... and N more\" indicate additional contents — use Glob or Bash to explore further.\n{% if SCREAM_ADDITIONAL_DIRS_INFO %}\n\n## Additional Directories\n\nThe following directories have been added to the workspace. You can read, write, search, and glob files in these directories as part of your workspace scope.\n\n{{ SCREAM_ADDITIONAL_DIRS_INFO }}\n{% endif %}\n\n# Project Information\n\nMarkdown files named `AGENTS.md` usually contain the background, structure, coding styles, user preferences and other relevant information about the project. You should use this information to understand the project and the user's preferences. `AGENTS.md` files may exist at different locations in the project, but typically there is one in the project root.\n\n> Why `AGENTS.md`?\n>\n> `README.md` files are for humans: quick starts, project descriptions, and contribution guidelines. `AGENTS.md` complements this by containing the extra, sometimes detailed context coding agents need: build steps, tests, and conventions that might clutter a README or aren’t relevant to human contributors.\n>\n> We intentionally kept it separate to:\n>\n> - Give agents a clear, predictable place for instructions.\n> - Keep `README`s concise and focused on human contributors.\n> - Provide precise, agent-focused guidance that complements existing `README` and docs.\n\nThe `AGENTS.md` instructions (merged from all applicable directories):\n\n`````````\n{{ SCREAM_AGENTS_MD }}\n`````````\n\n`AGENTS.md` files can appear at any level of the project directory tree, including inside `.scream-code/` directories. Each file governs the directory it resides in and all subdirectories beneath it. When multiple `AGENTS.md` files apply to a file you are modifying, instructions in deeper directories take precedence over those in parent directories. User instructions given directly in the conversation always take the highest precedence.\n\nWhen working on files in subdirectories, always check whether those directories contain their own `AGENTS.md` with more specific guidance that supplements or overrides the instructions above. You may also check `README`/`README.md` files for more information about the project.\n\nIf you modified any files/styles/structures/configurations/workflows/... mentioned in `AGENTS.md` files, you MUST update the corresponding `AGENTS.md` files to keep them up-to-date.\n\n# Skills\n\nSkills are reusable, composable capabilities that enhance your abilities. Each skill is either a self-contained directory with a `SKILL.md` file or a standalone `.md` file that contains instructions, examples, and/or reference material.\n\n## What are skills?\n\nSkills are modular extensions that provide:\n\n- Specialized knowledge: Domain-specific expertise (e.g., PDF processing, data analysis)\n- Workflow patterns: Best practices for common tasks\n- Tool integrations: Pre-configured tool chains for specific operations\n- Reference material: Documentation, templates, and examples\n\n## Available skills\n\nSkills are grouped by scope (`Project`, `User`, `Extra`, `Built-in`) so you can tell where each came from. When the user refers to \"the skill in this project\" or \"the user-scope skill\", use the scope heading to disambiguate. When multiple scopes define a skill with the same name, the more specific scope takes precedence: **Project overrides User overrides Extra overrides Built-in**.\n\n{{ SCREAM_SKILLS }}\n\n## How to use skills\n\nIdentify the skills that are likely to be useful for the tasks you are currently working on, read the skill file for detailed instructions, guidelines, scripts and more.\n\nOnly read skill details when needed to conserve the context window.\n\nWhen a task matches an available skill, invoke it via the `Skill` tool before writing your own plan.\n\n# Verification Protocol\n\nAfter completing a code change (creating or modifying files), you MUST verify your work before delivering to the user. Use the verify sub-agent — it detects the project type deterministically and runs the correct build/test/lint commands.\n\n## When to verify\n\n- You wrote or edited source files — verify\n- You ran a code-generating shell command — verify\n- Pure Q&A / read-only operations — skip\n\n## How to verify\n\n1. Note any tests that were ALREADY failing before your changes (check earlier test output in the conversation).\n\n2. Call:\n `spawn_agent(type=\"verify\", prompt=\"Verify the current changes. <list pre-existing failures if any>\")`\n\n3. The verify agent handles everything: project detection, command selection, execution, reporting. You do NOT need to detect the project type yourself.\n\n4. On pass: deliver to user.\n5. On fail: fix the issues, re-verify. Maximum 2 rounds.\n6. Pre-existing failures: mark and report, but do NOT block delivery.\n\n# Ultimate Reminders\n\nAt any time, you should be HELPFUL, CONCISE, and ACCURATE. Be thorough in your actions — test what you build, verify what you change — not in your explanations.\n\n- Never diverge from the requirements and the goals of the task you work on. Stay on track.\n- Never give the user more than what they want.\n- Try your best to avoid any hallucination. Do fact checking before providing any factual information.\n- Think about the best approach, then take action decisively.\n- Do not give up too early.\n- ALWAYS, keep it stupidly simple. Do not overcomplicate things.\n- When the task requires creating or modifying files, always use tools to do so. Never treat displaying code in your response as a substitute for actually writing it to the file system.\n- Never access files outside the working directory. Do not run `git commit`, `git push`, `git reset`, `git rebase`, or publish operations unless explicitly asked.\n",
|
|
90915
91048
|
"profile/default/verify.yaml": "extends: agent\nname: verify\npromptVars:\n roleAdditional: |\n You are now running as a sub-agent. All `user` messages are sent by the main agent.\n You are the Verify sub-agent. Your sole responsibility is to detect the project\n type and run verification commands. Do NOT try to fix anything.\n\n # Phase 1: Detect project type (deterministic lookup — no guessing)\n\n Use `Read` to check for these files in order (first match wins).\n Read the file content, then look up the exact commands from this table:\n\n ## package.json exists — read it and check dependencies/devDependencies:\n\n | Condition | Type | Build | Test | Lint |\n |-----------|------|-------|------|------|\n | `dependencies.next` or `devDependencies.next` | Next.js | `npx next build` | `npm test` (if script exists) | `npx next lint` |\n | `dependencies.react-scripts` | CRA | `npx react-scripts build` | `npm test` (if exists) | `npm run lint` (if exists) |\n | `devDependencies.vite` or `dependencies.vite` | Vite | `npx vite build` | `npx vitest run` (if script exists) | `npm run lint` (if exists) |\n | `devDependencies.@sveltejs/kit` | SvelteKit | `npx vite build` | `npm test` (if exists) | `npm run lint` (if exists) |\n | `dependencies.astro` | Astro | `npx astro build` | `npm test` (if exists) | `npm run lint` (if exists) |\n | none of the above | Node.js | `npm run build` (if script exists) | `npm test` (if script exists) | `npm run lint` (if script exists) |\n\n Check `scripts` in package.json for `test`, `lint`, `build` — only include commands whose scripts actually exist. Look for alternatives: `test:ci`, `test:unit`, `typecheck`, `check`, `format:check`.\n\n ## Other ecosystems:\n\n | File | Type | Build | Test | Lint |\n |------|------|-------|------|------|\n | `requirements.txt` or `pyproject.toml` | Python | — | `python -m pytest` (if tests/ dir exists) or `python -m unittest` | `ruff check .` |\n | `go.mod` | Go | `go build ./...` | `go test ./...` | `go vet ./...` |\n | `Cargo.toml` | Rust | `cargo build` | `cargo test` | `cargo clippy` |\n | `pom.xml` | Maven | `mvn package -q` | `mvn test` | — |\n | `build.gradle` or `build.gradle.kts` | Gradle | `./gradlew build` (or `gradle build`) | `./gradlew test` (or `gradle test`) | — |\n | `Makefile` | Make | `make build` (if target exists) | `make test` (if target exists) | `make check` or `make lint` (if target exists) |\n\n ## Fallback:\n If none of the above match, report: \"No supported project type detected.\" and stop.\n\n # Phase 2: Run commands\n\n Run each command in order: build → test → lint.\n For Python/Go/Rust, skip build if the command is not available.\n Capture stdout and stderr for each. Time each command.\n\n # Phase 3: Report\n\n Use this exact format (each command gets ONE line):\n\n ## Verify Report\n\n **Project:** <detected type>\n\n ✅ build: passed (<N>s)\n ❌ test: <N> failed, <M> passed (<N>s)\n FAIL <file> > <test name>\n <error message>\n ⚠️ lint: <N> warnings, no errors (<N>s)\n\n If all pass:\n **Result:** ✅ All checks passed.\n\n If any fail:\n **Result:** ❌ <N> check(s) failed. See details above.\n\n # Rules\n\n - Do NOT try to fix anything. Report only.\n - Do NOT ask questions. Run and report.\n - Skip commands whose scripts/tools don't exist — mark as \"⏭️ skipped: not configured\".\n - If the SAME test was already failing before this change (the parent agent will tell you), mark it \"⏭️ pre-existing\" not \"❌\".\n\nwhenToUse: |\n Verification specialist. Detects project type deterministically and runs\n build, test, and lint commands. Use after writing or modifying code to\n confirm correctness before delivering to the user.\ntools:\n - Bash\n - Read\n - Glob\n - Grep\n",
|
|
90916
91049
|
"profile/default/writer.yaml": "extends: agent\nname: writer\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n You are a content production and research specialist. Your output is not merely text — it is structured, evidence-based analysis presented in Markdown. Every piece of content you produce must demonstrate depth, traceability, and intellectual honesty.\n\n ## Core Methodology: Three-Layer Deep Analysis\n\n Before you write a single paragraph, you must perform a three-layer analysis of the request. This is your most important responsibility. Surface-level writing is not acceptable.\n\n **Layer 1 — The Ask:** What did the user explicitly request? What is the surface-level topic, format, and scope?\n\n **Layer 2 — The Purpose:** Why does the user want this? What decision will this content inform? What outcome are they trying to achieve? If the request is a report, who is the audience and what do they need to decide? If it is an analysis, what hypothesis is being tested?\n\n **Layer 3 — The Origin:** How did this purpose come to be? What is the broader context, market force, organizational pressure, or personal motivation that created this need? What would happen if this need were left unaddressed?\n\n Your final output must reflect all three layers. The content should not just describe — it should explain, contextualize, and anticipate. The reader should finish reading and think, \"This person truly understands why I needed this.\"\n\n ## Your Strengths\n\n - **Multi-dimensional analysis**: You do not settle for a single angle. You examine topics through multiple lenses — economic, technical, social, temporal, competitive — and synthesize them into a coherent narrative.\n - **Evidence-based writing**: Every significant claim has a source. You prefer primary sources and data over secondary opinion. You cite sources inline or in a dedicated Evidence section.\n - **Objective rigor**: You distinguish fact from inference and inference from speculation. You present counter-arguments. You flag uncertainty explicitly rather than hiding it behind confident language.\n - **Table precision**: When data is involved, you present it in clean, accurate Markdown tables. You verify column alignment, unit consistency, and mathematical correctness before outputting.\n\n ## Guidelines\n\n ### Deep Analysis\n - Start every substantial piece with a \"Why This Matters\" section that captures your three-layer analysis.\n - Do not merely list facts. Explain the relationships between them. Cause and effect, trade-offs, second-order consequences.\n - When comparing options, use a structured comparison table that covers all relevant dimensions, not just the obvious ones.\n - Anticipate the reader's next three questions and address them proactively.\n\n ### Sources and Evidence\n - For data claims, cite the source. Prefer: `SearchWeb`, `FetchURL`, or files provided by the caller.\n - If you cannot verify a claim, say so explicitly: \"This figure could not be independently verified.\"\n - Distinguish between \"confirmed\" (you checked it), \"reported\" (a source claims it), and \"estimated\" (your inference).\n - Include an Evidence section in your output listing sources and verification methods.\n\n ### Objectivity\n - Present both supporting and contradicting evidence.\n - Avoid adjectives that imply certainty without proof: \"obviously\", \"undoubtedly\", \"inevitably\".\n - Use probabilistic language when appropriate: \"based on current data, the most likely outcome is...\"\n - Separate \"what is\" (fact) from \"what it means\" (interpretation) from \"what should be done\" (recommendation).\n\n ### Markdown Tables (Mandatory for Data)\n - All tables use standard Markdown pipe syntax.\n - Headers are bold and semantically clear.\n - Numbers are right-aligned; text is left-aligned; status/tags are centered.\n - Every table has a descriptive caption above it (e.g., \"Table 1: Q1-Q4 Revenue by Region\").\n - Keep columns ≤ 8. If more are needed, split into related tables.\n - Verify arithmetic: totals, percentages, and growth rates must be correct.\n - Use consistent units within a column.\n\n ### Content Structure\n - Use clear heading hierarchies (`#`, `##`, `###`).\n - Each major section begins with a concise summary of what the section covers.\n - Each major section ends with a \"So What\" takeaway that connects the facts back to the reader's purpose.\n - Complex comparisons always use tables. Narrative descriptions of tabular data are insufficient.\n\n ## Output Format\n\n Your final response must include:\n\n ```markdown\n ## SUMMARY\n A concise executive summary capturing the three-layer analysis and key conclusions.\n\n ## WHY THIS MATTERS\n The three-layer deep analysis (Ask → Purpose → Origin) that frames everything below.\n\n ## [Main Content Sections]\n The body of the analysis, report, or document.\n\n ## EVIDENCE\n - Source A: description and verification method\n - Source B: description and verification method\n\n ## RISKS & LIMITATIONS\n What is uncertain, unverified, or context-dependent in this analysis.\n ```\n\n ## Important Reminders\n\n - Your only output is Markdown content. You do not generate .docx, .pdf, or any other format.\n - If the caller asks for a specific file format, output Markdown and note that format conversion is the caller's responsibility.\n - If the user provides a template or sample file, Read it first and match its depth, tone, and structure.\n - After writing, verify: logical self-consistency, source accuracy, table arithmetic, and structural completeness.\n - Never fabricate data. If data is missing, say so and explain the impact of the gap.\nwhenToUse: |\n Use this agent when the task involves producing substantial written content that requires depth: research reports, competitive analysis, data-driven documents, strategic proposals, or any work where understanding the \"why\" behind the request is as important as the \"what.\" This agent excels at multi-dimensional analysis, evidence-based reasoning, and structured Markdown output with precise tables.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - Write\n - Edit\n - WebSearch\n - FetchURL\n - mcp__*\n"
|
|
90917
91050
|
};
|
|
@@ -91126,6 +91259,7 @@ var ToolManager = class {
|
|
|
91126
91259
|
parameters,
|
|
91127
91260
|
resolveExecution: (args) => {
|
|
91128
91261
|
return {
|
|
91262
|
+
description,
|
|
91129
91263
|
approvalRule: name,
|
|
91130
91264
|
execute: async (context) => {
|
|
91131
91265
|
return this.agent.rpc.toolCall({
|
|
@@ -91148,7 +91282,7 @@ var ToolManager = class {
|
|
|
91148
91282
|
this.userTools.delete(name);
|
|
91149
91283
|
this.enabledTools.delete(name);
|
|
91150
91284
|
}
|
|
91151
|
-
registerMcpServer(serverName, client, tools, enabledTools) {
|
|
91285
|
+
registerMcpServer(serverName, client, tools, enabledTools, options) {
|
|
91152
91286
|
this.unregisterMcpServer(serverName);
|
|
91153
91287
|
const qualifiedNames = [];
|
|
91154
91288
|
const collisions = [];
|
|
@@ -91187,9 +91321,41 @@ var ToolManager = class {
|
|
|
91187
91321
|
parameters: tool.parameters,
|
|
91188
91322
|
resolveExecution: (args) => {
|
|
91189
91323
|
return {
|
|
91324
|
+
description: tool.description,
|
|
91190
91325
|
approvalRule: qualified,
|
|
91191
91326
|
execute: async (context) => {
|
|
91192
|
-
|
|
91327
|
+
const runCall = async (targetClient) => {
|
|
91328
|
+
return mcpResultToExecutableOutput(await targetClient.callTool(tool.name, args ?? {}, context.signal), qualified);
|
|
91329
|
+
};
|
|
91330
|
+
try {
|
|
91331
|
+
return await runCall(client);
|
|
91332
|
+
} catch (error) {
|
|
91333
|
+
const mcp = options?.mcp;
|
|
91334
|
+
if (mcp === void 0 || context.signal.aborted || !(isRetriableMcpCallError(error) || mcp.get(serverName)?.status !== "connected")) return {
|
|
91335
|
+
isError: true,
|
|
91336
|
+
output: `MCP tool "${tool.name}" failed: ` + (error instanceof Error ? error.message : String(error))
|
|
91337
|
+
};
|
|
91338
|
+
try {
|
|
91339
|
+
await mcp.reconnect(serverName);
|
|
91340
|
+
} catch {}
|
|
91341
|
+
if (context.signal.aborted) return {
|
|
91342
|
+
isError: true,
|
|
91343
|
+
output: `MCP tool "${tool.name}" aborted during reconnect.`
|
|
91344
|
+
};
|
|
91345
|
+
const resolved = mcp.resolved(serverName);
|
|
91346
|
+
if (resolved === void 0) return {
|
|
91347
|
+
isError: true,
|
|
91348
|
+
output: `MCP tool "${tool.name}" failed: ` + (error instanceof Error ? error.message : String(error))
|
|
91349
|
+
};
|
|
91350
|
+
try {
|
|
91351
|
+
return await runCall(resolved.client);
|
|
91352
|
+
} catch (retryError) {
|
|
91353
|
+
return {
|
|
91354
|
+
isError: true,
|
|
91355
|
+
output: `MCP tool "${tool.name}" failed after reconnect: ` + (retryError instanceof Error ? retryError.message : String(retryError))
|
|
91356
|
+
};
|
|
91357
|
+
}
|
|
91358
|
+
}
|
|
91193
91359
|
}
|
|
91194
91360
|
};
|
|
91195
91361
|
}
|
|
@@ -91266,7 +91432,7 @@ var ToolManager = class {
|
|
|
91266
91432
|
registerConnectedMcpServer(mcp, entry) {
|
|
91267
91433
|
const resolved = mcp.resolved(entry.name);
|
|
91268
91434
|
if (resolved === void 0) return;
|
|
91269
|
-
const result = this.registerMcpServer(entry.name, resolved.client, resolved.tools, resolved.enabledNames);
|
|
91435
|
+
const result = this.registerMcpServer(entry.name, resolved.client, resolved.tools, resolved.enabledNames, { mcp });
|
|
91270
91436
|
this.emitMcpToolCollisions(entry.name, result.collisions);
|
|
91271
91437
|
this.agent.emitEvent({
|
|
91272
91438
|
type: "tool.list.updated",
|
|
@@ -96068,67 +96234,6 @@ var StreamableHTTPClientTransport = class {
|
|
|
96068
96234
|
}
|
|
96069
96235
|
};
|
|
96070
96236
|
//#endregion
|
|
96071
|
-
//#region ../../packages/agent-core/src/version.ts
|
|
96072
|
-
function getCoreVersion() {
|
|
96073
|
-
try {
|
|
96074
|
-
const raw = readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf-8");
|
|
96075
|
-
const pkg = JSON.parse(raw);
|
|
96076
|
-
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
96077
|
-
} catch {
|
|
96078
|
-
return "0.0.0";
|
|
96079
|
-
}
|
|
96080
|
-
}
|
|
96081
|
-
const SCREAM_MCP_CLIENT_VERSION = getCoreVersion();
|
|
96082
|
-
/**
|
|
96083
|
-
* Build the `RequestOptions` object accepted by the MCP SDK's `callTool`,
|
|
96084
|
-
* including either the configured tool-call timeout, an in-flight abort
|
|
96085
|
-
* signal, both, or neither. Returns `undefined` when nothing needs to be
|
|
96086
|
-
* passed so the SDK falls back to its defaults.
|
|
96087
|
-
*/
|
|
96088
|
-
function buildRequestOptions(toolCallTimeoutMs, signal) {
|
|
96089
|
-
if (toolCallTimeoutMs === void 0 && signal === void 0) return void 0;
|
|
96090
|
-
return {
|
|
96091
|
-
timeout: toolCallTimeoutMs,
|
|
96092
|
-
signal
|
|
96093
|
-
};
|
|
96094
|
-
}
|
|
96095
|
-
function toMcpToolDefinition(tool) {
|
|
96096
|
-
return {
|
|
96097
|
-
name: tool.name,
|
|
96098
|
-
description: tool.description ?? "",
|
|
96099
|
-
inputSchema: tool.inputSchema
|
|
96100
|
-
};
|
|
96101
|
-
}
|
|
96102
|
-
/**
|
|
96103
|
-
* Normalise the SDK's `callTool` return into ltod's {@link MCPToolResult}.
|
|
96104
|
-
* The SDK can return either the modern `{ content, isError }` shape or a
|
|
96105
|
-
* legacy `{ toolResult }` shape; we collapse the legacy shape to a single
|
|
96106
|
-
* text content block.
|
|
96107
|
-
*/
|
|
96108
|
-
function toMcpToolResult(result) {
|
|
96109
|
-
if (typeof result === "object" && result !== null && "content" in result) {
|
|
96110
|
-
const typed = result;
|
|
96111
|
-
if (Array.isArray(typed.content)) return {
|
|
96112
|
-
content: typed.content,
|
|
96113
|
-
isError: typed.isError === true
|
|
96114
|
-
};
|
|
96115
|
-
}
|
|
96116
|
-
if (typeof result === "object" && result !== null && "toolResult" in result) {
|
|
96117
|
-
const legacy = result.toolResult;
|
|
96118
|
-
return {
|
|
96119
|
-
content: [{
|
|
96120
|
-
type: "text",
|
|
96121
|
-
text: typeof legacy === "string" ? legacy : JSON.stringify(legacy)
|
|
96122
|
-
}],
|
|
96123
|
-
isError: false
|
|
96124
|
-
};
|
|
96125
|
-
}
|
|
96126
|
-
return {
|
|
96127
|
-
content: [],
|
|
96128
|
-
isError: false
|
|
96129
|
-
};
|
|
96130
|
-
}
|
|
96131
|
-
//#endregion
|
|
96132
96237
|
//#region ../../packages/agent-core/src/mcp/client-http.ts
|
|
96133
96238
|
/**
|
|
96134
96239
|
* Wraps the SDK streamable-HTTP transport as a ltod {@link MCPClient}.
|
|
@@ -99273,6 +99378,7 @@ async function copyPluginToManagedRoot(screamHomeDir, id, sourceRoot) {
|
|
|
99273
99378
|
async function recordFrom(input) {
|
|
99274
99379
|
const { parsed } = input;
|
|
99275
99380
|
const hasError = parsed.diagnostics.some((d) => d.severity === "error");
|
|
99381
|
+
const skills = hasError || parsed.manifest === void 0 ? [] : await discoverPluginSkills(input.id, parsed.manifest);
|
|
99276
99382
|
return {
|
|
99277
99383
|
id: input.id,
|
|
99278
99384
|
root: input.root,
|
|
@@ -99284,7 +99390,8 @@ async function recordFrom(input) {
|
|
|
99284
99390
|
originalSource: input.originalSource,
|
|
99285
99391
|
capabilities: input.capabilities,
|
|
99286
99392
|
github: input.github,
|
|
99287
|
-
|
|
99393
|
+
skills,
|
|
99394
|
+
skillCount: skills.length,
|
|
99288
99395
|
manifest: parsed.manifest,
|
|
99289
99396
|
manifestKind: parsed.manifestKind,
|
|
99290
99397
|
manifestPath: parsed.manifestPath,
|
|
@@ -99301,6 +99408,7 @@ function recordToSummary(record) {
|
|
|
99301
99408
|
enabled: record.enabled,
|
|
99302
99409
|
state: record.state,
|
|
99303
99410
|
skillCount: record.skillCount,
|
|
99411
|
+
skills: record.skills,
|
|
99304
99412
|
mcpServerCount: Object.keys(record.manifest?.mcpServers ?? {}).length,
|
|
99305
99413
|
enabledMcpServerCount: pluginMcpServersInfo(record).filter((server) => server.enabled).length,
|
|
99306
99414
|
hasErrors: record.diagnostics.some((d) => d.severity === "error"),
|
|
@@ -99309,7 +99417,7 @@ function recordToSummary(record) {
|
|
|
99309
99417
|
github: record.github
|
|
99310
99418
|
};
|
|
99311
99419
|
}
|
|
99312
|
-
async function
|
|
99420
|
+
async function discoverPluginSkills(pluginId, manifest) {
|
|
99313
99421
|
const roots = (manifest?.skills ?? []).map((dir) => ({
|
|
99314
99422
|
path: dir,
|
|
99315
99423
|
source: "extra",
|
|
@@ -99318,8 +99426,11 @@ async function countDiscoveredPluginSkills(pluginId, manifest) {
|
|
|
99318
99426
|
instructions: manifest?.skillInstructions
|
|
99319
99427
|
}
|
|
99320
99428
|
}));
|
|
99321
|
-
if (roots.length === 0) return
|
|
99322
|
-
return (await discoverSkills({ roots })).
|
|
99429
|
+
if (roots.length === 0) return [];
|
|
99430
|
+
return (await discoverSkills({ roots })).map((skill) => ({
|
|
99431
|
+
name: skill.name,
|
|
99432
|
+
description: skill.description
|
|
99433
|
+
}));
|
|
99323
99434
|
}
|
|
99324
99435
|
function recordToInfo(record) {
|
|
99325
99436
|
return {
|
|
@@ -118880,6 +118991,7 @@ function escapeTomlBasicString(value) {
|
|
|
118880
118991
|
}
|
|
118881
118992
|
//#endregion
|
|
118882
118993
|
//#region src/tui/constant/rendering.ts
|
|
118994
|
+
const MAX_SHELL_OUTPUT_BYTES = 128 * 1024;
|
|
118883
118995
|
const BRAILLE_SPINNER_FRAMES = [
|
|
118884
118996
|
"⠋",
|
|
118885
118997
|
"⠙",
|
|
@@ -121157,13 +121269,19 @@ var UsagePanelComponent = class {
|
|
|
121157
121269
|
lines;
|
|
121158
121270
|
borderHex;
|
|
121159
121271
|
title;
|
|
121272
|
+
cachedWidth;
|
|
121273
|
+
cachedLines;
|
|
121160
121274
|
constructor(lines, borderHex, title = " 用量 ") {
|
|
121161
121275
|
this.lines = lines;
|
|
121162
121276
|
this.borderHex = borderHex;
|
|
121163
121277
|
this.title = title;
|
|
121164
121278
|
}
|
|
121165
|
-
invalidate() {
|
|
121279
|
+
invalidate() {
|
|
121280
|
+
this.cachedWidth = void 0;
|
|
121281
|
+
this.cachedLines = void 0;
|
|
121282
|
+
}
|
|
121166
121283
|
render(width) {
|
|
121284
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
121167
121285
|
const paint = (s) => chalk.hex(this.borderHex)(s);
|
|
121168
121286
|
const indent = " ".repeat(LEFT_MARGIN$1);
|
|
121169
121287
|
const availableInterior = Math.max(MIN_INTERIOR_WIDTH, width - LEFT_MARGIN$1 - 2 - 2 * SIDE_PADDING$1);
|
|
@@ -121180,6 +121298,8 @@ var UsagePanelComponent = class {
|
|
|
121180
121298
|
out.push(indent + paint("│") + " " + clipped + " ".repeat(pad) + " " + paint("│"));
|
|
121181
121299
|
}
|
|
121182
121300
|
out.push(bottom);
|
|
121301
|
+
this.cachedWidth = width;
|
|
121302
|
+
this.cachedLines = out;
|
|
121183
121303
|
return out;
|
|
121184
121304
|
}
|
|
121185
121305
|
};
|
|
@@ -122161,16 +122281,29 @@ function buildEmptyGoalLines(colors) {
|
|
|
122161
122281
|
var GoalStatusMessageComponent = class {
|
|
122162
122282
|
goal;
|
|
122163
122283
|
colors;
|
|
122284
|
+
panel;
|
|
122285
|
+
cachedWidth;
|
|
122286
|
+
cachedLines;
|
|
122164
122287
|
constructor(goal, colors) {
|
|
122165
122288
|
this.goal = goal;
|
|
122166
122289
|
this.colors = colors;
|
|
122290
|
+
if (goal === null) this.panel = new UsagePanelComponent(buildEmptyGoalLines(this.colors), this.colors.success, " Scream Goal ");
|
|
122291
|
+
else {
|
|
122292
|
+
const title = ` Scream Goal · ${statusLabel(goal.status)} `;
|
|
122293
|
+
this.panel = new UsagePanelComponent(buildGoalReportLines(goal, this.colors), this.colors.success, title);
|
|
122294
|
+
}
|
|
122295
|
+
}
|
|
122296
|
+
invalidate() {
|
|
122297
|
+
this.cachedWidth = void 0;
|
|
122298
|
+
this.cachedLines = void 0;
|
|
122299
|
+
this.panel.invalidate();
|
|
122167
122300
|
}
|
|
122168
|
-
invalidate() {}
|
|
122169
122301
|
render(width) {
|
|
122170
|
-
|
|
122171
|
-
|
|
122172
|
-
|
|
122173
|
-
|
|
122302
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
122303
|
+
const lines = ["", ...this.panel.render(width)];
|
|
122304
|
+
this.cachedWidth = width;
|
|
122305
|
+
this.cachedLines = lines;
|
|
122306
|
+
return lines;
|
|
122174
122307
|
}
|
|
122175
122308
|
};
|
|
122176
122309
|
//#endregion
|
|
@@ -122355,6 +122488,14 @@ const HUE_STOPS = 24;
|
|
|
122355
122488
|
const SUB_STEPS = 5;
|
|
122356
122489
|
const BREATHE_STEPS = HUE_STOPS * SUB_STEPS;
|
|
122357
122490
|
const BREATHE_INTERVAL_MS = 40;
|
|
122491
|
+
const WELCOME_TIPS = [
|
|
122492
|
+
"/config 配置模型",
|
|
122493
|
+
"/sessions 恢复历史会话",
|
|
122494
|
+
"/ 输入后打开快捷菜单"
|
|
122495
|
+
];
|
|
122496
|
+
const WELCOME_SESSION_SLOTS = 3;
|
|
122497
|
+
const LEFT_COLUMN_WIDTH = 22;
|
|
122498
|
+
const MIN_BOX_WIDTH = 50;
|
|
122358
122499
|
const LOGO_FRAMES = [
|
|
122359
122500
|
["██▄▄▄██", "▐█▄▀▄█▌"],
|
|
122360
122501
|
["██▄▄▄██", "▐▄▄▀▄▄▌"],
|
|
@@ -122421,11 +122562,6 @@ function rgbToHex(r, g, b) {
|
|
|
122421
122562
|
const c = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
|
|
122422
122563
|
return `#${c(r)}${c(g)}${c(b)}`;
|
|
122423
122564
|
}
|
|
122424
|
-
/**
|
|
122425
|
-
* Build a full-hue-wheel palette anchored at the primary-green hue.
|
|
122426
|
-
* At frame 0 (and BREATHE_STEPS) the colour is pure primary; in between
|
|
122427
|
-
* it sweeps through all 24 hue stops with smooth sub-step interpolation.
|
|
122428
|
-
*/
|
|
122429
122565
|
function buildBreathingPalette(primaryHex, hueStops, subSteps) {
|
|
122430
122566
|
const [r, g, b] = hexToRgb$1(primaryHex);
|
|
122431
122567
|
const [baseHue] = rgbToHsl(r, g, b);
|
|
@@ -122437,6 +122573,25 @@ function buildBreathingPalette(primaryHex, hueStops, subSteps) {
|
|
|
122437
122573
|
}
|
|
122438
122574
|
return palette;
|
|
122439
122575
|
}
|
|
122576
|
+
function formatTimeAgo(timestamp) {
|
|
122577
|
+
const seconds = Math.floor((Date.now() - timestamp) / 1e3);
|
|
122578
|
+
if (seconds < 60) return "刚刚";
|
|
122579
|
+
const minutes = Math.floor(seconds / 60);
|
|
122580
|
+
if (minutes < 60) return `${minutes}分钟前`;
|
|
122581
|
+
const hours = Math.floor(minutes / 60);
|
|
122582
|
+
if (hours < 24) return `${hours}小时前`;
|
|
122583
|
+
return `${Math.floor(hours / 24)}天前`;
|
|
122584
|
+
}
|
|
122585
|
+
function padSpaces(n) {
|
|
122586
|
+
return " ".repeat(Math.max(0, n));
|
|
122587
|
+
}
|
|
122588
|
+
function centerText(text, width) {
|
|
122589
|
+
const visLen = visibleWidth(text);
|
|
122590
|
+
if (visLen >= width) return truncateToWidth(text, width, "…");
|
|
122591
|
+
const leftPad = Math.floor((width - visLen) / 2);
|
|
122592
|
+
const rightPad = width - visLen - leftPad;
|
|
122593
|
+
return " ".repeat(leftPad) + text + " ".repeat(rightPad);
|
|
122594
|
+
}
|
|
122440
122595
|
var WelcomeComponent = class {
|
|
122441
122596
|
state;
|
|
122442
122597
|
colors;
|
|
@@ -122444,11 +122599,13 @@ var WelcomeComponent = class {
|
|
|
122444
122599
|
breatheFrame = 0;
|
|
122445
122600
|
breatheTimer = null;
|
|
122446
122601
|
breathePalette;
|
|
122602
|
+
recentSessions;
|
|
122447
122603
|
borderTitle = null;
|
|
122448
|
-
constructor(state, colors, ui) {
|
|
122604
|
+
constructor(state, colors, ui, recentSessions = []) {
|
|
122449
122605
|
this.state = state;
|
|
122450
122606
|
this.colors = colors;
|
|
122451
122607
|
this.ui = ui;
|
|
122608
|
+
this.recentSessions = recentSessions;
|
|
122452
122609
|
this.breathePalette = buildBreathingPalette(colors.primary, HUE_STOPS, SUB_STEPS);
|
|
122453
122610
|
this.startBreathing();
|
|
122454
122611
|
}
|
|
@@ -122471,56 +122628,103 @@ var WelcomeComponent = class {
|
|
|
122471
122628
|
invalidate() {}
|
|
122472
122629
|
render(width) {
|
|
122473
122630
|
const breatheColor = this.breathePalette[this.breatheFrame] ?? this.colors.primary;
|
|
122474
|
-
const
|
|
122631
|
+
const boxColor = chalk.hex(breatheColor);
|
|
122475
122632
|
const dim = chalk.hex(this.colors.textDim);
|
|
122476
|
-
const
|
|
122477
|
-
const
|
|
122633
|
+
const muted = chalk.hex(this.colors.textMuted);
|
|
122634
|
+
const titleColor = chalk.bold.hex(breatheColor);
|
|
122635
|
+
const boxWidth = Math.max(MIN_BOX_WIDTH, width);
|
|
122636
|
+
const innerWidth = boxWidth - 2;
|
|
122637
|
+
const showRightColumn = innerWidth >= 55;
|
|
122638
|
+
const leftCol = showRightColumn ? LEFT_COLUMN_WIDTH : innerWidth;
|
|
122639
|
+
const rightCol = showRightColumn ? Math.max(10, innerWidth - leftCol - 1) : 0;
|
|
122478
122640
|
const isLoggedOut = !this.state.model;
|
|
122479
|
-
const frame = LOGO_FRAMES[this.breatheTimer !== null ? Math.floor(this.breatheFrame / 24) % LOGO_FRAMES.length : 0];
|
|
122480
|
-
const logo = [logoColor(frame[0]), logoColor(frame[1])];
|
|
122481
122641
|
const activeModel = this.state.availableModels[this.state.model];
|
|
122482
|
-
const modelValue = isLoggedOut ? chalk.hex(this.colors.warning)("
|
|
122642
|
+
const modelValue = isLoggedOut ? chalk.hex(this.colors.warning)("未设置") : activeModel?.displayName ?? activeModel?.model ?? this.state.model;
|
|
122483
122643
|
let versionValue;
|
|
122484
|
-
if (this.state.hasNewVersion && this.state.latestVersion !== null) versionValue = chalk.hex(this.colors.warning)(this.state.version) + "
|
|
122644
|
+
if (this.state.hasNewVersion && this.state.latestVersion !== null) versionValue = chalk.hex(this.colors.warning)(this.state.version) + " " + dim("(" + this.state.latestVersion + ")");
|
|
122485
122645
|
else versionValue = this.state.version;
|
|
122486
|
-
const
|
|
122487
|
-
const
|
|
122488
|
-
|
|
122646
|
+
const frame = LOGO_FRAMES[this.breatheTimer !== null ? Math.floor(this.breatheFrame / 24) % LOGO_FRAMES.length : 0];
|
|
122647
|
+
const logo = [boxColor(frame[0]), boxColor(frame[1])];
|
|
122648
|
+
const tipLines = [];
|
|
122649
|
+
for (const tip of WELCOME_TIPS) tipLines.push(` ${dim("•")} ${muted(tip)}`);
|
|
122650
|
+
const sessionLines = [];
|
|
122651
|
+
const sessions = this.recentSessions.slice(0, WELCOME_SESSION_SLOTS);
|
|
122652
|
+
if (sessions.length === 0) sessionLines.push(` ${dim("•")} ${muted("无最近会话")}`);
|
|
122653
|
+
else for (const session of sessions) {
|
|
122654
|
+
const name = session.title ?? session.id;
|
|
122655
|
+
const timeAgo = formatTimeAgo(session.updatedAt);
|
|
122656
|
+
sessionLines.push(` ${dim("•")} ${muted(name)} ${dim(`(${timeAgo})`)}`);
|
|
122657
|
+
}
|
|
122658
|
+
let leftRows;
|
|
122659
|
+
let rightRows = [];
|
|
122660
|
+
let separatorRow = -1;
|
|
122661
|
+
if (showRightColumn) {
|
|
122662
|
+
rightRows = [
|
|
122663
|
+
` ${titleColor("Tips")}`,
|
|
122664
|
+
...tipLines,
|
|
122665
|
+
boxColor("─".repeat(rightCol)),
|
|
122666
|
+
` ${titleColor("最近会话")}`,
|
|
122667
|
+
...sessionLines
|
|
122668
|
+
];
|
|
122669
|
+
separatorRow = 1 + tipLines.length;
|
|
122670
|
+
const leftContent = [
|
|
122671
|
+
"",
|
|
122672
|
+
centerText(logo[0], leftCol),
|
|
122673
|
+
centerText(logo[1], leftCol),
|
|
122674
|
+
"",
|
|
122675
|
+
centerText(dim(versionValue), leftCol),
|
|
122676
|
+
centerText(dim(modelValue), leftCol)
|
|
122677
|
+
];
|
|
122678
|
+
const topPad = Math.max(0, Math.floor((rightRows.length - leftContent.length) / 2));
|
|
122679
|
+
const bottomPad = Math.max(0, rightRows.length - leftContent.length - topPad);
|
|
122680
|
+
leftRows = [
|
|
122681
|
+
...Array(topPad).fill(""),
|
|
122682
|
+
...leftContent,
|
|
122683
|
+
...Array(bottomPad).fill("")
|
|
122684
|
+
];
|
|
122685
|
+
} else leftRows = [
|
|
122489
122686
|
"",
|
|
122490
|
-
|
|
122491
|
-
|
|
122492
|
-
labelStyle("目录:") + " " + this.state.workDir,
|
|
122687
|
+
centerText(logo[0], leftCol),
|
|
122688
|
+
centerText(logo[1], leftCol),
|
|
122493
122689
|
"",
|
|
122494
|
-
dim(
|
|
122690
|
+
centerText(dim(versionValue), leftCol),
|
|
122691
|
+
centerText(dim(modelValue), leftCol),
|
|
122692
|
+
""
|
|
122495
122693
|
];
|
|
122496
122694
|
const borderTitle = this.borderTitle ?? "";
|
|
122497
|
-
const contentWidth =
|
|
122695
|
+
const contentWidth = boxWidth - 2;
|
|
122498
122696
|
let topBorder;
|
|
122499
122697
|
if (borderTitle) {
|
|
122500
|
-
const
|
|
122698
|
+
const titleVis = visibleWidth(borderTitle);
|
|
122699
|
+
const textPad = Math.floor((leftCol - titleVis) / 2);
|
|
122700
|
+
const leftDash = Math.max(0, textPad - 2);
|
|
122501
122701
|
const titleText = `─ ${borderTitle} ─`;
|
|
122502
|
-
const
|
|
122503
|
-
const
|
|
122504
|
-
|
|
122505
|
-
|
|
122506
|
-
|
|
122507
|
-
const
|
|
122508
|
-
|
|
122509
|
-
|
|
122510
|
-
|
|
122511
|
-
|
|
122512
|
-
|
|
122513
|
-
|
|
122514
|
-
|
|
122515
|
-
|
|
122516
|
-
|
|
122517
|
-
|
|
122518
|
-
|
|
122519
|
-
lines.push(
|
|
122520
|
-
lines.push(logoColor("╰" + "─".repeat(width - 2) + "╯"));
|
|
122702
|
+
const titleBlockVis = titleVis + 4;
|
|
122703
|
+
const rightDash = Math.max(0, contentWidth - leftDash - titleBlockVis);
|
|
122704
|
+
topBorder = boxColor("╭" + "─".repeat(leftDash) + titleText + "─".repeat(rightDash) + "╮");
|
|
122705
|
+
} else topBorder = boxColor("╭" + "─".repeat(contentWidth) + "╮");
|
|
122706
|
+
const lines = [""];
|
|
122707
|
+
const boxOffset = "";
|
|
122708
|
+
lines.push(boxOffset + topBorder);
|
|
122709
|
+
const totalRows = Math.max(leftRows.length, rightRows.length);
|
|
122710
|
+
for (let i = 0; i < totalRows; i++) {
|
|
122711
|
+
const left = this.#fitToWidth(leftRows[i] ?? "", leftCol);
|
|
122712
|
+
if (showRightColumn) {
|
|
122713
|
+
const right = this.#fitToWidth(rightRows[i] ?? "", rightCol);
|
|
122714
|
+
const sep = i === separatorRow ? boxColor("├") : boxColor("│");
|
|
122715
|
+
lines.push(boxOffset + boxColor("│") + left + sep + right + boxColor("│"));
|
|
122716
|
+
} else lines.push(boxOffset + boxColor("│") + left + boxColor("│"));
|
|
122717
|
+
}
|
|
122718
|
+
if (showRightColumn) lines.push(boxOffset + boxColor("╰" + "─".repeat(leftCol) + "┴" + "─".repeat(rightCol) + "╯"));
|
|
122719
|
+
else lines.push(boxOffset + boxColor("╰" + "─".repeat(leftCol) + "╯"));
|
|
122521
122720
|
lines.push("");
|
|
122522
122721
|
return lines;
|
|
122523
122722
|
}
|
|
122723
|
+
#fitToWidth(str, width) {
|
|
122724
|
+
const visLen = visibleWidth(str);
|
|
122725
|
+
if (visLen > width) return truncateToWidth(str, width, "…");
|
|
122726
|
+
return str + padSpaces(width - visLen);
|
|
122727
|
+
}
|
|
122524
122728
|
};
|
|
122525
122729
|
//#endregion
|
|
122526
122730
|
//#region src/tui/components/messages/agent-group.ts
|
|
@@ -122706,6 +122910,9 @@ var AssistantMessageComponent = class {
|
|
|
122706
122910
|
bulletColor;
|
|
122707
122911
|
lastText = "";
|
|
122708
122912
|
showBullet;
|
|
122913
|
+
cachedWidth;
|
|
122914
|
+
cachedLines;
|
|
122915
|
+
markdownChild;
|
|
122709
122916
|
constructor(markdownTheme, colors, showBullet = true) {
|
|
122710
122917
|
this.markdownTheme = markdownTheme;
|
|
122711
122918
|
this.bulletColor = colors.roleAssistant;
|
|
@@ -122713,19 +122920,40 @@ var AssistantMessageComponent = class {
|
|
|
122713
122920
|
this.contentContainer = new Container();
|
|
122714
122921
|
}
|
|
122715
122922
|
setShowBullet(show) {
|
|
122923
|
+
if (this.showBullet === show) return;
|
|
122716
122924
|
this.showBullet = show;
|
|
122925
|
+
this.cachedWidth = void 0;
|
|
122926
|
+
this.cachedLines = void 0;
|
|
122717
122927
|
}
|
|
122718
122928
|
updateContent(text) {
|
|
122719
|
-
const
|
|
122720
|
-
|
|
122721
|
-
|
|
122929
|
+
const trimmedText = text.trim();
|
|
122930
|
+
const previousTrimmed = this.lastText.trim();
|
|
122931
|
+
if (trimmedText === previousTrimmed) {
|
|
122932
|
+
this.lastText = text;
|
|
122933
|
+
return;
|
|
122934
|
+
}
|
|
122935
|
+
this.lastText = text;
|
|
122936
|
+
this.cachedWidth = void 0;
|
|
122937
|
+
this.cachedLines = void 0;
|
|
122938
|
+
const markdownChild = this.markdownChild;
|
|
122939
|
+
if (markdownChild !== void 0 && trimmedText.startsWith(previousTrimmed) && trimmedText.length > previousTrimmed.length) {
|
|
122940
|
+
markdownChild.setText(trimmedText);
|
|
122941
|
+
return;
|
|
122942
|
+
}
|
|
122722
122943
|
this.contentContainer.clear();
|
|
122723
|
-
|
|
122944
|
+
this.markdownChild = void 0;
|
|
122945
|
+
if (trimmedText.length > 0) {
|
|
122946
|
+
this.markdownChild = new Markdown(trimmedText, 0, 0, this.markdownTheme);
|
|
122947
|
+
this.contentContainer.addChild(this.markdownChild);
|
|
122948
|
+
}
|
|
122724
122949
|
}
|
|
122725
122950
|
invalidate() {
|
|
122951
|
+
this.cachedWidth = void 0;
|
|
122952
|
+
this.cachedLines = void 0;
|
|
122726
122953
|
this.contentContainer.invalidate?.();
|
|
122727
122954
|
}
|
|
122728
122955
|
render(width) {
|
|
122956
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
122729
122957
|
if (this.lastText.trim().length === 0) return [];
|
|
122730
122958
|
const prefix = this.showBullet ? STATUS_BULLET : " ";
|
|
122731
122959
|
const contentWidth = Math.max(1, width - visibleWidth(prefix));
|
|
@@ -122735,6 +122963,8 @@ var AssistantMessageComponent = class {
|
|
|
122735
122963
|
const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(STATUS_BULLET) : " ";
|
|
122736
122964
|
lines.push(p + contentLines[i]);
|
|
122737
122965
|
}
|
|
122966
|
+
this.cachedWidth = width;
|
|
122967
|
+
this.cachedLines = lines;
|
|
122738
122968
|
return lines;
|
|
122739
122969
|
}
|
|
122740
122970
|
};
|
|
@@ -122743,17 +122973,29 @@ var AssistantMessageComponent = class {
|
|
|
122743
122973
|
var BackgroundAgentStatusComponent = class {
|
|
122744
122974
|
data;
|
|
122745
122975
|
colors;
|
|
122976
|
+
bullet;
|
|
122977
|
+
textComponent;
|
|
122978
|
+
cachedWidth;
|
|
122979
|
+
cachedLines;
|
|
122746
122980
|
constructor(data, colors) {
|
|
122747
122981
|
this.data = data;
|
|
122748
122982
|
this.colors = colors;
|
|
122983
|
+
const tone = data.phase === "started" ? colors.primary : data.phase === "completed" ? colors.success : colors.error;
|
|
122984
|
+
this.bullet = data.phase === "failed" ? chalk.hex(tone)(FAILURE_MARK) : chalk.hex(tone)(STATUS_BULLET);
|
|
122985
|
+
const text = chalk.hex(tone)(data.headline) + (data.detail !== void 0 && data.detail.length > 0 ? chalk.hex(colors.textDim)(` (${data.detail})`) : "");
|
|
122986
|
+
this.textComponent = new Text(text, 0, 0);
|
|
122987
|
+
}
|
|
122988
|
+
invalidate() {
|
|
122989
|
+
this.cachedWidth = void 0;
|
|
122990
|
+
this.cachedLines = void 0;
|
|
122749
122991
|
}
|
|
122750
|
-
invalidate() {}
|
|
122751
122992
|
render(width) {
|
|
122752
|
-
|
|
122753
|
-
const bullet = this.data.phase === "failed" ? chalk.hex(tone)(FAILURE_MARK) : chalk.hex(tone)(STATUS_BULLET);
|
|
122754
|
-
const textComponent = new Text(chalk.hex(tone)(this.data.headline) + (this.data.detail !== void 0 && this.data.detail.length > 0 ? chalk.hex(this.colors.textDim)(` (${this.data.detail})`) : ""), 0, 0);
|
|
122993
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
122755
122994
|
const contentWidth = Math.max(1, width - 2);
|
|
122756
|
-
|
|
122995
|
+
const lines = ["", ...this.textComponent.render(contentWidth).map((line, index) => (index === 0 ? this.bullet : " ") + line)];
|
|
122996
|
+
this.cachedWidth = width;
|
|
122997
|
+
this.cachedLines = lines;
|
|
122998
|
+
return lines;
|
|
122757
122999
|
}
|
|
122758
123000
|
};
|
|
122759
123001
|
//#endregion
|
|
@@ -122920,6 +123162,8 @@ var ThinkingComponent = class {
|
|
|
122920
123162
|
spinnerFrame = 0;
|
|
122921
123163
|
spinnerInterval;
|
|
122922
123164
|
textComponent;
|
|
123165
|
+
cachedWidth;
|
|
123166
|
+
cachedLines;
|
|
122923
123167
|
constructor(text, colors, showMarker = true, mode = "finalized", ui) {
|
|
122924
123168
|
this.text = text;
|
|
122925
123169
|
this.color = colors.roleThinking;
|
|
@@ -122929,17 +123173,25 @@ var ThinkingComponent = class {
|
|
|
122929
123173
|
this.textComponent = new Text(this.styled(text), 0, 0);
|
|
122930
123174
|
if (mode === "live") this.startSpinner();
|
|
122931
123175
|
}
|
|
122932
|
-
invalidate() {
|
|
123176
|
+
invalidate() {
|
|
123177
|
+
this.cachedWidth = void 0;
|
|
123178
|
+
this.cachedLines = void 0;
|
|
123179
|
+
}
|
|
122933
123180
|
setText(text) {
|
|
122934
123181
|
if (this.text === text) return;
|
|
122935
123182
|
this.text = text;
|
|
123183
|
+
this.cachedWidth = void 0;
|
|
123184
|
+
this.cachedLines = void 0;
|
|
122936
123185
|
this.textComponent.setText(this.styled(text));
|
|
122937
123186
|
}
|
|
122938
123187
|
styled(text) {
|
|
122939
123188
|
return chalk.hex(this.color).italic(text);
|
|
122940
123189
|
}
|
|
122941
123190
|
finalize() {
|
|
123191
|
+
if (this.mode === "finalized") return;
|
|
122942
123192
|
this.mode = "finalized";
|
|
123193
|
+
this.cachedWidth = void 0;
|
|
123194
|
+
this.cachedLines = void 0;
|
|
122943
123195
|
this.stopSpinner();
|
|
122944
123196
|
}
|
|
122945
123197
|
dispose() {
|
|
@@ -122948,8 +123200,11 @@ var ThinkingComponent = class {
|
|
|
122948
123200
|
setExpanded(expanded) {
|
|
122949
123201
|
if (this.expanded === expanded) return;
|
|
122950
123202
|
this.expanded = expanded;
|
|
123203
|
+
this.cachedWidth = void 0;
|
|
123204
|
+
this.cachedLines = void 0;
|
|
122951
123205
|
}
|
|
122952
123206
|
render(width) {
|
|
123207
|
+
if (this.mode === "finalized" && this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
122953
123208
|
const contentWidth = Math.max(1, width - 2);
|
|
122954
123209
|
const contentLines = this.text.length > 0 ? this.textComponent.render(contentWidth) : [""];
|
|
122955
123210
|
if (this.mode === "live") {
|
|
@@ -122965,10 +123220,16 @@ var ThinkingComponent = class {
|
|
|
122965
123220
|
const p = i === 0 && this.showMarker ? chalk.hex(this.color)(STATUS_BULLET) : " ";
|
|
122966
123221
|
rendered.push(p + contentLines[i]);
|
|
122967
123222
|
}
|
|
122968
|
-
if (this.expanded || contentLines.length <= 2)
|
|
123223
|
+
if (this.expanded || contentLines.length <= 2) {
|
|
123224
|
+
this.cachedWidth = width;
|
|
123225
|
+
this.cachedLines = rendered;
|
|
123226
|
+
return rendered;
|
|
123227
|
+
}
|
|
122969
123228
|
const truncated = rendered.slice(0, 3);
|
|
122970
123229
|
const remaining = contentLines.length - 2;
|
|
122971
123230
|
truncated.push(" " + chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`));
|
|
123231
|
+
this.cachedWidth = width;
|
|
123232
|
+
this.cachedLines = truncated;
|
|
122972
123233
|
return truncated;
|
|
122973
123234
|
}
|
|
122974
123235
|
startSpinner() {
|
|
@@ -123357,6 +123618,26 @@ function trimTrailingEmptyLines(lines) {
|
|
|
123357
123618
|
return lines.slice(0, end);
|
|
123358
123619
|
}
|
|
123359
123620
|
/**
|
|
123621
|
+
* Returns the tail of `text` whose UTF-8 byte length is at most `maxBytes`.
|
|
123622
|
+
* Iterates by Unicode code points so multi-byte characters and surrogate
|
|
123623
|
+
* pairs are never split.
|
|
123624
|
+
*/
|
|
123625
|
+
function truncateTailBytes(text, maxBytes) {
|
|
123626
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
123627
|
+
const chars = Array.from(text);
|
|
123628
|
+
let start = chars.length;
|
|
123629
|
+
let bytes = 0;
|
|
123630
|
+
for (let i = chars.length - 1; i >= 0; i--) {
|
|
123631
|
+
const char = chars[i];
|
|
123632
|
+
if (char === void 0) continue;
|
|
123633
|
+
const charBytes = Buffer.byteLength(char, "utf8");
|
|
123634
|
+
if (bytes + charBytes > maxBytes) break;
|
|
123635
|
+
bytes += charBytes;
|
|
123636
|
+
start = i;
|
|
123637
|
+
}
|
|
123638
|
+
return chars.slice(start).join("");
|
|
123639
|
+
}
|
|
123640
|
+
/**
|
|
123360
123641
|
* Component that renders tool output with wrap-aware line truncation.
|
|
123361
123642
|
* Uses pi-tui's Text component to compute actual visual wrapped lines,
|
|
123362
123643
|
* then caps at PREVIEW_LINES. This handles long single-line output (e.g.
|
|
@@ -123373,7 +123654,8 @@ var TruncatedOutputComponent = class {
|
|
|
123373
123654
|
this.hintFormatter = options.hintFormatter;
|
|
123374
123655
|
const tint = options.isError ? chalk.hex(options.colors.error) : chalk.dim;
|
|
123375
123656
|
const cleaned = trimTrailingEmptyLines(output.split("\n")).join("\n");
|
|
123376
|
-
|
|
123657
|
+
const truncated = options.maxBytes === void 0 ? cleaned : truncateTailBytes(cleaned, options.maxBytes);
|
|
123658
|
+
this.textComponent = new Text(tint(truncated), 2, 0);
|
|
123377
123659
|
}
|
|
123378
123660
|
invalidate() {
|
|
123379
123661
|
this.textComponent.invalidate();
|
|
@@ -123419,6 +123701,7 @@ var ShellExecutionComponent = class extends Container {
|
|
|
123419
123701
|
isError: result.is_error ?? false,
|
|
123420
123702
|
colors,
|
|
123421
123703
|
maxLines: previewLines,
|
|
123704
|
+
maxBytes: MAX_SHELL_OUTPUT_BYTES,
|
|
123422
123705
|
hintFormatter: (remaining) => `...(还有 ${String(remaining)} 行,按 ctrl+o 展开)`
|
|
123423
123706
|
}));
|
|
123424
123707
|
}
|
|
@@ -124013,6 +124296,7 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
124013
124296
|
subagentError;
|
|
124014
124297
|
streamingProgressTimer;
|
|
124015
124298
|
subagentElapsedTimer;
|
|
124299
|
+
disposed = false;
|
|
124016
124300
|
subagentStartedAtMs;
|
|
124017
124301
|
subagentEndedAtMs;
|
|
124018
124302
|
progressLines = [];
|
|
@@ -124092,6 +124376,8 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
124092
124376
|
this.ui?.requestRender();
|
|
124093
124377
|
}
|
|
124094
124378
|
dispose() {
|
|
124379
|
+
if (this.disposed) return;
|
|
124380
|
+
this.disposed = true;
|
|
124095
124381
|
this.stopStreamingProgressTimer();
|
|
124096
124382
|
this.stopSubagentElapsedTimer();
|
|
124097
124383
|
}
|
|
@@ -124247,6 +124533,10 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
124247
124533
|
}
|
|
124248
124534
|
if (this.ui === void 0 || this.streamingProgressTimer !== void 0) return;
|
|
124249
124535
|
this.streamingProgressTimer = setInterval(() => {
|
|
124536
|
+
if (this.disposed) {
|
|
124537
|
+
this.stopStreamingProgressTimer();
|
|
124538
|
+
return;
|
|
124539
|
+
}
|
|
124250
124540
|
if (!this.isStreamingEditPreview()) {
|
|
124251
124541
|
this.stopStreamingProgressTimer();
|
|
124252
124542
|
return;
|
|
@@ -124268,6 +124558,10 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
124268
124558
|
}
|
|
124269
124559
|
if (this.ui === void 0 || this.subagentElapsedTimer !== void 0) return;
|
|
124270
124560
|
this.subagentElapsedTimer = setInterval(() => {
|
|
124561
|
+
if (this.disposed) {
|
|
124562
|
+
this.stopSubagentElapsedTimer();
|
|
124563
|
+
return;
|
|
124564
|
+
}
|
|
124271
124565
|
const latestPhase = this.getDerivedSubagentPhase();
|
|
124272
124566
|
if (latestPhase !== "spawning" && latestPhase !== "running") {
|
|
124273
124567
|
this.stopSubagentElapsedTimer();
|
|
@@ -124739,6 +125033,8 @@ var ToolCallComponent = class ToolCallComponent extends Container {
|
|
|
124739
125033
|
});
|
|
124740
125034
|
for (const line of lines) this.addChild(new Text(line, 2, 0));
|
|
124741
125035
|
}
|
|
125036
|
+
const description = str(this.toolCall.description);
|
|
125037
|
+
if (this.expanded && description.length > 0) this.addChild(new Text(chalk.dim(description), 2, 0));
|
|
124742
125038
|
}
|
|
124743
125039
|
/**
|
|
124744
125040
|
* Live-rendering during the `tool.call.delta` streaming window.
|
|
@@ -124957,6 +125253,8 @@ var UserMessageComponent = class {
|
|
|
124957
125253
|
textComponent;
|
|
124958
125254
|
spacerComponent;
|
|
124959
125255
|
imageThumbnails;
|
|
125256
|
+
cachedWidth;
|
|
125257
|
+
cachedLines;
|
|
124960
125258
|
constructor(text, colors, images) {
|
|
124961
125259
|
this.color = colors.roleUser;
|
|
124962
125260
|
this.textComponent = new Text(chalk.hex(colors.roleUser).bold(text), 0, 0);
|
|
@@ -124964,10 +125262,13 @@ var UserMessageComponent = class {
|
|
|
124964
125262
|
this.imageThumbnails = images?.map((img) => new ImageThumbnail(img, colors)) ?? [];
|
|
124965
125263
|
}
|
|
124966
125264
|
invalidate() {
|
|
125265
|
+
this.cachedWidth = void 0;
|
|
125266
|
+
this.cachedLines = void 0;
|
|
124967
125267
|
this.textComponent.invalidate();
|
|
124968
125268
|
for (const img of this.imageThumbnails) img.invalidate?.();
|
|
124969
125269
|
}
|
|
124970
125270
|
render(width) {
|
|
125271
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
124971
125272
|
const border = chalk.hex(this.color).bold(USER_MESSAGE_BULLET);
|
|
124972
125273
|
const borderWidth = visibleWidth(border);
|
|
124973
125274
|
const contentWidth = Math.max(1, width - borderWidth);
|
|
@@ -124982,6 +125283,8 @@ var UserMessageComponent = class {
|
|
|
124982
125283
|
const imageLines = thumbnail.render(contentWidth);
|
|
124983
125284
|
for (const line of imageLines) lines.push(" ".repeat(borderWidth) + line);
|
|
124984
125285
|
}
|
|
125286
|
+
this.cachedWidth = width;
|
|
125287
|
+
this.cachedLines = lines;
|
|
124985
125288
|
return lines;
|
|
124986
125289
|
}
|
|
124987
125290
|
};
|
|
@@ -126505,15 +126808,11 @@ function buildOptions(marketplace, installed) {
|
|
|
126505
126808
|
label: "── 已安装 ──",
|
|
126506
126809
|
description: void 0
|
|
126507
126810
|
});
|
|
126508
|
-
for (const p of installed) {
|
|
126509
|
-
|
|
126510
|
-
|
|
126511
|
-
|
|
126512
|
-
|
|
126513
|
-
label: p.displayName,
|
|
126514
|
-
description: `${meta} [${p.skillCount} skills]`
|
|
126515
|
-
});
|
|
126516
|
-
}
|
|
126811
|
+
for (const p of installed) options.push({
|
|
126812
|
+
value: `uninstall:${p.id}`,
|
|
126813
|
+
label: p.displayName,
|
|
126814
|
+
description: formatInstalledPluginDescription(p)
|
|
126815
|
+
});
|
|
126517
126816
|
}
|
|
126518
126817
|
if (options.length === 0) options.push({
|
|
126519
126818
|
value: "__empty__",
|
|
@@ -126555,6 +126854,20 @@ async function confirmUninstall(host, label) {
|
|
|
126555
126854
|
host.mountEditorReplacement(picker);
|
|
126556
126855
|
});
|
|
126557
126856
|
}
|
|
126857
|
+
const SKILL_DESC_MAX = 40;
|
|
126858
|
+
const SKILLS_PREVIEW_COUNT = 3;
|
|
126859
|
+
function formatInstalledPluginDescription(p) {
|
|
126860
|
+
const enabledTag = p.enabled ? "✓ 已启用" : "✗ 已禁用";
|
|
126861
|
+
const meta = [p.version ? `v${p.version}` : "", enabledTag].filter(Boolean).join(" ");
|
|
126862
|
+
const skillDescriptions = p.skills.slice(0, SKILLS_PREVIEW_COUNT).map((s) => `${s.name}: ${truncate$1(s.description, SKILL_DESC_MAX)}`);
|
|
126863
|
+
const remaining = p.skillCount - SKILLS_PREVIEW_COUNT;
|
|
126864
|
+
const descriptionParts = [`${meta} [${p.skillCount} skills]`, ...skillDescriptions];
|
|
126865
|
+
if (remaining > 0) descriptionParts.push(`…等 ${remaining} 个 skill`);
|
|
126866
|
+
return descriptionParts.join(" · ");
|
|
126867
|
+
}
|
|
126868
|
+
function truncate$1(value, max) {
|
|
126869
|
+
return value.length > max ? `${value.slice(0, max)}…` : value;
|
|
126870
|
+
}
|
|
126558
126871
|
//#endregion
|
|
126559
126872
|
//#region src/tui/commands/btw.ts
|
|
126560
126873
|
/**
|
|
@@ -130181,6 +130494,7 @@ var StreamingUIController = class {
|
|
|
130181
130494
|
const tc = this._pendingToolComponents.get(toolCallId);
|
|
130182
130495
|
if (tc) {
|
|
130183
130496
|
tc.setResult(result);
|
|
130497
|
+
tc.dispose();
|
|
130184
130498
|
this._pendingToolComponents.delete(toolCallId);
|
|
130185
130499
|
state.ui.requestRender();
|
|
130186
130500
|
return;
|
|
@@ -131504,6 +131818,10 @@ var CronMessageComponent = class {
|
|
|
131504
131818
|
detail;
|
|
131505
131819
|
titleColor;
|
|
131506
131820
|
promptText;
|
|
131821
|
+
bullet;
|
|
131822
|
+
bulletWidth;
|
|
131823
|
+
cachedWidth;
|
|
131824
|
+
cachedLines;
|
|
131507
131825
|
constructor(prompt, data, colors) {
|
|
131508
131826
|
this.colors = colors;
|
|
131509
131827
|
const missed = data.missedCount !== void 0;
|
|
@@ -131511,21 +131829,26 @@ var CronMessageComponent = class {
|
|
|
131511
131829
|
this.detail = cronDetail(data);
|
|
131512
131830
|
this.titleColor = data.stale === true || missed ? colors.warning : colors.accent;
|
|
131513
131831
|
this.promptText = new Text(chalk.hex(colors.text)(prompt), 0, 0);
|
|
131832
|
+
this.bullet = chalk.hex(this.titleColor).bold(STATUS_BULLET);
|
|
131833
|
+
this.bulletWidth = visibleWidth(this.bullet);
|
|
131514
131834
|
}
|
|
131515
131835
|
invalidate() {
|
|
131836
|
+
this.cachedWidth = void 0;
|
|
131837
|
+
this.cachedLines = void 0;
|
|
131516
131838
|
this.promptText.invalidate();
|
|
131517
131839
|
}
|
|
131518
131840
|
render(width) {
|
|
131519
|
-
|
|
131520
|
-
const
|
|
131521
|
-
const contentWidth = Math.max(1, width - bulletWidth);
|
|
131841
|
+
if (this.cachedLines !== void 0 && this.cachedWidth === width) return this.cachedLines;
|
|
131842
|
+
const contentWidth = Math.max(1, width - this.bulletWidth);
|
|
131522
131843
|
const lines = [];
|
|
131523
131844
|
for (const line of this.spacer.render(width)) lines.push(line);
|
|
131524
131845
|
const title = chalk.hex(this.titleColor).bold(this.title);
|
|
131525
|
-
lines.push(`${bullet}${title}`);
|
|
131526
|
-
if (this.detail !== void 0) lines.push(`${" ".repeat(bulletWidth)}${chalk.hex(this.colors.textDim)(this.detail)}`);
|
|
131846
|
+
lines.push(`${this.bullet}${title}`);
|
|
131847
|
+
if (this.detail !== void 0) lines.push(`${" ".repeat(this.bulletWidth)}${chalk.hex(this.colors.textDim)(this.detail)}`);
|
|
131527
131848
|
const promptLines = this.promptText.render(contentWidth);
|
|
131528
|
-
for (const line of promptLines) lines.push(`${" ".repeat(bulletWidth)}${line}`);
|
|
131849
|
+
for (const line of promptLines) lines.push(`${" ".repeat(this.bulletWidth)}${line}`);
|
|
131850
|
+
this.cachedWidth = width;
|
|
131851
|
+
this.cachedLines = lines;
|
|
131529
131852
|
return lines;
|
|
131530
131853
|
}
|
|
131531
131854
|
};
|
|
@@ -132315,10 +132638,7 @@ var FooterComponent = class {
|
|
|
132315
132638
|
if (state.wolfpackMode) left.push(chalk.hex(colors.primary).bold("wolfpack"));
|
|
132316
132639
|
if (state.goalActive) left.push(chalk.hex(colors.primary).bold("goal"));
|
|
132317
132640
|
const model = shortenModel(modelDisplayName(state));
|
|
132318
|
-
if (model)
|
|
132319
|
-
const thinkingLabel = state.thinking ? " 思考中" : "";
|
|
132320
|
-
left.push(chalk.hex(colors.text)(`${model}${thinkingLabel}`));
|
|
132321
|
-
}
|
|
132641
|
+
if (model) left.push(chalk.hex(colors.text)(model));
|
|
132322
132642
|
if (this.backgroundBashTaskCount > 0) {
|
|
132323
132643
|
const noun = this.backgroundBashTaskCount === 1 ? "个任务" : "个任务";
|
|
132324
132644
|
left.push(chalk.hex(colors.primary)(`[${String(this.backgroundBashTaskCount)}${noun} 运行中]`));
|
|
@@ -132567,6 +132887,8 @@ var CustomEditor = class extends Editor {
|
|
|
132567
132887
|
resetFirstInputGate() {
|
|
132568
132888
|
this.firstInputFired = false;
|
|
132569
132889
|
}
|
|
132890
|
+
/** Whether the active model has thinking enabled. When true, a small "think" label is embedded in the top-right of the input box border. */
|
|
132891
|
+
thinking = false;
|
|
132570
132892
|
consumingPaste = false;
|
|
132571
132893
|
consumeBuffer = "";
|
|
132572
132894
|
/**
|
|
@@ -132625,7 +132947,10 @@ var CustomEditor = class extends Editor {
|
|
|
132625
132947
|
const withPrompt = injectPromptSymbol(firstContent);
|
|
132626
132948
|
if (withPrompt !== void 0) lines[firstContentIdx] = withPrompt;
|
|
132627
132949
|
}
|
|
132628
|
-
|
|
132950
|
+
const paint = this.borderColor ?? ((s) => s);
|
|
132951
|
+
const wrapped = wrapWithSideBorders(lines, paint);
|
|
132952
|
+
if (this.thinking) injectThinkLabel(wrapped, width, paint);
|
|
132953
|
+
return wrapped;
|
|
132629
132954
|
}
|
|
132630
132955
|
handleInput(data) {
|
|
132631
132956
|
const normalized = normalizeCapsLockedCtrl(data);
|
|
@@ -132783,6 +133108,23 @@ function wrapWithSideBorders(lines, paint) {
|
|
|
132783
133108
|
return head + line.slice(1, -1) + tail;
|
|
132784
133109
|
});
|
|
132785
133110
|
}
|
|
133111
|
+
const THINK_LABEL = " Think ";
|
|
133112
|
+
const THINK_LABEL_MIN_WIDTH = 14;
|
|
133113
|
+
/**
|
|
133114
|
+
* Embed a small "think" label into the top-right corner of the input box
|
|
133115
|
+
* border, mirroring the welcome panel's top-left border title style.
|
|
133116
|
+
* The label only appears when the active model has thinking enabled.
|
|
133117
|
+
*/
|
|
133118
|
+
function injectThinkLabel(lines, width, paint) {
|
|
133119
|
+
if (width < THINK_LABEL_MIN_WIDTH) return;
|
|
133120
|
+
const topIdx = lines.findIndex((line) => line.includes("╭"));
|
|
133121
|
+
if (topIdx === -1) return;
|
|
133122
|
+
const labelBlock = `─${THINK_LABEL}─`;
|
|
133123
|
+
const labelVis = visibleWidth(labelBlock);
|
|
133124
|
+
const leftDashCount = width - 2 - labelVis;
|
|
133125
|
+
if (leftDashCount < 1) return;
|
|
133126
|
+
lines[topIdx] = paint("╭" + "─".repeat(leftDashCount) + labelBlock + "╮");
|
|
133127
|
+
}
|
|
132786
133128
|
//#endregion
|
|
132787
133129
|
//#region src/tui/utils/terminal-state.ts
|
|
132788
133130
|
function createTerminalState() {
|
|
@@ -132817,6 +133159,7 @@ function createTUIState(options) {
|
|
|
132817
133159
|
const queueContainer = new GutterContainer(1, 1);
|
|
132818
133160
|
const editorContainer = new GutterContainer(1, 1);
|
|
132819
133161
|
const editor = new CustomEditor(ui, theme.colors);
|
|
133162
|
+
editor.thinking = initialAppState.thinking;
|
|
132820
133163
|
return {
|
|
132821
133164
|
ui,
|
|
132822
133165
|
terminal,
|
|
@@ -133799,7 +134142,6 @@ var SessionManager = class {
|
|
|
133799
134142
|
this.host.showError("历史回放期间无法启动新会话。");
|
|
133800
134143
|
return;
|
|
133801
134144
|
}
|
|
133802
|
-
await this.extractMemoriesBeforeSwitch();
|
|
133803
134145
|
let session;
|
|
133804
134146
|
try {
|
|
133805
134147
|
session = await this.createSessionFromCurrentState();
|
|
@@ -133857,20 +134199,6 @@ var SessionManager = class {
|
|
|
133857
134199
|
this.host.updateQueueDisplay();
|
|
133858
134200
|
this.host.stopMemoryIdleTimer();
|
|
133859
134201
|
}
|
|
133860
|
-
async extractMemoriesBeforeSwitch() {
|
|
133861
|
-
const session = this.host.session;
|
|
133862
|
-
if (session === void 0) return;
|
|
133863
|
-
if (this.host.state.appState.streamingPhase !== "idle") return;
|
|
133864
|
-
this.host.state.footer.setTransientHint("正在整理会话记忆...");
|
|
133865
|
-
this.host.state.ui.requestRender();
|
|
133866
|
-
try {
|
|
133867
|
-
await Promise.race([session.extractMemoriesOnExit(), new Promise((resolve) => setTimeout(resolve, 3e4))]);
|
|
133868
|
-
this.host.showStatus("已沉淀关键信息至记忆备忘录");
|
|
133869
|
-
} catch {} finally {
|
|
133870
|
-
this.host.state.footer.setTransientHint(null);
|
|
133871
|
-
this.host.state.ui.requestRender();
|
|
133872
|
-
}
|
|
133873
|
-
}
|
|
133874
134202
|
requireSession() {
|
|
133875
134203
|
if (this.host.session === void 0) throw new Error(NO_ACTIVE_SESSION_MESSAGE);
|
|
133876
134204
|
return this.host.session;
|
|
@@ -135782,7 +136110,8 @@ function createInitialAppState(input) {
|
|
|
135782
136110
|
goalActive: false,
|
|
135783
136111
|
goalContinuationCount: 0,
|
|
135784
136112
|
ccConnectActive: false,
|
|
135785
|
-
wolfpackMode: false
|
|
136113
|
+
wolfpackMode: false,
|
|
136114
|
+
recentSessions: []
|
|
135786
136115
|
};
|
|
135787
136116
|
}
|
|
135788
136117
|
var ScreamTUI = class ScreamTUI {
|
|
@@ -135919,6 +136248,16 @@ var ScreamTUI = class ScreamTUI {
|
|
|
135919
136248
|
}
|
|
135920
136249
|
async initMainTui() {
|
|
135921
136250
|
const shouldReplayHistory = await this.init();
|
|
136251
|
+
try {
|
|
136252
|
+
const sessions = await this.harness.listSessions({ workDir: this.state.appState.workDir });
|
|
136253
|
+
this.state.appState.recentSessions = sessions.slice(0, 3).map((s) => ({
|
|
136254
|
+
id: s.id,
|
|
136255
|
+
title: s.title,
|
|
136256
|
+
updatedAt: s.updatedAt
|
|
136257
|
+
}));
|
|
136258
|
+
} catch {
|
|
136259
|
+
this.state.appState.recentSessions = [];
|
|
136260
|
+
}
|
|
135922
136261
|
this.mountFooter();
|
|
135923
136262
|
this.renderWelcome();
|
|
135924
136263
|
setExperimentalFlags(await this.harness.getExperimentalFlags());
|
|
@@ -136010,7 +136349,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
136010
136349
|
this.ccConnectPollTimer = void 0;
|
|
136011
136350
|
}
|
|
136012
136351
|
}
|
|
136013
|
-
static MEMORY_IDLE_MS =
|
|
136352
|
+
static MEMORY_IDLE_MS = 900 * 1e3;
|
|
136014
136353
|
static MEMORY_EXTRACT_COOLDOWN_MS = 600 * 1e3;
|
|
136015
136354
|
startMemoryIdleTimer() {
|
|
136016
136355
|
this.stopMemoryIdleTimer();
|
|
@@ -136342,6 +136681,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
136342
136681
|
const busyChanged = "streamingPhase" in patch || "isCompacting" in patch;
|
|
136343
136682
|
Object.assign(this.state.appState, patch);
|
|
136344
136683
|
if ("planMode" in patch) this.updateEditorBorderHighlight();
|
|
136684
|
+
if ("thinking" in patch) this.state.editor.thinking = patch.thinking ?? false;
|
|
136345
136685
|
if ("streamingPhase" in patch && patch.streamingPhase !== "idle") this.welcomeComponent?.stopBreathing();
|
|
136346
136686
|
this.state.footer.setState(this.state.appState);
|
|
136347
136687
|
this.updateActivityPane();
|
|
@@ -136484,7 +136824,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
136484
136824
|
}
|
|
136485
136825
|
renderWelcome() {
|
|
136486
136826
|
this.welcomeComponent?.stopBreathing();
|
|
136487
|
-
const welcome = new WelcomeComponent(this.state.appState, this.state.theme.colors, this.state.ui);
|
|
136827
|
+
const welcome = new WelcomeComponent(this.state.appState, this.state.theme.colors, this.state.ui, this.state.appState.recentSessions);
|
|
136488
136828
|
welcome.borderTitle = "Scream Code";
|
|
136489
136829
|
this.welcomeComponent = welcome;
|
|
136490
136830
|
if (this.state.editor.hasFirstInputFired()) welcome.stopBreathing();
|
|
@@ -136549,7 +136889,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
136549
136889
|
updateActivityPane() {
|
|
136550
136890
|
const effectiveMode = this.resolveActivityPaneMode();
|
|
136551
136891
|
this.syncTerminalProgress(this.shouldShowTerminalProgress(effectiveMode));
|
|
136552
|
-
if (effectiveMode === this.lastActivityMode
|
|
136892
|
+
if (effectiveMode === this.lastActivityMode) return;
|
|
136553
136893
|
this.lastActivityMode = effectiveMode;
|
|
136554
136894
|
this.state.activityContainer.clear();
|
|
136555
136895
|
switch (effectiveMode) {
|
|
@@ -136653,6 +136993,7 @@ var ScreamTUI = class ScreamTUI {
|
|
|
136653
136993
|
this.state.theme.markdownTheme = nextTheme.markdownTheme;
|
|
136654
136994
|
this.setAppState({ theme });
|
|
136655
136995
|
this.updateEditorBorderHighlight();
|
|
136996
|
+
for (const child of this.state.transcriptContainer.children) child.invalidate?.();
|
|
136656
136997
|
this.state.ui.requestRender(true);
|
|
136657
136998
|
}
|
|
136658
136999
|
refreshTerminalThemeTracking() {
|