@trim21/personal-pi-extensions 0.1.20261011230807 → 0.1.20261012000609

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.1.20261011230807",
3
+ "version": "0.1.20261012000609",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -26,7 +26,28 @@ function propertyName(name: string): string {
26
26
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
27
27
  }
28
28
 
29
- function renderType(schema: unknown, indent: string): string {
29
+ /** schema 自带的字段说明:没有或为空时不渲染注释。 */
30
+ function descriptionOf(schema: unknown): string {
31
+ if (!isRecord(schema) || typeof schema.description !== "string") {
32
+ return "";
33
+ }
34
+ return schema.description.trim();
35
+ }
36
+
37
+ /** 字段注释:单行用 `//`,多行才用 JSDoc;缩进与它下面的属性对齐。 */
38
+ function fieldComment(description: string, indent: string): string {
39
+ const lines = description.split("\n").map((line) => line.trim());
40
+ if (lines.length === 1) {
41
+ return `${indent}// ${lines[0]}`;
42
+ }
43
+ return [
44
+ `${indent}/**`,
45
+ ...lines.map((line) => (line === "" ? `${indent} *` : `${indent} * ${line}`)),
46
+ `${indent} */`,
47
+ ].join("\n");
48
+ }
49
+
50
+ export function renderType(schema: unknown, indent: string): string {
30
51
  if (!isRecord(schema)) {
31
52
  return "unknown";
32
53
  }
@@ -67,7 +88,11 @@ function renderType(schema: unknown, indent: string): string {
67
88
  }
68
89
  const lines = entries.map(([key, value]) => {
69
90
  const optional = required.has(key) ? "" : "?";
70
- return `${indent} ${propertyName(key)}${optional}: ${renderType(value, `${indent} `)};`;
91
+ const property = `${indent} ${propertyName(key)}${optional}: ${renderType(value, `${indent} `)};`;
92
+ const description = descriptionOf(value);
93
+ return description === ""
94
+ ? property
95
+ : `${fieldComment(description, `${indent} `)}\n${property}`;
71
96
  });
72
97
  return `{\n${lines.join("\n")}\n${indent}}`;
73
98
  }
@@ -82,12 +107,24 @@ function renderType(schema: unknown, indent: string): string {
82
107
  * 返回类型取自工具声明的输出结构,未声明就是文本(`string`)。
83
108
  */
84
109
  function renderOverload(tool: ToolLike): string {
85
- const summary = tool.description?.split("\n", 1)[0]?.trim();
86
- const doc = summary ? `/** ${summary} */\n` : "";
110
+ const description = tool.description?.trim();
111
+ const doc = description ? `${docComment(description)}\n` : "";
87
112
  const output = renderType(tool.structuredSchema, "");
88
113
  return `${doc}declare function call(name: ${JSON.stringify(tool.name)}, args: ${renderType(tool.parameters, "")}): Promise<${output}>;`;
89
114
  }
90
115
 
116
+ /**
117
+ * 工具的 JSDoc 注释:description 的每一行都要进描述——只取首行会让工具作者写的说明
118
+ * 在 codemode 里凭空消失。多行按 JSDoc 缩进渲染,单行仍是一行注释。
119
+ */
120
+ function docComment(description: string): string {
121
+ const lines = description.split("\n").map((line) => line.trim());
122
+ if (lines.length === 1) {
123
+ return `/** ${lines[0]} */`;
124
+ }
125
+ return ["/**", ...lines.map((line) => (line === "" ? " *" : ` * ${line}`)), " */"].join("\n");
126
+ }
127
+
91
128
  /** 给脚本用的工具清单(名字 + 说明)。 */
92
129
  export function toScriptTools(tools: readonly ToolLike[]): ScriptTool[] {
93
130
  return tools.map((tool) => ({
@@ -197,9 +197,8 @@ export function addDownloadReleaseAssetsTool(gh: GhClient, bus: ToolBus) {
197
197
  "Download a GitHub release's assets (or its source archive) into " +
198
198
  "~/.cache/pi/github/releases/<owner>/<repo>/<tag>/ using the GitHub credentials, " +
199
199
  "so private repositories and large binaries work where a plain HTTP fetch cannot. " +
200
- "Files already in that directory are kept, never re-fetched. The result is the JSON " +
201
- "summary {repo, tag, dir, files:[{name, path, bytes}]} listing everything now in the " +
202
- "directory; file contents are not echoed. Read the entries you need from `path`.",
200
+ "Files already in that directory are kept, never re-fetched. The result lists everything " +
201
+ "now in the directory; file contents are not echoed. Read the entries you need from `path`.",
203
202
  promptSnippet: "Download GitHub release assets",
204
203
  parameters: Type.Object({
205
204
  repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
@@ -41,7 +41,7 @@ export function addGetWorkflowJobsTool(gh: GhClient, bus: ToolBus) {
41
41
  name: "get-github-workflow-jobs",
42
42
  label: "GitHub Workflow Jobs",
43
43
  description:
44
- "Get every job of a workflow run as JSON {total_count, jobs:[{id, run_id, run_url, name, status, conclusion, html_url, steps:[{name, number, status, conclusion, started_at}]}]}. Paginated server-side, so runs with more than 30 jobs return all of them. Use the `id` with read-github-ci-logs after read-github-pr-status / wait-github-commit-checks did not already give you a job id.",
44
+ "Get every job of a workflow run. Paginated server-side, so runs with more than 30 jobs return all of them. Use the `id` with read-github-ci-logs after read-github-pr-status / wait-github-commit-checks did not already give you a job id.",
45
45
  promptSnippet: "Get GitHub workflow run jobs",
46
46
  parameters: Type.Object({
47
47
  run_id: Type.Union([Type.Number(), Type.String()], { description: "Workflow run ID" }),
@@ -38,7 +38,7 @@ export function addListIssuesTool(gh: GhClient, bus: ToolBus) {
38
38
  name: "list-github-issues",
39
39
  label: "GitHub Issues List",
40
40
  description:
41
- 'List GitHub issues with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open issues — pass state="all" to include closed ones. Set fields to choose the columns of each result row.',
41
+ 'List GitHub issues with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open issues — pass state="all" to include closed ones.',
42
42
  promptSnippet: "List or search GitHub issues",
43
43
  parameters: listFilterParameters("issue"),
44
44
  structuredSchema: ghHitListPayloadSchema,
@@ -38,7 +38,7 @@ export function addListPrsTool(gh: GhClient, bus: ToolBus) {
38
38
  name: "list-github-prs",
39
39
  label: "GitHub PRs List",
40
40
  description:
41
- 'List GitHub pull requests with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open PRs — pass state="merged", state="closed" (merged excluded) or state="all" to broaden. Set fields to choose the columns of each result row.',
41
+ 'List GitHub pull requests with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open PRs — pass state="merged", state="closed" (merged excluded) or state="all" to broaden.',
42
42
  promptSnippet: "List or search GitHub PRs",
43
43
  parameters: listFilterParameters("pr"),
44
44
  structuredSchema: ghHitListPayloadSchema,
@@ -17,8 +17,7 @@ export function addListReleasesTool(gh: GhClient, bus: ToolBus) {
17
17
  defineStructuredTool({
18
18
  name: "list-github-releases",
19
19
  label: "GitHub Releases List",
20
- description:
21
- "List GitHub releases. Each row is: tag, flags (latest/prerelease/draft), published date, name.",
20
+ description: "List GitHub releases.",
22
21
  promptSnippet: "List GitHub releases",
23
22
  parameters: Type.Object({
24
23
  repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
@@ -17,8 +17,7 @@ export function addListWorkflowRunsTool(gh: GhClient, bus: ToolBus) {
17
17
  defineStructuredTool({
18
18
  name: "list-github-workflow-runs",
19
19
  label: "GitHub Workflow Runs",
20
- description:
21
- "List GitHub Actions workflow runs. Each row is: id, status, conclusion, workflow, branch, event, created date, url.",
20
+ description: "List GitHub Actions workflow runs.",
22
21
  promptSnippet: "List GitHub workflow runs",
23
22
  parameters: Type.Object({
24
23
  repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
@@ -414,8 +414,9 @@ export function addReadCiLogsTool(gh: GhClient, bus: ToolBus) {
414
414
  name: "read-github-ci-logs",
415
415
  label: "GitHub CI Logs",
416
416
  description:
417
- "Download one GitHub Actions job's CI log by job ID and index its steps. Returns JSON {name, id, status, conclusion, log_file, steps:[{number, name, conclusion, start_line?, end_line?}]}: `log_file` is the job's complete raw log on disk (runner timestamps and ANSI kept, exactly as GitHub delivers it) and each step carries the 1-based inclusive line range of its block inside that file. Read the content out of the file yourself (read/grep with offset/limit) — it is not echoed back. Get the job IDs from get-github-workflow-jobs, then call this once per job you need." +
418
- " Note: queued jobs have no logs yet; use watch-github-run to wait for completion.",
417
+ "Download one GitHub Actions job's CI log by job ID and index its steps. `log_file` is the job's complete raw log on disk (runner timestamps and ANSI kept, exactly as GitHub delivers it) and each step carries the 1-based inclusive line range of its block inside that file." +
418
+ " The log text is not echoed back: `log_file` is the only copy, so read the parts you need out of it with Read/Grep — e.g. `Read(log_file, offset=start_line, limit=end_line - start_line + 1)` for one step's block, or grep for a pattern. Do not slice or filter the returned JSON to reconstruct log text: it carries only the step index, not the log. Fetch each job once; re-reading that file is how you get every further part of the log." +
419
+ " Get the job IDs from get-github-workflow-jobs, then call this once per job you need. Note: queued jobs have no logs yet; use watch-github-run to wait for completion.",
419
420
  promptSnippet: "Read GitHub CI logs",
420
421
  parameters: Type.Object({
421
422
  job_id: Type.Union([Type.Number(), Type.String()], {
@@ -76,7 +76,7 @@ export function addReadPrStatusTool(gh: GhClient, bus: ToolBus) {
76
76
  name: "read-github-pr-status",
77
77
  label: "GitHub PR Status",
78
78
  description:
79
- "Get the current checks of a pull request's head commit as JSON {pr, repo, head_sha, checks:[{name, bucket, event, run_id, job_id, suite_id, url}]}. `bucket` is pass / fail / pending / skipped / action_required; `action_required` means the workflow is waiting for a maintainer's approval (a fork PR's first run) and has created no check run yet, so it is listed from its check suite and named after its workflow run. Actions checks carry the `run_id` and `job_id` behind them (null for other CI), which is what read-github-ci-logs and get-github-workflow-jobs take; `suite_id` groups the checks of one suite. Returns the snapshot immediately without waiting — use wait-github-pr-checks to block until the checks finish.",
79
+ "Get the current checks of a pull request's head commit. Each check's `bucket` is pass / fail / pending / skipped / action_required; `action_required` means the workflow is waiting for a maintainer's approval (a fork PR's first run) and has created no check run yet, so it is listed from its check suite and named after its workflow run. Actions checks carry the `run_id` and `job_id` behind them (null for other CI), which is what read-github-ci-logs and get-github-workflow-jobs take; `suite_id` groups the checks of one suite. Returns the snapshot immediately without waiting — use wait-github-pr-checks to block until the checks finish.",
80
80
  promptSnippet: "Read GitHub PR status checks",
81
81
  parameters: Type.Object({
82
82
  number: Type.Union([Type.Number(), Type.String()], { description: "PR number" }),