u1s1-cli 0.3.0 → 0.5.0
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 +1 -0
- package/dist/brand.js +56 -20
- package/dist/import/claude.js +372 -0
- package/dist/import/codex.js +362 -0
- package/dist/import/index.js +354 -0
- package/dist/import/types.js +1 -0
- package/dist/import/util.js +231 -0
- package/dist/import/write.js +95 -0
- package/dist/index.js +26 -10
- package/dist/login.js +6 -3
- package/dist/style.js +6 -284
- package/package.json +5 -4
- package/dist/themes.js +0 -175
package/dist/index.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { printConsoleBanner } from "./brand.js";
|
|
7
7
|
import { agentDir, loadConfig, MODELS, persistPreferredModel, PROVIDER_ID, resolvePreferredModel, } from "./config.js";
|
|
8
8
|
import { applyBrandUi } from "./style.js";
|
|
9
|
-
import { DEFAULT_THEME_SETTING, ensureBrandThemes } from "./themes.js";
|
|
10
9
|
const require = createRequire(import.meta.url);
|
|
11
10
|
const VERSION = require("../package.json").version;
|
|
12
11
|
const BRAND_APPEND = `## u1s1
|
|
@@ -39,19 +38,31 @@ function ensureDefaultSettings() {
|
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
let changed = false;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
// Pi default is one-at-a-time (one queued message per turn). u1s1 delivers
|
|
42
|
+
// all queued messages together so burst typing isn't split across turns.
|
|
43
|
+
// Only fill in missing keys so an explicit /settings choice still sticks.
|
|
44
|
+
for (const key of ["steeringMode", "followUpMode"]) {
|
|
45
|
+
if (!(key in settings)) {
|
|
46
|
+
settings[key] = "all";
|
|
47
|
+
changed = true;
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
// ≤0.4.0 shipped branded themes and forced them as default; the files are
|
|
51
|
+
// gone now, so a settings.json still pointing at them must fall back to
|
|
52
|
+
// pi's default theme.
|
|
53
|
+
if (typeof settings.theme === "string" && settings.theme.includes("u1s1-")) {
|
|
54
|
+
delete settings.theme;
|
|
50
55
|
changed = true;
|
|
51
56
|
}
|
|
52
57
|
if (changed)
|
|
53
58
|
writeFileSync(p, JSON.stringify(settings, null, 2) + "\n");
|
|
54
59
|
}
|
|
60
|
+
/** ≤0.4.0 wrote u1s1-dark/u1s1-light into the pi themes dir; remove them. */
|
|
61
|
+
function cleanupBrandThemes() {
|
|
62
|
+
for (const name of ["u1s1-dark", "u1s1-light"]) {
|
|
63
|
+
rmSync(join(agentDir, "themes", `${name}.json`), { force: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
55
66
|
/**
|
|
56
67
|
* tmux 默认不转发键盘修饰键,会把 Shift+Enter 当成普通回车发出去,消息没写完就被发送。
|
|
57
68
|
* 只开 extended-keys 不够:默认 terminal-features 里 xterm* 没有 extkeys,tmux 根本不会
|
|
@@ -93,7 +104,7 @@ function ensureTmuxKeyboardProtocol() {
|
|
|
93
104
|
run(["bind-key", "-n", "C-Enter", "send-keys", "-l", "\x1b[13;5u"]);
|
|
94
105
|
}
|
|
95
106
|
async function runAgent(cfg, args) {
|
|
96
|
-
|
|
107
|
+
cleanupBrandThemes();
|
|
97
108
|
ensureBrandPrompt();
|
|
98
109
|
ensureDefaultSettings();
|
|
99
110
|
ensureTmuxKeyboardProtocol();
|
|
@@ -206,6 +217,11 @@ async function run() {
|
|
|
206
217
|
await update();
|
|
207
218
|
return;
|
|
208
219
|
}
|
|
220
|
+
if (cmd === "import") {
|
|
221
|
+
const { importCommand } = await import("./import/index.js");
|
|
222
|
+
await importCommand(args.slice(1));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
209
225
|
const { ensureAuth } = await import("./login.js");
|
|
210
226
|
const cfg = await ensureAuth();
|
|
211
227
|
await runAgent(cfg, args);
|
package/dist/login.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { createInterface } from "node:readline/promises";
|
|
2
3
|
import { fetchMe } from "./api.js";
|
|
4
|
+
import { DASHBOARD_URL, printConsoleBanner } from "./brand.js";
|
|
3
5
|
import { loadConfig, saveConfig } from "./config.js";
|
|
4
|
-
const
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const VERSION = require("../package.json").version;
|
|
5
8
|
function tryOpenBrowser(url) {
|
|
6
9
|
import("node:child_process")
|
|
7
10
|
.then(({ spawn }) => {
|
|
@@ -14,8 +17,8 @@ export async function login(keyArg) {
|
|
|
14
17
|
const cfg = loadConfig();
|
|
15
18
|
let key = keyArg?.trim();
|
|
16
19
|
if (!key) {
|
|
17
|
-
|
|
18
|
-
console.log("
|
|
20
|
+
printConsoleBanner(VERSION);
|
|
21
|
+
console.log(" 需要一把 API Key(免费):");
|
|
19
22
|
console.log(` 1. 打开 ${DASHBOARD_URL} 注册/登录(30 秒,送 $10 额度)`);
|
|
20
23
|
console.log(" 2. 复制你的 API Key,粘贴到下面");
|
|
21
24
|
console.log("");
|
package/dist/style.js
CHANGED
|
@@ -1,299 +1,21 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
1
|
import { basename } from "node:path";
|
|
3
|
-
import {
|
|
2
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
4
3
|
import { renderBrandHeader } from "./brand.js";
|
|
5
|
-
function row(text) {
|
|
6
|
-
return {
|
|
7
|
-
render() {
|
|
8
|
-
return text.split("\n");
|
|
9
|
-
},
|
|
10
|
-
invalidate() { },
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
function shortPath(path) {
|
|
14
|
-
if (typeof path !== "string" || !path)
|
|
15
|
-
return "";
|
|
16
|
-
const home = homedir();
|
|
17
|
-
if (path === home)
|
|
18
|
-
return "~";
|
|
19
|
-
if (path.startsWith(`${home}/`) || path.startsWith(`${home}\\`)) {
|
|
20
|
-
return `~${path.slice(home.length)}`;
|
|
21
|
-
}
|
|
22
|
-
const cwd = process.cwd();
|
|
23
|
-
if (path === cwd)
|
|
24
|
-
return ".";
|
|
25
|
-
if (path.startsWith(`${cwd}/`) || path.startsWith(`${cwd}\\`)) {
|
|
26
|
-
return path.slice(cwd.length + 1);
|
|
27
|
-
}
|
|
28
|
-
return path;
|
|
29
|
-
}
|
|
30
|
-
function asString(value) {
|
|
31
|
-
return typeof value === "string" ? value : "";
|
|
32
|
-
}
|
|
33
|
-
function clip(text, max = 72) {
|
|
34
|
-
if (text.length <= max)
|
|
35
|
-
return text;
|
|
36
|
-
return `${text.slice(0, max - 1)}…`;
|
|
37
|
-
}
|
|
38
|
-
function callLine(theme, name, detail) {
|
|
39
|
-
let text = `${theme.fg("accent", "⏺")} ${theme.bold(theme.fg("toolTitle", name))}`;
|
|
40
|
-
if (detail)
|
|
41
|
-
text += ` ${theme.fg("muted", clip(detail))}`;
|
|
42
|
-
return text;
|
|
43
|
-
}
|
|
44
|
-
function resultLine(theme, text, color = "dim") {
|
|
45
|
-
return ` ${theme.fg("dim", "⎿")} ${theme.fg(color, text)}`;
|
|
46
|
-
}
|
|
47
|
-
function indentOutput(theme, output, limit = 20) {
|
|
48
|
-
const all = output.split("\n");
|
|
49
|
-
const shown = all.slice(0, limit);
|
|
50
|
-
let text = shown.map((line) => ` ${theme.fg("dim", line)}`).join("\n");
|
|
51
|
-
if (all.length > limit) {
|
|
52
|
-
text += `\n ${theme.fg("muted", `… ${all.length - limit} 行`)}`;
|
|
53
|
-
}
|
|
54
|
-
return text;
|
|
55
|
-
}
|
|
56
|
-
const toolCache = new Map();
|
|
57
|
-
function createBuiltInTools(cwd) {
|
|
58
|
-
return {
|
|
59
|
-
read: createReadTool(cwd),
|
|
60
|
-
bash: createBashTool(cwd),
|
|
61
|
-
edit: createEditTool(cwd),
|
|
62
|
-
write: createWriteTool(cwd),
|
|
63
|
-
find: createFindTool(cwd),
|
|
64
|
-
grep: createGrepTool(cwd),
|
|
65
|
-
ls: createLsTool(cwd),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function toolsFor(cwd) {
|
|
69
|
-
let cached = toolCache.get(cwd);
|
|
70
|
-
if (!cached) {
|
|
71
|
-
cached = createBuiltInTools(cwd);
|
|
72
|
-
toolCache.set(cwd, cached);
|
|
73
|
-
}
|
|
74
|
-
return cached;
|
|
75
|
-
}
|
|
76
4
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
5
|
+
* Brand chrome is just the startup hero + window title; everything else
|
|
6
|
+
* (tool rows, thinking blocks, spinner) stays Pi's default UI.
|
|
79
7
|
*/
|
|
80
8
|
export function applyBrandUi(pi, version) {
|
|
81
9
|
pi.on("session_start", async (_event, ctx) => {
|
|
82
10
|
if (ctx.mode !== "tui")
|
|
83
11
|
return;
|
|
84
12
|
ctx.ui.setHeader((_tui, theme) => ({
|
|
85
|
-
render() {
|
|
86
|
-
|
|
13
|
+
render(width) {
|
|
14
|
+
// pi-tui crashes on lines wider than the terminal, so truncate defensively.
|
|
15
|
+
return renderBrandHeader(theme, version, process.cwd(), width).map((line) => truncateToWidth(line, width));
|
|
87
16
|
},
|
|
88
17
|
invalidate() { },
|
|
89
18
|
}));
|
|
90
|
-
const theme = ctx.ui.theme;
|
|
91
|
-
ctx.ui.setWorkingMessage("思考中");
|
|
92
|
-
ctx.ui.setWorkingIndicator({
|
|
93
|
-
frames: [
|
|
94
|
-
theme.fg("dim", "·"),
|
|
95
|
-
theme.fg("muted", "•"),
|
|
96
|
-
theme.fg("accent", "●"),
|
|
97
|
-
theme.fg("muted", "•"),
|
|
98
|
-
],
|
|
99
|
-
intervalMs: 140,
|
|
100
|
-
});
|
|
101
|
-
ctx.ui.setHiddenThinkingLabel("思考中");
|
|
102
19
|
ctx.ui.setTitle(`u1s1 — ${basename(process.cwd())}`);
|
|
103
20
|
});
|
|
104
|
-
const cwd = process.cwd();
|
|
105
|
-
const originals = toolsFor(cwd);
|
|
106
|
-
pi.registerTool({
|
|
107
|
-
name: "read",
|
|
108
|
-
label: "read",
|
|
109
|
-
description: originals.read.description,
|
|
110
|
-
parameters: originals.read.parameters,
|
|
111
|
-
renderShell: "self",
|
|
112
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
113
|
-
return toolsFor(ctx.cwd).read.execute(toolCallId, params, signal, onUpdate);
|
|
114
|
-
},
|
|
115
|
-
renderCall(args, theme) {
|
|
116
|
-
return row(callLine(theme, "Read", shortPath(args.path)));
|
|
117
|
-
},
|
|
118
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
119
|
-
if (isPartial)
|
|
120
|
-
return row(resultLine(theme, "读取中…", "warning"));
|
|
121
|
-
const content = result.content[0];
|
|
122
|
-
if (content?.type === "image")
|
|
123
|
-
return row(resultLine(theme, "图片", "success"));
|
|
124
|
-
if (content?.type !== "text")
|
|
125
|
-
return row(resultLine(theme, "无内容", "error"));
|
|
126
|
-
const details = result.details;
|
|
127
|
-
const count = content.text.split("\n").length;
|
|
128
|
-
let text = resultLine(theme, `${count} 行`, "success");
|
|
129
|
-
if (details?.truncation?.truncated)
|
|
130
|
-
text += theme.fg("warning", " · 已截断");
|
|
131
|
-
if (expanded)
|
|
132
|
-
text += `\n${indentOutput(theme, content.text)}`;
|
|
133
|
-
return row(text);
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
pi.registerTool({
|
|
137
|
-
name: "bash",
|
|
138
|
-
label: "bash",
|
|
139
|
-
description: originals.bash.description,
|
|
140
|
-
parameters: originals.bash.parameters,
|
|
141
|
-
renderShell: "self",
|
|
142
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
143
|
-
return toolsFor(ctx.cwd).bash.execute(toolCallId, params, signal, onUpdate);
|
|
144
|
-
},
|
|
145
|
-
renderCall(args, theme) {
|
|
146
|
-
return row(callLine(theme, "Bash", asString(args.command)));
|
|
147
|
-
},
|
|
148
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
149
|
-
if (isPartial)
|
|
150
|
-
return row(resultLine(theme, "执行中…", "warning"));
|
|
151
|
-
const content = result.content[0];
|
|
152
|
-
const output = content?.type === "text" ? content.text : "";
|
|
153
|
-
const exitMatch = output.match(/exit code: (\d+)/);
|
|
154
|
-
const exitCode = exitMatch ? Number(exitMatch[1]) : null;
|
|
155
|
-
const details = result.details;
|
|
156
|
-
const ok = exitCode === 0 || exitCode === null;
|
|
157
|
-
let text = resultLine(theme, ok ? "完成" : `退出码 ${exitCode}`, ok ? "success" : "error");
|
|
158
|
-
if (details?.truncation?.truncated)
|
|
159
|
-
text += theme.fg("warning", " · 已截断");
|
|
160
|
-
if (expanded && output)
|
|
161
|
-
text += `\n${indentOutput(theme, output)}`;
|
|
162
|
-
return row(text);
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
pi.registerTool({
|
|
166
|
-
name: "edit",
|
|
167
|
-
label: "edit",
|
|
168
|
-
description: originals.edit.description,
|
|
169
|
-
parameters: originals.edit.parameters,
|
|
170
|
-
renderShell: "self",
|
|
171
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
172
|
-
return toolsFor(ctx.cwd).edit.execute(toolCallId, params, signal, onUpdate);
|
|
173
|
-
},
|
|
174
|
-
renderCall(args, theme) {
|
|
175
|
-
return row(callLine(theme, "Edit", shortPath(args.path)));
|
|
176
|
-
},
|
|
177
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
178
|
-
if (isPartial)
|
|
179
|
-
return row(resultLine(theme, "修改中…", "warning"));
|
|
180
|
-
const content = result.content[0];
|
|
181
|
-
if (content?.type === "text" && content.text.startsWith("Error")) {
|
|
182
|
-
return row(resultLine(theme, content.text.split("\n")[0] ?? "失败", "error"));
|
|
183
|
-
}
|
|
184
|
-
const details = result.details;
|
|
185
|
-
if (!details?.diff)
|
|
186
|
-
return row(resultLine(theme, "已写入", "success"));
|
|
187
|
-
let additions = 0;
|
|
188
|
-
let removals = 0;
|
|
189
|
-
for (const line of details.diff.split("\n")) {
|
|
190
|
-
if (line.startsWith("+") && !line.startsWith("+++"))
|
|
191
|
-
additions++;
|
|
192
|
-
if (line.startsWith("-") && !line.startsWith("---"))
|
|
193
|
-
removals++;
|
|
194
|
-
}
|
|
195
|
-
let text = resultLine(theme, `+${additions} / -${removals}`, "success");
|
|
196
|
-
if (expanded)
|
|
197
|
-
text += `\n${indentOutput(theme, details.diff, 30)}`;
|
|
198
|
-
return row(text);
|
|
199
|
-
},
|
|
200
|
-
});
|
|
201
|
-
pi.registerTool({
|
|
202
|
-
name: "write",
|
|
203
|
-
label: "write",
|
|
204
|
-
description: originals.write.description,
|
|
205
|
-
parameters: originals.write.parameters,
|
|
206
|
-
renderShell: "self",
|
|
207
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
208
|
-
return toolsFor(ctx.cwd).write.execute(toolCallId, params, signal, onUpdate);
|
|
209
|
-
},
|
|
210
|
-
renderCall(args, theme) {
|
|
211
|
-
const count = asString(args.content).split("\n").length;
|
|
212
|
-
const detail = `${shortPath(args.path)}${count ? ` (${count} 行)` : ""}`;
|
|
213
|
-
return row(callLine(theme, "Write", detail));
|
|
214
|
-
},
|
|
215
|
-
renderResult(result, { isPartial }, theme) {
|
|
216
|
-
if (isPartial)
|
|
217
|
-
return row(resultLine(theme, "写入中…", "warning"));
|
|
218
|
-
const content = result.content[0];
|
|
219
|
-
if (content?.type === "text" && content.text.startsWith("Error")) {
|
|
220
|
-
return row(resultLine(theme, content.text.split("\n")[0] ?? "失败", "error"));
|
|
221
|
-
}
|
|
222
|
-
return row(resultLine(theme, "已写入", "success"));
|
|
223
|
-
},
|
|
224
|
-
});
|
|
225
|
-
pi.registerTool({
|
|
226
|
-
name: "grep",
|
|
227
|
-
label: "grep",
|
|
228
|
-
description: originals.grep.description,
|
|
229
|
-
parameters: originals.grep.parameters,
|
|
230
|
-
renderShell: "self",
|
|
231
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
232
|
-
return toolsFor(ctx.cwd).grep.execute(toolCallId, params, signal, onUpdate);
|
|
233
|
-
},
|
|
234
|
-
renderCall(args, theme) {
|
|
235
|
-
const where = shortPath(args.path);
|
|
236
|
-
const detail = where ? `${asString(args.pattern)} ${where}` : asString(args.pattern);
|
|
237
|
-
return row(callLine(theme, "Grep", detail));
|
|
238
|
-
},
|
|
239
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
240
|
-
if (isPartial)
|
|
241
|
-
return row(resultLine(theme, "搜索中…", "warning"));
|
|
242
|
-
const content = result.content[0];
|
|
243
|
-
const output = content?.type === "text" ? content.text : "";
|
|
244
|
-
const hits = output ? output.split("\n").filter(Boolean).length : 0;
|
|
245
|
-
let text = resultLine(theme, hits ? `${hits} 处` : "无匹配", hits ? "success" : "dim");
|
|
246
|
-
if (expanded && output)
|
|
247
|
-
text += `\n${indentOutput(theme, output)}`;
|
|
248
|
-
return row(text);
|
|
249
|
-
},
|
|
250
|
-
});
|
|
251
|
-
pi.registerTool({
|
|
252
|
-
name: "find",
|
|
253
|
-
label: "find",
|
|
254
|
-
description: originals.find.description,
|
|
255
|
-
parameters: originals.find.parameters,
|
|
256
|
-
renderShell: "self",
|
|
257
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
258
|
-
return toolsFor(ctx.cwd).find.execute(toolCallId, params, signal, onUpdate);
|
|
259
|
-
},
|
|
260
|
-
renderCall(args, theme) {
|
|
261
|
-
return row(callLine(theme, "Find", asString(args.pattern)));
|
|
262
|
-
},
|
|
263
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
264
|
-
if (isPartial)
|
|
265
|
-
return row(resultLine(theme, "查找中…", "warning"));
|
|
266
|
-
const content = result.content[0];
|
|
267
|
-
const output = content?.type === "text" ? content.text : "";
|
|
268
|
-
const hits = output ? output.split("\n").filter(Boolean).length : 0;
|
|
269
|
-
let text = resultLine(theme, hits ? `${hits} 个文件` : "没找到", hits ? "success" : "dim");
|
|
270
|
-
if (expanded && output)
|
|
271
|
-
text += `\n${indentOutput(theme, output)}`;
|
|
272
|
-
return row(text);
|
|
273
|
-
},
|
|
274
|
-
});
|
|
275
|
-
pi.registerTool({
|
|
276
|
-
name: "ls",
|
|
277
|
-
label: "ls",
|
|
278
|
-
description: originals.ls.description,
|
|
279
|
-
parameters: originals.ls.parameters,
|
|
280
|
-
renderShell: "self",
|
|
281
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
282
|
-
return toolsFor(ctx.cwd).ls.execute(toolCallId, params, signal, onUpdate);
|
|
283
|
-
},
|
|
284
|
-
renderCall(args, theme) {
|
|
285
|
-
return row(callLine(theme, "List", shortPath(args.path) || "."));
|
|
286
|
-
},
|
|
287
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
288
|
-
if (isPartial)
|
|
289
|
-
return row(resultLine(theme, "列出中…", "warning"));
|
|
290
|
-
const content = result.content[0];
|
|
291
|
-
const output = content?.type === "text" ? content.text : "";
|
|
292
|
-
const hits = output ? output.split("\n").filter(Boolean).length : 0;
|
|
293
|
-
let text = resultLine(theme, `${hits} 项`, "success");
|
|
294
|
-
if (expanded && output)
|
|
295
|
-
text += `\n${indentOutput(theme, output)}`;
|
|
296
|
-
return row(text);
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u1s1-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"homepage": "https://u1s1.io",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@earendil-works/pi-coding-agent": "0.84.2"
|
|
31
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
32
|
+
"@earendil-works/pi-tui": "0.84.2"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"
|
|
35
|
+
"@types/node": "^22.15.0",
|
|
35
36
|
"tsx": "^4.20.0",
|
|
36
|
-
"
|
|
37
|
+
"typescript": "^5.9.2"
|
|
37
38
|
}
|
|
38
39
|
}
|
package/dist/themes.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { agentDir } from "./config.js";
|
|
4
|
-
/**
|
|
5
|
-
* Claude Code-like: warm terracotta accent, no gray message/tool boxes,
|
|
6
|
-
* text sits on the terminal background instead of Codex-style cards.
|
|
7
|
-
*/
|
|
8
|
-
export const DARK_THEME = {
|
|
9
|
-
name: "u1s1-dark",
|
|
10
|
-
vars: {
|
|
11
|
-
orange: "#d97757",
|
|
12
|
-
orangeSoft: "#e8a87c",
|
|
13
|
-
text: "#e8e4db",
|
|
14
|
-
muted: "#9a9488",
|
|
15
|
-
dim: "#6b665e",
|
|
16
|
-
green: "#8fbc8f",
|
|
17
|
-
red: "#e07a7a",
|
|
18
|
-
yellow: "#d4a574",
|
|
19
|
-
blue: "#89b4c4",
|
|
20
|
-
selectedBg: "#3a342e",
|
|
21
|
-
},
|
|
22
|
-
colors: {
|
|
23
|
-
accent: "orange",
|
|
24
|
-
border: "orangeSoft",
|
|
25
|
-
borderAccent: "orange",
|
|
26
|
-
borderMuted: "dim",
|
|
27
|
-
success: "green",
|
|
28
|
-
error: "red",
|
|
29
|
-
warning: "yellow",
|
|
30
|
-
muted: "muted",
|
|
31
|
-
dim: "dim",
|
|
32
|
-
text: "text",
|
|
33
|
-
thinkingText: "muted",
|
|
34
|
-
selectedBg: "selectedBg",
|
|
35
|
-
scrollbarThumb: "selectedBg",
|
|
36
|
-
searchMatchBg: "selectedBg",
|
|
37
|
-
searchMatchText: "text",
|
|
38
|
-
userMessageBg: "",
|
|
39
|
-
userMessageText: "text",
|
|
40
|
-
customMessageBg: "",
|
|
41
|
-
customMessageText: "text",
|
|
42
|
-
customMessageLabel: "orangeSoft",
|
|
43
|
-
toolPendingBg: "",
|
|
44
|
-
toolSuccessBg: "",
|
|
45
|
-
toolErrorBg: "",
|
|
46
|
-
toolTitle: "text",
|
|
47
|
-
toolOutput: "muted",
|
|
48
|
-
mdHeading: "orangeSoft",
|
|
49
|
-
mdLink: "blue",
|
|
50
|
-
mdLinkUrl: "dim",
|
|
51
|
-
mdCode: "orangeSoft",
|
|
52
|
-
mdCodeBlock: "text",
|
|
53
|
-
mdCodeBlockBorder: "dim",
|
|
54
|
-
mdQuote: "muted",
|
|
55
|
-
mdQuoteBorder: "orange",
|
|
56
|
-
mdHr: "dim",
|
|
57
|
-
mdListBullet: "orange",
|
|
58
|
-
toolDiffAdded: "green",
|
|
59
|
-
toolDiffRemoved: "red",
|
|
60
|
-
toolDiffContext: "muted",
|
|
61
|
-
syntaxComment: "muted",
|
|
62
|
-
syntaxKeyword: "orangeSoft",
|
|
63
|
-
syntaxFunction: "orange",
|
|
64
|
-
syntaxVariable: "text",
|
|
65
|
-
syntaxString: "green",
|
|
66
|
-
syntaxNumber: "yellow",
|
|
67
|
-
syntaxType: "blue",
|
|
68
|
-
syntaxOperator: "muted",
|
|
69
|
-
syntaxPunctuation: "dim",
|
|
70
|
-
thinkingOff: "dim",
|
|
71
|
-
thinkingMinimal: "muted",
|
|
72
|
-
thinkingLow: "blue",
|
|
73
|
-
thinkingMedium: "orangeSoft",
|
|
74
|
-
thinkingHigh: "orange",
|
|
75
|
-
thinkingXhigh: "yellow",
|
|
76
|
-
thinkingMax: "red",
|
|
77
|
-
bashMode: "green",
|
|
78
|
-
},
|
|
79
|
-
export: {
|
|
80
|
-
pageBg: "#1c1917",
|
|
81
|
-
cardBg: "#292524",
|
|
82
|
-
infoBg: "#3d3429",
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
export const LIGHT_THEME = {
|
|
86
|
-
name: "u1s1-light",
|
|
87
|
-
vars: {
|
|
88
|
-
orange: "#c45c38",
|
|
89
|
-
orangeSoft: "#b86a45",
|
|
90
|
-
text: "#2c2825",
|
|
91
|
-
muted: "#6f6a63",
|
|
92
|
-
dim: "#9a9488",
|
|
93
|
-
green: "#3d7a4a",
|
|
94
|
-
red: "#c45c5c",
|
|
95
|
-
yellow: "#9a6b2f",
|
|
96
|
-
blue: "#3d6f8a",
|
|
97
|
-
selectedBg: "#efe8e0",
|
|
98
|
-
},
|
|
99
|
-
colors: {
|
|
100
|
-
accent: "orange",
|
|
101
|
-
border: "orangeSoft",
|
|
102
|
-
borderAccent: "orange",
|
|
103
|
-
borderMuted: "dim",
|
|
104
|
-
success: "green",
|
|
105
|
-
error: "red",
|
|
106
|
-
warning: "yellow",
|
|
107
|
-
muted: "muted",
|
|
108
|
-
dim: "dim",
|
|
109
|
-
text: "text",
|
|
110
|
-
thinkingText: "muted",
|
|
111
|
-
selectedBg: "selectedBg",
|
|
112
|
-
scrollbarThumb: "selectedBg",
|
|
113
|
-
searchMatchBg: "selectedBg",
|
|
114
|
-
searchMatchText: "text",
|
|
115
|
-
userMessageBg: "",
|
|
116
|
-
userMessageText: "text",
|
|
117
|
-
customMessageBg: "",
|
|
118
|
-
customMessageText: "text",
|
|
119
|
-
customMessageLabel: "orangeSoft",
|
|
120
|
-
toolPendingBg: "",
|
|
121
|
-
toolSuccessBg: "",
|
|
122
|
-
toolErrorBg: "",
|
|
123
|
-
toolTitle: "text",
|
|
124
|
-
toolOutput: "muted",
|
|
125
|
-
mdHeading: "orange",
|
|
126
|
-
mdLink: "blue",
|
|
127
|
-
mdLinkUrl: "dim",
|
|
128
|
-
mdCode: "orangeSoft",
|
|
129
|
-
mdCodeBlock: "text",
|
|
130
|
-
mdCodeBlockBorder: "dim",
|
|
131
|
-
mdQuote: "muted",
|
|
132
|
-
mdQuoteBorder: "orange",
|
|
133
|
-
mdHr: "dim",
|
|
134
|
-
mdListBullet: "orange",
|
|
135
|
-
toolDiffAdded: "green",
|
|
136
|
-
toolDiffRemoved: "red",
|
|
137
|
-
toolDiffContext: "muted",
|
|
138
|
-
syntaxComment: "muted",
|
|
139
|
-
syntaxKeyword: "orange",
|
|
140
|
-
syntaxFunction: "orangeSoft",
|
|
141
|
-
syntaxVariable: "text",
|
|
142
|
-
syntaxString: "green",
|
|
143
|
-
syntaxNumber: "yellow",
|
|
144
|
-
syntaxType: "blue",
|
|
145
|
-
syntaxOperator: "muted",
|
|
146
|
-
syntaxPunctuation: "dim",
|
|
147
|
-
thinkingOff: "dim",
|
|
148
|
-
thinkingMinimal: "muted",
|
|
149
|
-
thinkingLow: "blue",
|
|
150
|
-
thinkingMedium: "orangeSoft",
|
|
151
|
-
thinkingHigh: "orange",
|
|
152
|
-
thinkingXhigh: "yellow",
|
|
153
|
-
thinkingMax: "red",
|
|
154
|
-
bashMode: "green",
|
|
155
|
-
},
|
|
156
|
-
export: {
|
|
157
|
-
pageBg: "#faf7f2",
|
|
158
|
-
cardBg: "#ffffff",
|
|
159
|
-
infoBg: "#fff4e8",
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
const THEMES = [DARK_THEME, LIGHT_THEME];
|
|
163
|
-
export const DEFAULT_THEME_SETTING = "u1s1-light/u1s1-dark";
|
|
164
|
-
/** Keep ~/.u1s1/agent/themes in sync so Pi picks them up as normal theme files. */
|
|
165
|
-
export function ensureBrandThemes() {
|
|
166
|
-
const dir = join(agentDir, "themes");
|
|
167
|
-
mkdirSync(dir, { recursive: true });
|
|
168
|
-
for (const theme of THEMES) {
|
|
169
|
-
const path = join(dir, `${theme.name}.json`);
|
|
170
|
-
const body = `${JSON.stringify(theme, null, 2)}\n`;
|
|
171
|
-
if (!existsSync(path) || readFileSync(path, "utf8") !== body) {
|
|
172
|
-
writeFileSync(path, body);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|