u1s1-cli 0.16.4 → 0.16.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/dist/loop.js CHANGED
@@ -8,8 +8,9 @@
8
8
  * /loop 20m /review-pr 1234 循环里也能跑其他斜杠命令
9
9
  * /loop stop 停止当前循环
10
10
  *
11
- * 语义与 Claude Code 对齐:任务只活在当前会话,/clear 或退出即消失。
12
- * 实现核心是 pi.sendUserMessage():定时器到点就"替用户打字"发一轮,
11
+ * 语义与 Claude Code 对齐:任务只活在当前会话,/clear 或退出即消失;
12
+ * 启动时立刻跑第一轮,之后每隔一个间隔再跑。
13
+ * 实现核心是 pi.sendUserMessage():到点就"替用户打字"发一轮,
13
14
  * deliverAs: "followUp" 保证上一轮没跑完时排队等它结束,不硬插打断;
14
15
  * expandPromptTemplates: true 让 prompt 里的斜杠命令也能被展开执行。
15
16
  */
@@ -116,7 +117,7 @@ export function registerLoopCommand(pi) {
116
117
  stopLoop(ctx.ui);
117
118
  activePrompt = parsed.prompt;
118
119
  activeIntervalMs = parsed.intervalMs;
119
- timer = setInterval(() => {
120
+ const fire = () => {
120
121
  try {
121
122
  pi.sendUserMessage(activePrompt, {
122
123
  deliverAs: "followUp",
@@ -126,9 +127,12 @@ export function registerLoopCommand(pi) {
126
127
  catch (e) {
127
128
  ctx.ui.notify(`loop 执行失败: ${e instanceof Error ? e.message : String(e)}`, "error");
128
129
  }
129
- }, activeIntervalMs);
130
+ };
131
+ // 第一轮立刻跑:用户写完命令就想看到结果,不该干等一个间隔
132
+ fire();
133
+ timer = setInterval(fire, activeIntervalMs);
130
134
  ctx.ui.setStatus(STATUS_KEY, `🔁 每${formatInterval(activeIntervalMs)} · ${preview(activePrompt)}`);
131
- ctx.ui.notify(`🔁 已开始:每${formatInterval(activeIntervalMs)}执行一次「${preview(activePrompt)}」`, "info");
135
+ ctx.ui.notify(`🔁 已开始:现在先跑第一轮,之后每${formatInterval(activeIntervalMs)}一次「${preview(activePrompt)}」`, "info");
132
136
  },
133
137
  });
134
138
  }
package/dist/tools.d.ts CHANGED
@@ -4,10 +4,7 @@ import type { CliConfig } from "./config.js";
4
4
  export declare function createSearchTool(cfg: Pick<CliConfig, "baseUrl" | "apiKey">): import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
5
5
  query: Type.TString;
6
6
  maxResults: Type.TOptional<Type.TNumber>;
7
- }>, {
8
- query: string;
9
- count: number;
10
- }, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
7
+ }>, unknown, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
11
8
  /** 生图工具:走 u1s1 网关代理火山方舟 Seedream,上游 key 不落到用户机器上。 */
12
9
  export declare function createImageTool(cfg: Pick<CliConfig, "baseUrl" | "apiKey">): import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
13
10
  prompt: Type.TString;
@@ -31,9 +28,4 @@ export interface FetchToolConfig {
31
28
  */
32
29
  export declare function createFetchTool(cfg: FetchToolConfig): import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
33
30
  url: Type.TString;
34
- }>, {
35
- url: string;
36
- contentType: string;
37
- via: "direct" | "render";
38
- chars: number;
39
- }, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
31
+ }>, unknown, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
package/dist/tools.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { dirname, extname, resolve } from "node:path";
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
+ import { Text } from "@earendil-works/pi-tui";
4
5
  import { Type } from "typebox";
5
6
  import { generateImage, renderPage, searchWeb } from "./api.js";
6
7
  const FETCH_TIMEOUT_MS = 20_000;
@@ -75,6 +76,32 @@ function decodeBody(bytes, contentType) {
75
76
  return new TextDecoder("utf-8").decode(bytes);
76
77
  }
77
78
  }
79
+ /** 压成单行并截断,给联网工具的精简展示用 */
80
+ function previewLine(text, max = 100) {
81
+ const flat = String(text).replace(/\s*\n\s*/g, " ").trim();
82
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
83
+ }
84
+ /** 从工具结果里取纯文本内容 */
85
+ function resultText(result) {
86
+ const c = result.content.find((x) => x.type === "text");
87
+ return c && c.type === "text" ? (c.text ?? "") : "";
88
+ }
89
+ /** 联网工具通用渲染:调用行不占位,收起时只显一行摘要,ctrl+o 展开全文,出错显一行 ✗ */
90
+ function compactResultRender(result, options,
91
+ // 放宽成 any 只为避开 pi 的 ThemeColor 联合类型在结构化匹配时的逆变报错;
92
+ // 实际传入的就是 pi 的 Theme,取值只用 "warning"/"muted" 两色
93
+ theme, context, summaryLine) {
94
+ if (context.isError) {
95
+ // 低调处理:✗ 用警示色,内容用灰色弱化(与内置工具的错误展示一致)
96
+ const msg = previewLine(resultText(result) || "error", 160);
97
+ return new Text(theme.fg("warning", "✗ ") + theme.fg("muted", msg), 0, 0);
98
+ }
99
+ if (!options.expanded) {
100
+ return new Text(theme.fg("muted", summaryLine), 0, 0);
101
+ }
102
+ const text = resultText(result);
103
+ return new Text(text, 0, 0);
104
+ }
78
105
  /** 联网搜索工具:走 u1s1 网关代理,上游 key 不落到用户机器上。 */
79
106
  export function createSearchTool(cfg) {
80
107
  return defineTool({
@@ -90,6 +117,17 @@ export function createSearchTool(cfg) {
90
117
  query: Type.String({ description: "Search query. Be specific; keep it under ~15 words." }),
91
118
  maxResults: Type.Optional(Type.Number({ description: "How many results to return (1-10, default 5).", minimum: 1, maximum: 10 })),
92
119
  }),
120
+ // 精简展示:收起时一行摘要(🔍 搜索 "…" · N 条结果),ctrl+o 展开全文
121
+ renderShell: "self",
122
+ renderCall() {
123
+ return new Text("", 0, 0);
124
+ },
125
+ renderResult(result, options, theme, context) {
126
+ const d = result.details;
127
+ const q = d?.query ? `"${previewLine(d.query, 80)}"` : "…";
128
+ const n = typeof d?.count === "number" ? ` · ${d.count} 条结果` : "";
129
+ return compactResultRender(result, options, theme, context, `🔍 搜索 ${q}${n}`);
130
+ },
93
131
  async execute(_toolCallId, params, signal) {
94
132
  const data = await searchWeb(cfg, params.query, params.maxResults, signal);
95
133
  const lines = [];
@@ -275,6 +313,18 @@ export function createFetchTool(cfg) {
275
313
  parameters: Type.Object({
276
314
  url: Type.String({ description: "Absolute http(s) URL to fetch." }),
277
315
  }),
316
+ // 精简展示:收起时一行摘要(🌐 url · N 字符 · ☁ 渲染),ctrl+o 展开全文
317
+ renderShell: "self",
318
+ renderCall() {
319
+ return new Text("", 0, 0);
320
+ },
321
+ renderResult(result, options, theme, context) {
322
+ const d = result.details;
323
+ const u = previewLine(d?.url ?? "…", 90);
324
+ const n = typeof d?.chars === "number" ? ` · ${d.chars} 字符` : "";
325
+ const cloud = d?.via === "render" ? " · ☁ 浏览器渲染" : "";
326
+ return compactResultRender(result, options, theme, context, `🌐 ${u}${n}${cloud}`);
327
+ },
278
328
  async execute(_toolCallId, params, signal) {
279
329
  let url;
280
330
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "0.16.4",
3
+ "version": "0.16.6",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {