cc-viewer 1.6.282 → 1.6.283
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/README.md +8 -0
- package/concepts/ar/Tool-LSP.md +28 -0
- package/concepts/ar/Tool-Workflow.md +26 -0
- package/concepts/da/Tool-LSP.md +28 -0
- package/concepts/da/Tool-Workflow.md +26 -0
- package/concepts/de/Tool-LSP.md +28 -0
- package/concepts/de/Tool-Workflow.md +26 -0
- package/concepts/en/Tool-LSP.md +28 -0
- package/concepts/en/Tool-Workflow.md +26 -0
- package/concepts/es/Tool-LSP.md +28 -0
- package/concepts/es/Tool-Workflow.md +26 -0
- package/concepts/fr/Tool-LSP.md +28 -0
- package/concepts/fr/Tool-Workflow.md +26 -0
- package/concepts/it/Tool-LSP.md +28 -0
- package/concepts/it/Tool-Workflow.md +26 -0
- package/concepts/ja/Tool-LSP.md +28 -0
- package/concepts/ja/Tool-Workflow.md +26 -0
- package/concepts/ko/Tool-LSP.md +28 -0
- package/concepts/ko/Tool-Workflow.md +26 -0
- package/concepts/no/Tool-LSP.md +28 -0
- package/concepts/no/Tool-Workflow.md +26 -0
- package/concepts/pl/Tool-LSP.md +28 -0
- package/concepts/pl/Tool-Workflow.md +26 -0
- package/concepts/pt-BR/Tool-LSP.md +28 -0
- package/concepts/pt-BR/Tool-Workflow.md +26 -0
- package/concepts/ru/Tool-LSP.md +28 -0
- package/concepts/ru/Tool-Workflow.md +26 -0
- package/concepts/th/Tool-LSP.md +28 -0
- package/concepts/th/Tool-Workflow.md +26 -0
- package/concepts/tr/Tool-LSP.md +28 -0
- package/concepts/tr/Tool-Workflow.md +26 -0
- package/concepts/uk/Tool-LSP.md +28 -0
- package/concepts/uk/Tool-Workflow.md +26 -0
- package/concepts/zh/Tool-LSP.md +28 -0
- package/concepts/zh/Tool-Workflow.md +26 -0
- package/concepts/zh-TW/Tool-LSP.md +28 -0
- package/concepts/zh-TW/Tool-Workflow.md +26 -0
- package/dist/assets/{App-GKwX9hll.css → App-ByUIC-yr.css} +1 -1
- package/dist/assets/App-C-jwJCC7.js +1 -0
- package/dist/assets/{MdxEditorPanel-L-j1sTqi.js → MdxEditorPanel-DcHac_1Y.js} +1 -1
- package/dist/assets/{Mobile-B6Z3C51V.js → Mobile-D4IM7nzP.js} +1 -1
- package/dist/assets/index-BmzQMRa6.js +2 -0
- package/dist/assets/seqResourceLoaders-Bwq1pWpw.css +41 -0
- package/dist/assets/seqResourceLoaders-soK06gBr.js +2 -0
- package/dist/index.html +1 -1
- package/package.json +3 -2
- package/server/_paths.js +2 -0
- package/server/lib/proxy-env.js +16 -1
- package/server/lib/ultra-agents-api.js +113 -0
- package/server/proxy.js +23 -15
- package/server/routes/ultra-agents.js +20 -0
- package/server/server.js +2 -0
- package/ultraAgents/README.md +62 -0
- package/ultraAgents/code-expert.json +45 -0
- package/ultraAgents/research-expert.json +45 -0
- package/dist/assets/App-1VhyWwH5.js +0 -1
- package/dist/assets/index-Ce6a1edB.js +0 -2
- package/dist/assets/seqResourceLoaders-CWVxLnsD.css +0 -41
- package/dist/assets/seqResourceLoaders-D9p0n9TP.js +0 -2
package/dist/index.html
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
if (pick) document.documentElement.setAttribute('data-theme', pick);
|
|
20
20
|
} catch {}
|
|
21
21
|
</script>
|
|
22
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-BmzQMRa6.js"></script>
|
|
23
23
|
<link rel="modulepreload" crossorigin href="/assets/vendor-antd-Cb6u7XOI.js">
|
|
24
24
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-StjUr1Z2.js">
|
|
25
25
|
<link rel="modulepreload" crossorigin href="/assets/vendor-mdxeditor-CN_kFxJv.js">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-viewer",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.283",
|
|
4
4
|
"description": "Claude Code Logger visualization management tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server.js",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"interceptor.js",
|
|
57
57
|
"server/",
|
|
58
58
|
"plugins/",
|
|
59
|
-
"concepts/"
|
|
59
|
+
"concepts/",
|
|
60
|
+
"ultraAgents/"
|
|
60
61
|
],
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@codemirror/lang-cpp": "^6.0.3",
|
package/server/_paths.js
CHANGED
|
@@ -31,5 +31,7 @@ export const PUBLIC_DIR = join(PACKAGE_ROOT, 'public');
|
|
|
31
31
|
export const CONCEPTS_DIR = join(PACKAGE_ROOT, 'concepts');
|
|
32
32
|
/** Bundled plugin 目录(plugin-loader 启动时扫这里) */
|
|
33
33
|
export const PLUGINS_DIR = join(PACKAGE_ROOT, 'plugins');
|
|
34
|
+
/** 随包发布的 ultraplan 预设专家目录(ultraAgents/*.json,/api/ultra-agents 扫这里) */
|
|
35
|
+
export const ULTRA_AGENTS_DIR = join(PACKAGE_ROOT, 'ultraAgents');
|
|
34
36
|
/** cc-viewer 自己的 package.json(updater/server.js 读 version) */
|
|
35
37
|
export const PACKAGE_JSON = join(PACKAGE_ROOT, 'package.json');
|
package/server/lib/proxy-env.js
CHANGED
|
@@ -10,14 +10,29 @@ export function resolveProxyConfig(env = process.env) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// 关键坑位(Node 26 起的回归):代理转发上游用的是 Node 内置全局 fetch,背后是 Node 自带的
|
|
14
|
+
// 那份 undici。Node ≤25 时它与 userland undici 包共享同一个 global dispatcher(Symbol.for
|
|
15
|
+
// 同键),所以单调 setGlobalDispatcher 也能让转发请求走代理——这也是为何旧代码一直好用。
|
|
16
|
+
// 实测 Node 26 起两份 undici 不再共享 global dispatcher,单靠 setGlobalDispatcher,转发请求
|
|
17
|
+
// 读不到 http_proxy/https_proxy,会直连 api.anthropic.com 绕过用户的网络代理。
|
|
18
|
+
// 解法:把这里构造的 EnvHttpProxyAgent 显式保存下来,由 proxy 转发处作为 fetch 的 dispatcher
|
|
19
|
+
// 选项传入(内置 fetch 接受 userland undici 的 dispatcher 实例,各 Node 版本通用)。
|
|
20
|
+
let _proxyDispatcher = null;
|
|
21
|
+
|
|
13
22
|
export function setupProxyEnv() {
|
|
14
23
|
const { httpProxy, httpsProxy, noProxy } = resolveProxyConfig();
|
|
15
24
|
if (!httpProxy && !httpsProxy) return;
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
_proxyDispatcher = new EnvHttpProxyAgent({ httpProxy, httpsProxy, noProxy });
|
|
27
|
+
setGlobalDispatcher(_proxyDispatcher); // 仍保留:覆盖直接 import 'undici' 的 fetch 调用路径
|
|
18
28
|
if (process.env.CCV_DEBUG) {
|
|
19
29
|
console.error(`[CC Viewer] HTTP proxy: http=${httpProxy || '(none)'}, https=${httpsProxy || '(none)'}${noProxy ? `, no_proxy=${noProxy}` : ''}`);
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
|
|
33
|
+
// 返回供"内置全局 fetch"使用的代理 dispatcher;无代理配置时返回 null(调用方不传即直连)。
|
|
34
|
+
export function getProxyDispatcher() {
|
|
35
|
+
return _proxyDispatcher;
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
setupProxyEnv();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// 随包发布的 ultraplan「预设专家」加载器 —— 扫 `ultraAgents/*.json`,校验后返回给前端。
|
|
2
|
+
// 纯 Node 实现,无 React / 浏览器依赖,方便 test/ultra-agents-api.test.js 直接 import。
|
|
3
|
+
//
|
|
4
|
+
// 安全模型:只读 cc-viewer 包自带的 ULTRA_AGENTS_DIR,文件名由 readdirSync 枚举(不接受
|
|
5
|
+
// 请求参数 / 项目目录),故无路径穿越面。文件随包发布属可信内容,但仍做防御性体量限制
|
|
6
|
+
// (单文件 ≤256KB、有效专家 ≤100)以免被异常大文件 / 海量文件拖垮端点。
|
|
7
|
+
|
|
8
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { ULTRA_AGENTS_DIR } from '../_paths.js';
|
|
11
|
+
|
|
12
|
+
// 单文件体量上限:预设专家正文再长也远小于 256KB,超出视为异常文件,跳过。
|
|
13
|
+
const MAX_FILE_BYTES = 256 * 1024;
|
|
14
|
+
// 有效专家数量上限:防止目录被塞入海量文件导致响应体过大 / 事件循环阻塞。
|
|
15
|
+
const MAX_AGENTS = 100;
|
|
16
|
+
// agent id 安全字符集:字母数字 . _ -(不含冒号,agent id 不是 plugin 名)。
|
|
17
|
+
// id 仅作去重键与前端 key,不参与拼路径,但仍约束格式以保持数据干净。
|
|
18
|
+
const SAFE_ID = /^[A-Za-z0-9._-]+$/;
|
|
19
|
+
|
|
20
|
+
export function validateAgentId(id) {
|
|
21
|
+
if (typeof id !== 'string' || !id) return false;
|
|
22
|
+
if (id.length > 200) return false;
|
|
23
|
+
if (id.startsWith('.')) return false;
|
|
24
|
+
if (id.includes('..') || id.includes('/') || id.includes('\\') || id.includes('\0')) return false;
|
|
25
|
+
return SAFE_ID.test(id);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 文本字段校验:支持「纯字符串」或「{lang: str} 本地化对象」两种合法形态。
|
|
29
|
+
// · 字符串 → trim 后非空即可;
|
|
30
|
+
// · 普通对象(非数组)→ 至少一个值是非空字符串;
|
|
31
|
+
// · 其它(数组 / 数字 / 布尔 / null / 空对象 / 全空串对象)→ 非法。
|
|
32
|
+
// title / description 在 JSON 协议层内联本地化(可为对象),前端按当前语言解析;content 为单语言字符串。
|
|
33
|
+
export function isValidTextField(v) {
|
|
34
|
+
if (typeof v === 'string') return v.trim().length > 0;
|
|
35
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
36
|
+
return Object.values(v).some(x => typeof x === 'string' && x.trim().length > 0);
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// content 专用校验:content 是单语言字符串(不内联本地化,见 README / resolveLocalized 注释),
|
|
42
|
+
// 必须为 trim 后非空的字符串。对象/数组/标量一律非法——否则对象会被前端当字符串渲染成
|
|
43
|
+
// `[object Object]`(预览框 / 存入编辑器)。与 isValidTextField 分开,正是为了不放行本地化对象。
|
|
44
|
+
export function isNonEmptyString(v) {
|
|
45
|
+
return typeof v === 'string' && v.trim().length > 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 扫描 dir 下所有 *.json,解析 + 校验 + 去重,返回 [{ id, title, description, content }]。
|
|
49
|
+
// title/description 原样返回(可能是 {lang: str} 本地化对象),由前端 resolveLocalized 按当前语言解析;
|
|
50
|
+
// content 为单语言字符串、原样返回。
|
|
51
|
+
// 其它字段(如 version)一律忽略:version 为前向兼容标记、当前不读不校验,
|
|
52
|
+
// 未知 key 不报错也不透传,保证前向兼容。
|
|
53
|
+
// 任一文件损坏 / 不合法只 console.warn 跳过,绝不抛错中断整体加载。
|
|
54
|
+
export function listUltraAgents({ dir = ULTRA_AGENTS_DIR } = {}) {
|
|
55
|
+
if (!existsSync(dir)) return [];
|
|
56
|
+
const out = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn('[ultra-agents] 读取目录失败,返回空列表:', err?.message);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
// 文件名排序,保证「先到先得」去重与跨平台输出顺序稳定。
|
|
66
|
+
const files = entries
|
|
67
|
+
.filter(e => e.isFile() && e.name.toLowerCase().endsWith('.json'))
|
|
68
|
+
.map(e => e.name)
|
|
69
|
+
.sort();
|
|
70
|
+
|
|
71
|
+
for (const name of files) {
|
|
72
|
+
if (out.length >= MAX_AGENTS) {
|
|
73
|
+
console.warn(`[ultra-agents] 预设专家数超过上限 ${MAX_AGENTS},其余忽略`);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const filePath = join(dir, name);
|
|
77
|
+
try {
|
|
78
|
+
if (statSync(filePath).size > MAX_FILE_BYTES) {
|
|
79
|
+
console.warn(`[ultra-agents] 文件过大已跳过: ${name}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
83
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
84
|
+
console.warn(`[ultra-agents] 非对象 JSON 已跳过: ${name}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!validateAgentId(parsed.id)) {
|
|
88
|
+
console.warn(`[ultra-agents] id 非法已跳过: ${name}`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// title 可内联本地化(字符串或对象);content 必须是单语言字符串。
|
|
92
|
+
if (!isValidTextField(parsed.title) || !isNonEmptyString(parsed.content)) {
|
|
93
|
+
console.warn(`[ultra-agents] title/content 缺失或非法已跳过: ${name}`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (seen.has(parsed.id)) {
|
|
97
|
+
console.warn(`[ultra-agents] id 重复已跳过(先到先得): ${parsed.id} (${name})`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
seen.add(parsed.id);
|
|
101
|
+
out.push({
|
|
102
|
+
id: parsed.id,
|
|
103
|
+
title: parsed.title,
|
|
104
|
+
// description 可选;非法 / 缺失时给空串,前端 resolveLocalized 容错。
|
|
105
|
+
description: isValidTextField(parsed.description) ? parsed.description : '',
|
|
106
|
+
content: parsed.content,
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.warn(`[ultra-agents] 解析失败已跳过: ${name} (${err?.message})`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
package/server/proxy.js
CHANGED
|
@@ -6,26 +6,26 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import * as interceptor from './interceptor.js';
|
|
7
7
|
import { setupInterceptor } from './interceptor.js';
|
|
8
8
|
import { extractApiErrorMessage, formatProxyRequestError } from './lib/proxy-errors.js';
|
|
9
|
+
import { getProxyDispatcher } from './lib/proxy-env.js';
|
|
9
10
|
import { getClaudeConfigDir } from '../findcc.js';
|
|
10
11
|
|
|
11
12
|
// Setup interceptor to patch fetch
|
|
12
13
|
setupInterceptor();
|
|
13
14
|
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
15
|
+
// 强制上游返回未压缩响应,取代仅剥 zstd 的旧策略。
|
|
16
|
+
// 原因:链路中的网关/代理(典型是本地 MITM 网络代理)可能把上游的压缩 body 原样透传,
|
|
17
|
+
// 却把 content-encoding 响应头剥掉。undici 看不到 content-encoding 就不会解压,于是把一坨
|
|
18
|
+
// gzip 字节当明文交回;本代理再把它当 SSE 透传给 Claude CLI →
|
|
19
|
+
// "API returned an empty or malformed response (HTTP 200)"。
|
|
20
|
+
// 让上游直接不压缩,整条链路就没有可被剥离/错配的 content-encoding,从根上消除这类问题。
|
|
21
|
+
export function forceIdentityAcceptEncoding(headers) {
|
|
18
22
|
if (!headers) return headers;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.map(s => s.trim())
|
|
26
|
-
.filter(s => s && !/^zstd(\s*;.*)?$/i.test(s))
|
|
27
|
-
.join(', ');
|
|
28
|
-
return { ...headers, [key]: filtered || 'gzip, deflate, br' };
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const k of Object.keys(headers)) {
|
|
25
|
+
if (k.toLowerCase() !== 'accept-encoding') out[k] = headers[k];
|
|
26
|
+
}
|
|
27
|
+
out['accept-encoding'] = 'identity';
|
|
28
|
+
return out;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// 代理会改写请求 body(interceptor 的模型替换 JSON.parse→改 model→JSON.stringify),
|
|
@@ -97,7 +97,7 @@ export function startProxy() {
|
|
|
97
97
|
// Convert incoming headers
|
|
98
98
|
let headers = { ...req.headers };
|
|
99
99
|
delete headers.host; // Let fetch set the host
|
|
100
|
-
headers =
|
|
100
|
+
headers = forceIdentityAcceptEncoding(headers); // 让上游不压缩,规避网关剥 content-encoding 头导致的 body 错配
|
|
101
101
|
headers = stripContentLengthHeader(headers); // body 会被改写,旧 content-length 必须丢弃
|
|
102
102
|
|
|
103
103
|
const buffers = [];
|
|
@@ -119,6 +119,14 @@ export function startProxy() {
|
|
|
119
119
|
fetchOptions.body = body;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// 走用户的网络代理:Node 内置全局 fetch 既不读 http_proxy/https_proxy,也看不到
|
|
123
|
+
// userland undici 的 setGlobalDispatcher,必须把代理 dispatcher 显式传进来,否则
|
|
124
|
+
// 上游请求会绕过代理直连 api.anthropic.com(详见 lib/proxy-env.js 注释)。
|
|
125
|
+
const proxyDispatcher = getProxyDispatcher();
|
|
126
|
+
if (proxyDispatcher) {
|
|
127
|
+
fetchOptions.dispatcher = proxyDispatcher;
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
// 拼接完整 URL,保留 originalBaseUrl 中的路径前缀
|
|
123
131
|
const cleanBase = originalBaseUrl.endsWith('/') ? originalBaseUrl.slice(0, -1) : originalBaseUrl;
|
|
124
132
|
const cleanReq = req.url.startsWith('/') ? req.url.slice(1) : req.url;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ultraplan 预设专家路由 —— 枚举随包发布的 ultraAgents/*.json 供前端「添加预设专家」弹窗使用。
|
|
2
|
+
// 无参数、不读项目目录:listUltraAgents() 默认 dir = ULTRA_AGENTS_DIR(包内置目录),
|
|
3
|
+
// 不接受任何 query/body,杜绝路径穿越与越权读取被查看项目的风险。
|
|
4
|
+
import { listUltraAgents } from '../lib/ultra-agents-api.js';
|
|
5
|
+
|
|
6
|
+
async function ultraAgentsList(req, res) {
|
|
7
|
+
try {
|
|
8
|
+
const agents = listUltraAgents();
|
|
9
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10
|
+
res.end(JSON.stringify({ ok: true, agents }));
|
|
11
|
+
} catch (err) {
|
|
12
|
+
console.error('[api/ultra-agents]', err);
|
|
13
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
14
|
+
res.end(JSON.stringify({ error: 'internal_error' }));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ultraAgentsRoutes = [
|
|
19
|
+
{ method: 'GET', match: 'exact', path: '/api/ultra-agents', handler: ultraAgentsList },
|
|
20
|
+
];
|
package/server/server.js
CHANGED
|
@@ -21,6 +21,7 @@ import { pluginsRoutes } from './routes/plugins.js';
|
|
|
21
21
|
import { logsRoutes } from './routes/logs.js';
|
|
22
22
|
import { voicePackRoutes } from './routes/voice-pack.js';
|
|
23
23
|
import { skillsRoutes } from './routes/skills.js';
|
|
24
|
+
import { ultraAgentsRoutes } from './routes/ultra-agents.js';
|
|
24
25
|
import { filesContentRoutes } from './routes/files-content.js';
|
|
25
26
|
import { filesFsRoutes } from './routes/files-fs.js';
|
|
26
27
|
import { workspacesRoutes } from './routes/workspaces.js';
|
|
@@ -510,6 +511,7 @@ const _routes = [
|
|
|
510
511
|
...logsRoutes,
|
|
511
512
|
...voicePackRoutes,
|
|
512
513
|
...skillsRoutes,
|
|
514
|
+
...ultraAgentsRoutes,
|
|
513
515
|
...filesContentRoutes,
|
|
514
516
|
...filesFsRoutes,
|
|
515
517
|
...workspacesRoutes,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# ultraAgents — 预设专家 (Preset ultraplan experts)
|
|
2
|
+
|
|
3
|
+
本目录随 cc-viewer 包发布。其中每个 `*.json` 文件定义一个「预设专家」,会出现在
|
|
4
|
+
**UltraPlan → 自定义专家编辑器 → 「载入模版」** 弹窗中,供用户选中后将名称与内容
|
|
5
|
+
一键载入编辑面板(再按需改写并另存为自己的自定义专家)。
|
|
6
|
+
|
|
7
|
+
> 加载入口:服务端 `GET /api/ultra-agents`(只读本目录、无参数),实现见
|
|
8
|
+
> `server/lib/ultra-agents-api.js`。本目录中的非 `*.json` 文件(如本 README)会被忽略。
|
|
9
|
+
|
|
10
|
+
## JSON 格式
|
|
11
|
+
|
|
12
|
+
```jsonc
|
|
13
|
+
{
|
|
14
|
+
"id": "code-expert", // 必填。唯一标识,仅 [A-Za-z0-9._-]、长度 ≤200、不以 . 开头。
|
|
15
|
+
// 仅作去重键(同 id 取文件名排序靠前者),不参与拼路径。
|
|
16
|
+
"version": 1, // 可选。前向兼容标记,当前加载器忽略(不读不校验)。
|
|
17
|
+
"title": { "zh": "代码专家", "en": "Code Expert" }, // 必填。专家名称,内联本地化,见下。
|
|
18
|
+
"description": { "zh": "资深工程师…", "en": "Senior engineer…" }, // 可选。一句话描述,内联本地化。
|
|
19
|
+
"content": "<system-reminder>\n…\n</system-reminder>" // 必填。单语言正文,见下「content」。
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### title / description:在 JSON 协议层内联本地化
|
|
24
|
+
|
|
25
|
+
`title` 与 `description` 两个字段都支持两种写法,**本地化在 JSON 协议层内联完成**(不依赖外部 i18n):
|
|
26
|
+
|
|
27
|
+
- **纯字符串**:所有语言都用同一份文本。
|
|
28
|
+
- **本地化对象** `{ "zh": "…", "en": "…", "zh-TW": "…" }`:前端按当前界面语言解析,
|
|
29
|
+
回退顺序为 **精确语言 → 去区域的主语言(`zh-TW`→`zh`、`pt-BR`→`pt`)→ `en` → `zh`
|
|
30
|
+
→ 首个非空值**(解析逻辑见 `src/utils/resolveLocalized.js`)。
|
|
31
|
+
|
|
32
|
+
因此一个文件即可覆盖任意多种语言;未列出的界面语言走上述回退。
|
|
33
|
+
|
|
34
|
+
### content:单语言
|
|
35
|
+
|
|
36
|
+
`content` 是**单语言字符串**(不做本地化)。载入并保存后,发送给 Claude Code 时被当作 ultraplan
|
|
37
|
+
的作用域指令;若 `content`(trim 后)以 `<system-reminder>` 开头,则原样使用、**不会被二次包裹**
|
|
38
|
+
(见 `buildCustomTemplate`,`src/utils/ultraplanTemplates.js`)。因此预设 `content` 建议以
|
|
39
|
+
`<system-reminder>`、`[SCOPED INSTRUCTION]` 风格书写。
|
|
40
|
+
|
|
41
|
+
> 内置 `code-expert` / `research-expert` 的 `content` **直接取自**
|
|
42
|
+
> `src/utils/ultraplanTemplates.js` 的 `ULTRAPLAN_VARIANTS.codeExpert` / `researchExpert`,
|
|
43
|
+
> 并由 `test/ultra-agents-api.test.js` 钉死逐字节一致——改正文请改那个源文件后重新生成本目录 JSON,
|
|
44
|
+
> 不要在此手写另一份。
|
|
45
|
+
|
|
46
|
+
## 校验与限制
|
|
47
|
+
|
|
48
|
+
加载时对每个文件做防御性校验,不合法者仅 `console.warn` 跳过、不影响其它文件:
|
|
49
|
+
|
|
50
|
+
- 必须是合法 JSON 的**普通对象**(非数组/标量)。
|
|
51
|
+
- `id` 通过上述规则;`title`、`content` 为「非空字符串」或「至少含一个非空字符串值的对象」。
|
|
52
|
+
- 单文件 **≤ 256KB**,超出跳过;有效专家 **≤ 100** 个,超出忽略。
|
|
53
|
+
- `description` 缺失或非法时按空串处理。
|
|
54
|
+
|
|
55
|
+
## 现有 demo
|
|
56
|
+
|
|
57
|
+
| 文件 | 专家 | `title` / `description` | `content` 来源 |
|
|
58
|
+
| --- | --- | --- | --- |
|
|
59
|
+
| `code-expert.json` | 代码专家 / Code Expert | 内联本地化(全 18 语言) | `ULTRAPLAN_VARIANTS.codeExpert` |
|
|
60
|
+
| `research-expert.json` | 调研专家 / Research Expert | 内联本地化(全 18 语言) | `ULTRAPLAN_VARIANTS.researchExpert` |
|
|
61
|
+
|
|
62
|
+
新增预设:在本目录放一个新的 `*.json`(建议文件名与 `id` 一致),重启/刷新即可在弹窗中看到。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "code-expert",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"title": {
|
|
5
|
+
"zh": "代码专家",
|
|
6
|
+
"en": "Code Expert",
|
|
7
|
+
"zh-TW": "程式碼專家",
|
|
8
|
+
"ko": "코드 전문가",
|
|
9
|
+
"ja": "コード専門家",
|
|
10
|
+
"de": "Code-Experte",
|
|
11
|
+
"es": "Experto en código",
|
|
12
|
+
"fr": "Expert en code",
|
|
13
|
+
"it": "Esperto di codice",
|
|
14
|
+
"da": "Kodeekspert",
|
|
15
|
+
"pl": "Ekspert ds. kodu",
|
|
16
|
+
"ru": "Эксперт по коду",
|
|
17
|
+
"ar": "خبير الأكواد",
|
|
18
|
+
"no": "Kodeekspert",
|
|
19
|
+
"pt-BR": "Especialista em código",
|
|
20
|
+
"th": "ผู้เชี่ยวชาญด้านโค้ด",
|
|
21
|
+
"tr": "Kod uzmanı",
|
|
22
|
+
"uk": "Експерт із коду"
|
|
23
|
+
},
|
|
24
|
+
"description": {
|
|
25
|
+
"zh": "资深工程师:多智能体探索 + 计划评审 + 高质量实现,专注代码审查、重构、性能与可维护性。",
|
|
26
|
+
"en": "Senior engineer: multi-agent exploration, plan review and high-quality implementation — code review, refactor, performance, maintainability.",
|
|
27
|
+
"zh-TW": "資深工程師:多智能體探索 + 計畫評審 + 高品質實作,專注程式碼審查、重構、效能與可維護性。",
|
|
28
|
+
"ko": "시니어 엔지니어: 멀티 에이전트 탐색 + 계획 검토 + 고품질 구현 — 코드 리뷰, 리팩터링, 성능, 유지보수성에 집중.",
|
|
29
|
+
"ja": "シニアエンジニア:マルチエージェント探索 + 計画レビュー + 高品質な実装 — コードレビュー、リファクタリング、性能、保守性に注力。",
|
|
30
|
+
"de": "Senior-Ingenieur: Multi-Agent-Exploration, Planprüfung und hochwertige Umsetzung – Code-Review, Refactoring, Performance, Wartbarkeit.",
|
|
31
|
+
"es": "Ingeniero sénior: exploración multiagente, revisión de planes e implementación de alta calidad: revisión de código, refactorización, rendimiento y mantenibilidad.",
|
|
32
|
+
"fr": "Ingénieur senior : exploration multi-agents, revue de plan et implémentation de haute qualité — revue de code, refactorisation, performance, maintenabilité.",
|
|
33
|
+
"it": "Ingegnere senior: esplorazione multi-agente, revisione del piano e implementazione di alta qualità — revisione del codice, refactoring, prestazioni, manutenibilità.",
|
|
34
|
+
"da": "Senioringeniør: multi-agent-udforskning, plangennemgang og implementering af høj kvalitet – kodegennemgang, refaktorering, ydeevne, vedligeholdelse.",
|
|
35
|
+
"pl": "Starszy inżynier: eksploracja wieloagentowa, przegląd planu i wysokiej jakości implementacja — przegląd kodu, refaktoryzacja, wydajność, łatwość utrzymania.",
|
|
36
|
+
"ru": "Старший инженер: многоагентное исследование, ревью плана и качественная реализация — ревью кода, рефакторинг, производительность, поддерживаемость.",
|
|
37
|
+
"ar": "مهندس أول: استكشاف متعدد الوكلاء، ومراجعة الخطة، وتنفيذ عالي الجودة — مراجعة الشيفرة وإعادة الهيكلة والأداء وقابلية الصيانة.",
|
|
38
|
+
"no": "Senioringeniør: multiagent-utforsking, plangjennomgang og implementering av høy kvalitet – kodegjennomgang, refaktorering, ytelse, vedlikeholdbarhet.",
|
|
39
|
+
"pt-BR": "Engenheiro sênior: exploração multiagente, revisão de plano e implementação de alta qualidade — revisão de código, refatoração, desempenho e manutenibilidade.",
|
|
40
|
+
"th": "วิศวกรอาวุโส: การสำรวจแบบหลายเอเจนต์ + การรีวิวแผน + การพัฒนาคุณภาพสูง — เน้นการรีวิวโค้ด รีแฟกเตอร์ ประสิทธิภาพ และการบำรุงรักษา",
|
|
41
|
+
"tr": "Kıdemli mühendis: çoklu ajan keşfi, plan incelemesi ve yüksek kaliteli uygulama — kod incelemesi, yeniden düzenleme, performans, sürdürülebilirlik.",
|
|
42
|
+
"uk": "Senior-інженер: багатоагентне дослідження, рев'ю плану та якісна реалізація — рев'ю коду, рефакторинг, продуктивність, супровід."
|
|
43
|
+
},
|
|
44
|
+
"content": "<system-reminder>\n[SCOPED INSTRUCTION] The following instructions apply only to the next 1–3 interactions. Once the task is complete, these instructions should gradually decrease in priority and no longer affect subsequent interactions. You should be adept at utilizing tools such as `AskUserQuestion`, `EnterPlanMode`, and `TeamCreate`, rather than relying solely on plain text processing.\n\nPre-requisite: Use `AskUserQuestion` to clarify user intent whenever the request is ambiguous (target element, interaction style, scope of platforms, etc.). Skip only if the intent is unambiguous.\n\nLeverage a multi-agent exploration mechanism to formulate a highly detailed implementation plan.\n\nInstructions:\n1. Use the `Agent` tool to spawn parallel agents that simultaneously explore different aspects of the codebase:\n- If necessary, assign a preliminary researcher to use the `webSearch` tool to first investigate cutting-edge solutions in the relevant industry domain;\n- One agent responsible for understanding the relevant existing code and architecture;\n- One agent responsible for identifying all files that need to be modified;\n- One agent responsible for identifying potential risks, edge cases, and dependencies;\n- You may add other roles or deploy additional agents beyond the three listed above; the maximum number of concurrently dispatched agents is 5.\n\n2. Synthesize the findings from all agents into a detailed, step-by-step implementation plan.\n\n3. Use the `Agent` tool to spawn 2-3 review agents that examine the plan from different perspectives, checking for missing steps, potential risks, or corresponding mitigation strategies.\n\n4. Integrate the feedback gathered during the review process, then call `ExitPlanMode` to submit your final plan.\n\n5. Once `ExitPlanMode` returns a result:\n- If approved: proceed to execute the plan within this session.\n- If rejected: revise the plan based on the feedback provided and call `ExitPlanMode` again.\n- If an error occurs (including receiving a \"Not in Plan Mode\" message): do **not** follow the suggestions provided in the error message; instead, prompt the user for further instructions.\n\nYour final plan must include the following elements:\n- A clear summary of the implementation strategy;\n- An ordered list of files to be created or modified, with precise details of the required changes for each file;\n- A step-by-step execution sequence;\n- Testing and validation procedures;\n- Potential risks and their corresponding mitigation strategies;\n\n6. After the final plan has been successfully executed:\nFirst run `git diff --quiet && git diff --cached --quiet` (or equivalent) to detect whether the working tree actually has non-trivial changes; if there are no real changes (or only whitespace/comment-only edits), skip the UltraReview step.\nOtherwise, if the project is managed with Git:\nInitiate a team (`TeamCreate`), dynamically allocating the number of teammates based on task complexity (5 is recommended);\nTask: Conduct a Code Review of the current git changes from multiple perspectives;\nPre-requisites:\n- The git repository may be located in a subdirectory of the current directory; prefer `git rev-parse --show-toplevel` (fall back to recursive lookup) before proceeding;\n- In the case of multiple repositories, tasks may be executed separately;\nThe team's goal is to analyze the current Git change log and validate each modification from different perspectives, specifically including:\n- Whether requirements/objectives have been met and functionality is complete;\n- Whether newly added code introduces side effects, breaks existing functionality, or poses potential risks;\n- Code quality: naming, readability, complexity, technical debt, maintainability;\n- Testing and documentation: whether there is adequate test coverage, and whether critical logic has necessary comments or documentation;\n- Dependencies and compatibility: whether new dependencies or version compatibility issues have been introduced;\nWorkflow:\n- Each teammate, according to their own role, covers the review dimensions one by one and independently outputs a report;\n- After consolidating the reports, perform a cross-review to identify conflicts or shared concerns;\n- Distill specific, actionable modification suggestions and annotate them with priority levels (P0/P1/P2/P3);\n- Upon completion, adopt P0 items, and selectively adopt P1 items when they are concrete and low-risk; defer P2/P3 to backlog;\n- After execution is complete, close the team (`TeamDelete`);\n</system-reminder>"
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "research-expert",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"title": {
|
|
5
|
+
"zh": "调研专家",
|
|
6
|
+
"en": "Research Expert",
|
|
7
|
+
"zh-TW": "調研專家",
|
|
8
|
+
"ko": "리서치 전문가",
|
|
9
|
+
"ja": "リサーチ専門家",
|
|
10
|
+
"de": "Recherche-Experte",
|
|
11
|
+
"es": "Experto en investigación",
|
|
12
|
+
"fr": "Expert en recherche",
|
|
13
|
+
"it": "Esperto di ricerca",
|
|
14
|
+
"da": "Researchekspert",
|
|
15
|
+
"pl": "Ekspert ds. badań",
|
|
16
|
+
"ru": "Эксперт по исследованиям",
|
|
17
|
+
"ar": "خبير الأبحاث",
|
|
18
|
+
"no": "Researchekspert",
|
|
19
|
+
"pt-BR": "Especialista em pesquisa",
|
|
20
|
+
"th": "ผู้เชี่ยวชาญด้านการวิจัย",
|
|
21
|
+
"tr": "Araştırma uzmanı",
|
|
22
|
+
"uk": "Експерт із досліджень"
|
|
23
|
+
},
|
|
24
|
+
"description": {
|
|
25
|
+
"zh": "资深调研分析师:多源检索 + 交叉求证 + 结构化产出,适合行业调研、竞品分析与可信报告撰写。",
|
|
26
|
+
"en": "Senior research analyst: multi-source search, cross-verification and structured deliverables — industry research, competitive analysis, credible reports.",
|
|
27
|
+
"zh-TW": "資深調研分析師:多源檢索 + 交叉求證 + 結構化產出,適合行業調研、競品分析與可信報告撰寫。",
|
|
28
|
+
"ko": "시니어 리서치 분석가: 다중 출처 검색 + 교차 검증 + 구조화된 산출물 — 산업 리서치, 경쟁사 분석, 신뢰할 수 있는 보고서.",
|
|
29
|
+
"ja": "シニアリサーチアナリスト:多源検索 + クロス検証 + 構造化された成果物 — 業界調査、競合分析、信頼できるレポート。",
|
|
30
|
+
"de": "Senior-Research-Analyst: Multi-Quellen-Suche, Kreuzverifizierung und strukturierte Ergebnisse – Branchenrecherche, Wettbewerbsanalyse, glaubwürdige Berichte.",
|
|
31
|
+
"es": "Analista de investigación sénior: búsqueda multifuente, verificación cruzada y entregables estructurados: investigación de mercado, análisis de la competencia e informes fiables.",
|
|
32
|
+
"fr": "Analyste de recherche senior : recherche multi-sources, vérification croisée et livrables structurés — étude de marché, analyse concurrentielle, rapports fiables.",
|
|
33
|
+
"it": "Analista di ricerca senior: ricerca multi-fonte, verifica incrociata e deliverable strutturati — ricerca di settore, analisi della concorrenza, report affidabili.",
|
|
34
|
+
"da": "Senior researchanalytiker: multikildesøgning, krydsverificering og strukturerede leverancer – brancheresearch, konkurrentanalyse, troværdige rapporter.",
|
|
35
|
+
"pl": "Starszy analityk badawczy: wyszukiwanie wieloźródłowe, weryfikacja krzyżowa i ustrukturyzowane wyniki — badania branżowe, analiza konkurencji, wiarygodne raporty.",
|
|
36
|
+
"ru": "Старший аналитик-исследователь: многоисточниковый поиск, перекрёстная проверка и структурированные результаты — отраслевые исследования, анализ конкурентов, достоверные отчёты.",
|
|
37
|
+
"ar": "محلل أبحاث أول: بحث متعدد المصادر، وتحقق متقاطع، ومخرجات منظمة — أبحاث السوق وتحليل المنافسين وتقارير موثوقة.",
|
|
38
|
+
"no": "Senior researchanalytiker: multikildesøk, kryssverifisering og strukturerte leveranser – bransjeresearch, konkurrentanalyse, troverdige rapporter.",
|
|
39
|
+
"pt-BR": "Analista de pesquisa sênior: busca multifonte, verificação cruzada e entregáveis estruturados — pesquisa de mercado, análise da concorrência e relatórios confiáveis.",
|
|
40
|
+
"th": "นักวิเคราะห์วิจัยอาวุโส: การค้นหาหลายแหล่ง + การตรวจสอบไขว้ + ผลงานเชิงโครงสร้าง — เหมาะกับการวิจัยอุตสาหกรรม การวิเคราะห์คู่แข่ง และการเขียนรายงานที่น่าเชื่อถือ",
|
|
41
|
+
"tr": "Kıdemli araştırma analisti: çok kaynaklı arama, çapraz doğrulama ve yapılandırılmış çıktılar — sektör araştırması, rekabet analizi, güvenilir raporlar.",
|
|
42
|
+
"uk": "Senior-аналітик досліджень: багатоджерельний пошук, перехресна перевірка та структуровані результати — галузеві дослідження, аналіз конкурентів, надійні звіти."
|
|
43
|
+
},
|
|
44
|
+
"content": "<system-reminder>\n[SCOPED INSTRUCTION] The following instructions are intended for the next 1–3 interactions. Once the task is complete, these instructions should be gradually deprioritized and no longer influence subsequent interactions.You should be adept at utilizing tools such as `AskUserQuestion`, `EnterPlanMode`, `WebSearch` and `TeamCreate`, rather than relying solely on plain text processing.\n\nPre-requisite: Use `AskUserQuestion` to clarify the research scope, target audience, and deliverable format whenever the user's intent is ambiguous. Skip only if the intent is unambiguous.\n\nLeverage a multi-agent exploration mechanism to formulate an exceptionally detailed implementation plan.\n\nInstructions:\n1. Utilize the Agent tool to spawn parallel agents that simultaneously explore various facets of the requirements:\n- If necessary, deploy a preliminary investigator to conduct an initial survey of industry-specific solutions using `webSearch`;\n- If necessary, deploy a specialized investigator to research authoritative sources—such as academic papers, news articles, and research reports—using `webSearch`;\n- Assign an agent to synthesize the target solution, while simultaneously verifying the rigor and credibility of the gathered papers, news, and research reports;\n- If necessary, assign an agent to analyze competitor data to provide supplementary analytical perspectives;\n- If necessary, assign an agent to handle the implementation of a product demo (generating outputs such as HTML, Markdown, etc.);\n- If the task is sufficiently complex, you may assign additional teammates to the roles defined above, or introduce other specialized roles; you are permitted to schedule up to 5 teammates concurrently.\n\n2. Synthesize the findings from the aforementioned agents into a comprehensive, step-by-step implementation plan.\n\n3. Utilize the Agent tool to spawn a set of parallel review agents; these agents shall scrutinize the plan from multiple roles and perspectives to identify any omitted steps and to propose reasonable additions or optimizations.\n\n4. Consolidate the feedback received from the review agents, then invoke `ExitPlanMode` to submit your final plan.\n\n5. Upon receiving the result from `ExitPlanMode`:\n- If Approved: Proceed to execute the plan within this current session.\n- If Rejected: Revise the plan based on the provided feedback, and then invoke `ExitPlanMode` once again.\n- If an Error Occurs (including the message \"Not in Plan Mode\"): Do *not* follow the suggestions provided by the error message; instead, prompt the user for further instructions.\n\nYour final plan must include the following elements:\n- A clear summary of the proposed implementation strategy;\n- An ordered list of files to be created or modified, specifying the exact changes required for each;\n- A step-by-step sequence for executing the implementation;\n- Identification of potential risks and corresponding mitigation strategies;\n- Creative ideation and suggestions for advanced enhancements;\n- If a product demo was generated, place the corresponding demo output in an appropriate location and notify the user.\n</system-reminder>"
|
|
45
|
+
}
|