@trim21/personal-pi-extensions 0.1.612 → 0.1.613
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 +1 -1
- package/src/aft/config.ts +9 -3
- package/src/aft/index.ts +6 -2
- package/src/aft/logger.ts +9 -3
- package/src/aft/tools.ts +3 -1
- package/src/bwrap/approval-rules.ts +42 -14
- package/src/bwrap/approval-suggest.ts +12 -4
- package/src/bwrap/core.ts +39 -13
- package/src/bwrap/dcg-scan.ts +16 -6
- package/src/bwrap/holder.ts +3 -1
- package/src/bwrap/mihomo-config.ts +18 -6
- package/src/bwrap/network-stack.ts +30 -10
- package/src/bwrap/runtime.ts +30 -10
- package/src/claude-code/common.ts +6 -2
- package/src/claude-code/edit-utils.ts +3 -1
- package/src/claude-code/files.ts +40 -14
- package/src/claude-code/glob.ts +12 -4
- package/src/claude-code/grep.ts +41 -14
- package/src/claude-code/session-tools.ts +18 -6
- package/src/claude-code/shell.ts +9 -3
- package/src/gh/base.ts +72 -24
- package/src/gh/tools/download-release-assets.ts +12 -4
- package/src/gh/tools/list-releases.ts +3 -1
- package/src/gh/tools/list-workflow-runs.ts +9 -3
- package/src/gh/tools/read-ci-logs.ts +21 -7
- package/src/gh/tools/read-repo.ts +3 -1
- package/src/lib/cli-args.ts +6 -2
- package/src/lib/cli.ts +48 -16
- package/src/lib/file-reads.ts +24 -8
- package/src/lib/github.ts +35 -12
- package/src/lib/lsp/adapter.ts +6 -2
- package/src/lib/lsp/bin.ts +12 -4
- package/src/lib/lsp/client.ts +205 -69
- package/src/lib/lsp/diagnostic.ts +6 -2
- package/src/lib/lsp/inspect-tool.ts +25 -9
- package/src/lib/lsp/lsp.ts +138 -46
- package/src/lib/lsp/rename-tool.ts +11 -4
- package/src/lib/lsp/rename.ts +25 -8
- package/src/lib/lsp/server-config.ts +41 -14
- package/src/lib/lsp/watcher.ts +51 -17
- package/src/lib/path.ts +18 -6
- package/src/lib/proc.ts +3 -1
- package/src/lib/proxy.ts +9 -3
- package/src/lib/request-policy.ts +3 -1
- package/src/lib/seq-state.ts +3 -1
- package/src/lib/ui.ts +23 -8
- package/src/lib/write-guard.ts +21 -7
- package/src/openai-cost/config.ts +3 -1
- package/src/openai-cost/cost.ts +54 -18
- package/src/openai-cost/index.ts +3 -1
- package/src/openai-cost/provider.ts +6 -2
- package/src/opencode/bash.ts +9 -3
- package/src/opencode/edit-engine.ts +24 -8
- package/src/opencode/files.ts +30 -10
- package/src/opencode/glob.ts +3 -1
- package/src/opencode/grep.ts +12 -4
- package/src/opencode/question.ts +3 -1
- package/src/opencode/ripgrep.ts +18 -6
- package/src/opencode/todo.ts +3 -1
- package/src/session-name.ts +39 -13
- package/src/spawn-agent-agents.ts +18 -6
- package/src/spawn-agent.ts +53 -18
- package/src/system-prompt/index.ts +9 -3
- package/src/talk/core.ts +143 -48
- package/src/talk/format.ts +12 -4
- package/src/talk/group.ts +9 -3
- package/src/talk/index.ts +30 -10
- package/src/talk/mailbox.ts +30 -10
- package/src/talk/policy.ts +6 -2
- package/src/talk/registry.ts +33 -11
- package/src/talk/storage.ts +3 -1
- package/src/vision-agent.ts +41 -14
- package/src/web/config.ts +3 -1
- package/src/web/fetch.ts +75 -25
- package/src/web/search.ts +30 -10
|
@@ -54,7 +54,9 @@ export function releaseAssetDir(repo: string, tag: string): string {
|
|
|
54
54
|
|
|
55
55
|
/** Split the comma-separated `pattern` toolcall parameter into gh pattern values. */
|
|
56
56
|
export function releasePatterns(pattern: string | undefined): string[] {
|
|
57
|
-
if (pattern === undefined)
|
|
57
|
+
if (pattern === undefined) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
58
60
|
return pattern
|
|
59
61
|
.split(",")
|
|
60
62
|
.map((value) => value.trim())
|
|
@@ -75,8 +77,12 @@ export function releaseDownloadArgs(options: {
|
|
|
75
77
|
}): string[] {
|
|
76
78
|
const { tag, repo, dir, patterns, archive } = options;
|
|
77
79
|
const args = ["release", "download", tag, ...repoArgs(repo)];
|
|
78
|
-
if (archive !== undefined)
|
|
79
|
-
|
|
80
|
+
if (archive !== undefined) {
|
|
81
|
+
args.push("--archive", archive);
|
|
82
|
+
}
|
|
83
|
+
for (const pattern of patterns) {
|
|
84
|
+
args.push("--pattern", pattern);
|
|
85
|
+
}
|
|
80
86
|
args.push("--dir", dir, "--skip-existing");
|
|
81
87
|
return args;
|
|
82
88
|
}
|
|
@@ -86,7 +92,9 @@ async function listReleaseFiles(dir: string): Promise<ReleaseFile[]> {
|
|
|
86
92
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
87
93
|
const files: ReleaseFile[] = [];
|
|
88
94
|
for (const entry of entries) {
|
|
89
|
-
if (!entry.isFile())
|
|
95
|
+
if (!entry.isFile()) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
90
98
|
const path = join(dir, entry.name);
|
|
91
99
|
const info = await stat(path);
|
|
92
100
|
files.push({ name: entry.name, path, bytes: info.size });
|
|
@@ -16,7 +16,9 @@ export function addListReleasesTool(_gh: GhClient, pi: ExtensionAPI) {
|
|
|
16
16
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
17
17
|
const { repo, limit } = params;
|
|
18
18
|
const args = ["release", "list", ...repoArgs(repo)];
|
|
19
|
-
if (limit)
|
|
19
|
+
if (limit) {
|
|
20
|
+
args.push("--limit", String(limit));
|
|
21
|
+
}
|
|
20
22
|
const result = toToolResult(
|
|
21
23
|
await ghExec(args, { cwd: ctx.cwd, signal, input: params }),
|
|
22
24
|
params,
|
|
@@ -20,9 +20,15 @@ export function addListWorkflowRunsTool(_gh: GhClient, pi: ExtensionAPI) {
|
|
|
20
20
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
21
21
|
const { repo, limit, status, workflow } = params;
|
|
22
22
|
const args = ["run", "list", ...repoArgs(repo)];
|
|
23
|
-
if (limit)
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (limit) {
|
|
24
|
+
args.push("--limit", String(limit));
|
|
25
|
+
}
|
|
26
|
+
if (status) {
|
|
27
|
+
args.push("--status", status);
|
|
28
|
+
}
|
|
29
|
+
if (workflow) {
|
|
30
|
+
args.push("--workflow", workflow);
|
|
31
|
+
}
|
|
26
32
|
const result = toToolResult(
|
|
27
33
|
await ghExec(args, { cwd: ctx.cwd, signal, input: params }),
|
|
28
34
|
params,
|
|
@@ -138,7 +138,9 @@ const TIME_MATCH_SCORE = 2;
|
|
|
138
138
|
|
|
139
139
|
function headerTimestamp(line: string): number | null {
|
|
140
140
|
const match = LOG_TIMESTAMP_RE.exec(line);
|
|
141
|
-
if (match === null)
|
|
141
|
+
if (match === null) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
142
144
|
const ms = Date.parse(match[1]);
|
|
143
145
|
return Number.isNaN(ms) ? null : ms;
|
|
144
146
|
}
|
|
@@ -149,12 +151,18 @@ function stepHeaders(lines: string[]): StepHeader[] {
|
|
|
149
151
|
let depth = 0;
|
|
150
152
|
for (const [i, line] of lines.entries()) {
|
|
151
153
|
if (line.includes("##[endgroup]")) {
|
|
152
|
-
if (depth > 0)
|
|
154
|
+
if (depth > 0) {
|
|
155
|
+
depth--;
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (!line.includes("##[group]")) {
|
|
153
160
|
continue;
|
|
154
161
|
}
|
|
155
|
-
if (!line.includes("##[group]")) continue;
|
|
156
162
|
depth++;
|
|
157
|
-
if (depth !== 1)
|
|
163
|
+
if (depth !== 1) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
158
166
|
const name = /##\[group\](.*)/.exec(line)?.[1].trim() ?? "";
|
|
159
167
|
if (HEADER_PREFIX_RE.test(name)) {
|
|
160
168
|
headers.push({
|
|
@@ -252,7 +260,9 @@ function alignStepsToHeaders(
|
|
|
252
260
|
|
|
253
261
|
/** Exclusive end index with trailing blank lines dropped, so a span slices to real text. */
|
|
254
262
|
function trimTrailingBlankLines(lines: string[], start: number, end: number): number {
|
|
255
|
-
while (end > start && (lines[end - 1] ?? "").trim() === "")
|
|
263
|
+
while (end > start && (lines[end - 1] ?? "").trim() === "") {
|
|
264
|
+
end--;
|
|
265
|
+
}
|
|
256
266
|
return end;
|
|
257
267
|
}
|
|
258
268
|
|
|
@@ -306,7 +316,9 @@ export function extractStepFromLog(
|
|
|
306
316
|
apiSteps: readonly { number: number; name: string }[],
|
|
307
317
|
): string | null {
|
|
308
318
|
const span = stepLineSpans(log, apiSteps).get(stepNumber);
|
|
309
|
-
if (span === undefined)
|
|
319
|
+
if (span === undefined) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
310
322
|
return log.split("\n").slice(span.start, span.end).join("\n").trimEnd();
|
|
311
323
|
}
|
|
312
324
|
|
|
@@ -382,7 +394,9 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<ToolRe
|
|
|
382
394
|
target = await gh.checks.job(owner, name, jobId, signal);
|
|
383
395
|
} catch (error) {
|
|
384
396
|
const status = (error as { status?: number }).status;
|
|
385
|
-
if (status !== 404)
|
|
397
|
+
if (status !== 404) {
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
386
400
|
return failure(
|
|
387
401
|
`Job ${jobId} not found in ${effectiveRepo} — job IDs come from \`get-github-workflow-jobs\`.`,
|
|
388
402
|
);
|
|
@@ -15,7 +15,9 @@ export function addReadRepoTool(_gh: GhClient, pi: ExtensionAPI) {
|
|
|
15
15
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
16
16
|
const { repo } = params;
|
|
17
17
|
const args = ["repo", "view"];
|
|
18
|
-
if (repo)
|
|
18
|
+
if (repo) {
|
|
19
|
+
args.push(repo);
|
|
20
|
+
}
|
|
19
21
|
const result = toToolResult(
|
|
20
22
|
await ghExec(args, { cwd: ctx.cwd, signal, input: params }),
|
|
21
23
|
params,
|
package/src/lib/cli-args.ts
CHANGED
|
@@ -35,7 +35,9 @@ export function shlexSplit(raw: string): string[] {
|
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
37
|
if (c === "\\") {
|
|
38
|
-
if (i + 1 < raw.length)
|
|
38
|
+
if (i + 1 < raw.length) {
|
|
39
|
+
cur += raw[i + 1];
|
|
40
|
+
}
|
|
39
41
|
i += 2;
|
|
40
42
|
continue;
|
|
41
43
|
}
|
|
@@ -53,6 +55,8 @@ export function shlexSplit(raw: string): string[] {
|
|
|
53
55
|
if (quote !== undefined) {
|
|
54
56
|
throw new SyntaxError(`unterminated quote in command arguments: ${raw}`);
|
|
55
57
|
}
|
|
56
|
-
if (cur)
|
|
58
|
+
if (cur) {
|
|
59
|
+
tokens.push(cur);
|
|
60
|
+
}
|
|
57
61
|
return tokens;
|
|
58
62
|
}
|
package/src/lib/cli.ts
CHANGED
|
@@ -84,16 +84,28 @@ interface FlagInfo {
|
|
|
84
84
|
|
|
85
85
|
/** schema 的种类名,仅用于报错文案。 */
|
|
86
86
|
function schemaKindLabel(schema: TSchema): string {
|
|
87
|
-
if ("~kind" in schema && typeof schema["~kind"] === "string")
|
|
88
|
-
|
|
87
|
+
if ("~kind" in schema && typeof schema["~kind"] === "string") {
|
|
88
|
+
return schema["~kind"];
|
|
89
|
+
}
|
|
90
|
+
if ("type" in schema && typeof schema.type === "string") {
|
|
91
|
+
return schema.type;
|
|
92
|
+
}
|
|
89
93
|
return "unknown";
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
function kindOf(key: string, schema: TSchema): FlagKind {
|
|
93
|
-
if (IsKind(schema, "Boolean"))
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (IsKind(schema, "
|
|
97
|
+
if (IsKind(schema, "Boolean")) {
|
|
98
|
+
return "boolean";
|
|
99
|
+
}
|
|
100
|
+
if (IsKind(schema, "String")) {
|
|
101
|
+
return "string";
|
|
102
|
+
}
|
|
103
|
+
if (IsKind(schema, "Number") || IsKind(schema, "Integer")) {
|
|
104
|
+
return "number";
|
|
105
|
+
}
|
|
106
|
+
if (IsKind(schema, "Union")) {
|
|
107
|
+
return "enum";
|
|
108
|
+
}
|
|
97
109
|
throw new TypeError(
|
|
98
110
|
`Unsupported flag type for '${key}': ${schemaKindLabel(schema)} ` +
|
|
99
111
|
"(use Type.Boolean/String/Number/Integer or a string literal union)",
|
|
@@ -118,10 +130,14 @@ function flagDescription(schema: TSchema): string {
|
|
|
118
130
|
|
|
119
131
|
/** Allowed values for a string-literal union flag, or undefined for mixed unions. */
|
|
120
132
|
function enumValues(schema: TSchema): string[] | undefined {
|
|
121
|
-
if (!("anyOf" in schema) || !isUnknownArray(schema.anyOf))
|
|
133
|
+
if (!("anyOf" in schema) || !isUnknownArray(schema.anyOf)) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
122
136
|
const values: string[] = [];
|
|
123
137
|
for (const variant of schema.anyOf) {
|
|
124
|
-
if (!isRecord(variant) || typeof variant.const !== "string")
|
|
138
|
+
if (!isRecord(variant) || typeof variant.const !== "string") {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
125
141
|
values.push(variant.const);
|
|
126
142
|
}
|
|
127
143
|
return values;
|
|
@@ -164,7 +180,9 @@ function helpResult<TFlags extends TObject>(
|
|
|
164
180
|
autoHelp: boolean,
|
|
165
181
|
): CommandResult<TFlags> {
|
|
166
182
|
const lines = [`Usage: /${spec.name} ${spec.usage}`];
|
|
167
|
-
if (spec.description)
|
|
183
|
+
if (spec.description) {
|
|
184
|
+
lines.push("", spec.description);
|
|
185
|
+
}
|
|
168
186
|
const rows = flags.map((f) => ({
|
|
169
187
|
rawName:
|
|
170
188
|
(f.short ? `-${f.short}, ` : "") +
|
|
@@ -172,15 +190,21 @@ function helpResult<TFlags extends TObject>(
|
|
|
172
190
|
(f.kind === "boolean" ? "" : ` ${f.placeholder}`),
|
|
173
191
|
description: f.description,
|
|
174
192
|
}));
|
|
175
|
-
if (autoHelp)
|
|
193
|
+
if (autoHelp) {
|
|
194
|
+
rows.push({ rawName: "-h, --help", description: "Display this message" });
|
|
195
|
+
}
|
|
176
196
|
if (rows.length > 0) {
|
|
177
197
|
lines.push("", "Options:");
|
|
178
198
|
const width = Math.max(...rows.map((r) => r.rawName.length));
|
|
179
|
-
for (const r of rows)
|
|
199
|
+
for (const r of rows) {
|
|
200
|
+
lines.push(` ${r.rawName.padEnd(width)} ${r.description}`);
|
|
201
|
+
}
|
|
180
202
|
}
|
|
181
203
|
if (spec.examples?.length) {
|
|
182
204
|
lines.push("", "Examples:");
|
|
183
|
-
for (const e of spec.examples)
|
|
205
|
+
for (const e of spec.examples) {
|
|
206
|
+
lines.push(` ${e}`);
|
|
207
|
+
}
|
|
184
208
|
}
|
|
185
209
|
return { kind: "help", text: lines.join("\n") };
|
|
186
210
|
}
|
|
@@ -233,9 +257,13 @@ export function parseCommand<TFlags extends TObject>(
|
|
|
233
257
|
const body = token.slice(2);
|
|
234
258
|
const eq = body.indexOf("=");
|
|
235
259
|
const name = eq === -1 ? body : body.slice(0, eq);
|
|
236
|
-
if (autoHelp && name === "help")
|
|
260
|
+
if (autoHelp && name === "help") {
|
|
261
|
+
return helpResult(spec, flags, autoHelp);
|
|
262
|
+
}
|
|
237
263
|
const info = byLong.get(name);
|
|
238
|
-
if (!info)
|
|
264
|
+
if (!info) {
|
|
265
|
+
return errorResult(spec, `Unknown option '--${name}'`);
|
|
266
|
+
}
|
|
239
267
|
const inline = eq === -1 ? undefined : body.slice(eq + 1);
|
|
240
268
|
if (inline !== undefined) {
|
|
241
269
|
rawFlags[info.key] = inline;
|
|
@@ -258,9 +286,13 @@ export function parseCommand<TFlags extends TObject>(
|
|
|
258
286
|
const rest = token.slice(1);
|
|
259
287
|
for (let j = 0; j < rest.length; j++) {
|
|
260
288
|
const c = rest[j];
|
|
261
|
-
if (autoHelp && c === "h")
|
|
289
|
+
if (autoHelp && c === "h") {
|
|
290
|
+
return helpResult(spec, flags, autoHelp);
|
|
291
|
+
}
|
|
262
292
|
const info = byShort.get(c);
|
|
263
|
-
if (!info)
|
|
293
|
+
if (!info) {
|
|
294
|
+
return errorResult(spec, `Unknown option '-${c}'`);
|
|
295
|
+
}
|
|
264
296
|
if (info.kind === "boolean") {
|
|
265
297
|
rawFlags[info.key] = true;
|
|
266
298
|
continue;
|
package/src/lib/file-reads.ts
CHANGED
|
@@ -64,7 +64,9 @@ export async function digestIfExists(filePath: string): Promise<string | undefin
|
|
|
64
64
|
try {
|
|
65
65
|
return await fileDigest(filePath);
|
|
66
66
|
} catch (error) {
|
|
67
|
-
if (isMissingPath(error))
|
|
67
|
+
if (isMissingPath(error)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
68
70
|
throw error;
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -77,7 +79,9 @@ export async function readStateKey(filePath: string): Promise<string> {
|
|
|
77
79
|
try {
|
|
78
80
|
return await realpath(filePath);
|
|
79
81
|
} catch (error) {
|
|
80
|
-
if (isMissingPath(error))
|
|
82
|
+
if (isMissingPath(error)) {
|
|
83
|
+
return filePath;
|
|
84
|
+
}
|
|
81
85
|
throw error;
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -120,7 +124,9 @@ export function requireUnchangedRead(
|
|
|
120
124
|
currentDigest: string | undefined,
|
|
121
125
|
): void {
|
|
122
126
|
const readSnapshot = state.reads.get(key);
|
|
123
|
-
if (!readSnapshot)
|
|
127
|
+
if (!readSnapshot) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
124
130
|
if (currentDigest === undefined || readSnapshot.digest !== currentDigest) {
|
|
125
131
|
throw new Error(
|
|
126
132
|
"File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.",
|
|
@@ -135,9 +141,13 @@ export function requireUnchangedRead(
|
|
|
135
141
|
*/
|
|
136
142
|
export function deserializeReads(data: unknown): Map<string, FileSnapshot> {
|
|
137
143
|
const reads = new Map<string, FileSnapshot>();
|
|
138
|
-
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
144
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
145
|
+
return reads;
|
|
146
|
+
}
|
|
139
147
|
for (const [filePath, snapshot] of Object.entries(data)) {
|
|
140
|
-
if (Value.Check(fileSnapshotSchema, snapshot))
|
|
148
|
+
if (Value.Check(fileSnapshotSchema, snapshot)) {
|
|
149
|
+
reads.set(filePath, snapshot);
|
|
150
|
+
}
|
|
141
151
|
}
|
|
142
152
|
return reads;
|
|
143
153
|
}
|
|
@@ -153,10 +163,16 @@ export function restoreReads(
|
|
|
153
163
|
): void {
|
|
154
164
|
state.reads.clear();
|
|
155
165
|
for (const entry of sessionManager.getBranch()) {
|
|
156
|
-
if (entry.type !== "message" || entry.message.role !== "toolResult")
|
|
157
|
-
|
|
166
|
+
if (entry.type !== "message" || entry.message.role !== "toolResult") {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!toolNames.has(entry.message.toolName)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
158
172
|
const details = entry.message.details;
|
|
159
|
-
if (!isRecord(details) || !details.reads)
|
|
173
|
+
if (!isRecord(details) || !details.reads) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
160
176
|
for (const [filePath, snapshot] of deserializeReads(details.reads)) {
|
|
161
177
|
state.reads.set(filePath, snapshot);
|
|
162
178
|
}
|
package/src/lib/github.ts
CHANGED
|
@@ -91,7 +91,9 @@ export function buildSearchQuery(kind: SearchKind, params: SearchParams): string
|
|
|
91
91
|
const state = params.state ?? "open";
|
|
92
92
|
|
|
93
93
|
const parts: string[] = [];
|
|
94
|
-
if (repo)
|
|
94
|
+
if (repo) {
|
|
95
|
+
parts.push(`repo:${repo}`);
|
|
96
|
+
}
|
|
95
97
|
parts.push(kind === "issue" ? "is:issue" : "is:pr");
|
|
96
98
|
switch (state) {
|
|
97
99
|
case "open": {
|
|
@@ -105,7 +107,9 @@ export function buildSearchQuery(kind: SearchKind, params: SearchParams): string
|
|
|
105
107
|
break;
|
|
106
108
|
}
|
|
107
109
|
case "merged": {
|
|
108
|
-
if (kind === "issue")
|
|
110
|
+
if (kind === "issue") {
|
|
111
|
+
throw new Error("state=merged is only valid for PR searches");
|
|
112
|
+
}
|
|
109
113
|
parts.push("is:merged");
|
|
110
114
|
|
|
111
115
|
break;
|
|
@@ -118,17 +122,29 @@ export function buildSearchQuery(kind: SearchKind, params: SearchParams): string
|
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
|
-
if (keywords)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
125
|
+
if (keywords) {
|
|
126
|
+
parts.push(keywords);
|
|
127
|
+
}
|
|
128
|
+
if (label) {
|
|
129
|
+
parts.push(`label:${quoteQualifier(label)}`);
|
|
130
|
+
}
|
|
131
|
+
if (author) {
|
|
132
|
+
parts.push(`author:${author}`);
|
|
133
|
+
}
|
|
134
|
+
if (assignee) {
|
|
135
|
+
parts.push(`assignee:${assignee}`);
|
|
136
|
+
}
|
|
137
|
+
if (milestone) {
|
|
138
|
+
parts.push(`milestone:${quoteQualifier(milestone)}`);
|
|
139
|
+
}
|
|
126
140
|
return parts.join(" ");
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
/** Quote a qualifier value that contains whitespace or special characters. */
|
|
130
144
|
function quoteQualifier(value: string): string {
|
|
131
|
-
if (/^[\w@./-]+$/.test(value))
|
|
145
|
+
if (/^[\w@./-]+$/.test(value)) {
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
132
148
|
return `"${value.replaceAll('"', String.raw`\"`)}"`;
|
|
133
149
|
}
|
|
134
150
|
|
|
@@ -251,10 +267,15 @@ export class GithubSearchError extends Error {
|
|
|
251
267
|
}
|
|
252
268
|
|
|
253
269
|
function describeHttpError(status: number | undefined): string {
|
|
254
|
-
if (status === 401)
|
|
270
|
+
if (status === 401) {
|
|
255
271
|
return 'GitHub auth failed (401): token invalid or expired — run "gh auth login"';
|
|
256
|
-
|
|
257
|
-
if (status ===
|
|
272
|
+
}
|
|
273
|
+
if (status === 403) {
|
|
274
|
+
return "GitHub rate limit or permissions error (403)";
|
|
275
|
+
}
|
|
276
|
+
if (status === 404) {
|
|
277
|
+
return "repository not found, or the token has no access to it (404)";
|
|
278
|
+
}
|
|
258
279
|
return `GitHub API error${status === undefined ? "" : ` (HTTP ${status})`}`;
|
|
259
280
|
}
|
|
260
281
|
|
|
@@ -530,7 +551,9 @@ export function createGithubChecks(options: GithubClientOptions = {}): GithubChe
|
|
|
530
551
|
);
|
|
531
552
|
for (const run of commitRuns) {
|
|
532
553
|
const id = String(run.id);
|
|
533
|
-
if (runIds.has(id))
|
|
554
|
+
if (runIds.has(id)) {
|
|
555
|
+
events.set(id, run.event);
|
|
556
|
+
}
|
|
534
557
|
}
|
|
535
558
|
}
|
|
536
559
|
return runs.map((run) => {
|
package/src/lib/lsp/adapter.ts
CHANGED
|
@@ -57,7 +57,9 @@ export interface LspServerAdapter {
|
|
|
57
57
|
function dirChain(cwd: string, target: string): string[] {
|
|
58
58
|
const dirs = [cwd];
|
|
59
59
|
const rel = relative(cwd, target);
|
|
60
|
-
if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
|
|
60
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
|
|
61
|
+
return dirs;
|
|
62
|
+
}
|
|
61
63
|
let current = cwd;
|
|
62
64
|
for (const part of rel.split(sep)) {
|
|
63
65
|
current = join(current, part);
|
|
@@ -84,7 +86,9 @@ export function serverRoot(
|
|
|
84
86
|
const markers = adapter.rootMarkers ?? [];
|
|
85
87
|
if (markers.length > 0) {
|
|
86
88
|
for (const dir of dirChain(cwd, dirname(file))) {
|
|
87
|
-
if (markers.some((marker) => exists(join(dir, marker))))
|
|
89
|
+
if (markers.some((marker) => exists(join(dir, marker)))) {
|
|
90
|
+
return dir;
|
|
91
|
+
}
|
|
88
92
|
}
|
|
89
93
|
return cwd;
|
|
90
94
|
}
|
package/src/lib/lsp/bin.ts
CHANGED
|
@@ -21,9 +21,13 @@ export function walkUp(fromDir: string, stopDir: string): string[] {
|
|
|
21
21
|
const stop = normalize(stopDir);
|
|
22
22
|
for (;;) {
|
|
23
23
|
dirs.push(current);
|
|
24
|
-
if (current === stop)
|
|
24
|
+
if (current === stop) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
25
27
|
const parent = dirname(current);
|
|
26
|
-
if (parent === current)
|
|
28
|
+
if (parent === current) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
27
31
|
current = parent;
|
|
28
32
|
}
|
|
29
33
|
return dirs;
|
|
@@ -55,7 +59,9 @@ export function findBinaryInWorkspace(
|
|
|
55
59
|
for (const root of roots) {
|
|
56
60
|
for (const name of binaryNames(cmd)) {
|
|
57
61
|
const candidate = join(root, name);
|
|
58
|
-
if (exists(candidate))
|
|
62
|
+
if (exists(candidate)) {
|
|
63
|
+
return Promise.resolve(candidate);
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
}
|
|
@@ -68,7 +74,9 @@ export function which(cmd: string): string | undefined {
|
|
|
68
74
|
for (const dir of pathDirs) {
|
|
69
75
|
for (const name of binaryNames(cmd)) {
|
|
70
76
|
const candidate = join(dir, name);
|
|
71
|
-
if (existsSync(candidate))
|
|
77
|
+
if (existsSync(candidate)) {
|
|
78
|
+
return candidate;
|
|
79
|
+
}
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
return undefined;
|