@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.
Files changed (75) hide show
  1. package/package.json +1 -1
  2. package/src/aft/config.ts +9 -3
  3. package/src/aft/index.ts +6 -2
  4. package/src/aft/logger.ts +9 -3
  5. package/src/aft/tools.ts +3 -1
  6. package/src/bwrap/approval-rules.ts +42 -14
  7. package/src/bwrap/approval-suggest.ts +12 -4
  8. package/src/bwrap/core.ts +39 -13
  9. package/src/bwrap/dcg-scan.ts +16 -6
  10. package/src/bwrap/holder.ts +3 -1
  11. package/src/bwrap/mihomo-config.ts +18 -6
  12. package/src/bwrap/network-stack.ts +30 -10
  13. package/src/bwrap/runtime.ts +30 -10
  14. package/src/claude-code/common.ts +6 -2
  15. package/src/claude-code/edit-utils.ts +3 -1
  16. package/src/claude-code/files.ts +40 -14
  17. package/src/claude-code/glob.ts +12 -4
  18. package/src/claude-code/grep.ts +41 -14
  19. package/src/claude-code/session-tools.ts +18 -6
  20. package/src/claude-code/shell.ts +9 -3
  21. package/src/gh/base.ts +72 -24
  22. package/src/gh/tools/download-release-assets.ts +12 -4
  23. package/src/gh/tools/list-releases.ts +3 -1
  24. package/src/gh/tools/list-workflow-runs.ts +9 -3
  25. package/src/gh/tools/read-ci-logs.ts +21 -7
  26. package/src/gh/tools/read-repo.ts +3 -1
  27. package/src/lib/cli-args.ts +6 -2
  28. package/src/lib/cli.ts +48 -16
  29. package/src/lib/file-reads.ts +24 -8
  30. package/src/lib/github.ts +35 -12
  31. package/src/lib/lsp/adapter.ts +6 -2
  32. package/src/lib/lsp/bin.ts +12 -4
  33. package/src/lib/lsp/client.ts +205 -69
  34. package/src/lib/lsp/diagnostic.ts +6 -2
  35. package/src/lib/lsp/inspect-tool.ts +25 -9
  36. package/src/lib/lsp/lsp.ts +138 -46
  37. package/src/lib/lsp/rename-tool.ts +11 -4
  38. package/src/lib/lsp/rename.ts +25 -8
  39. package/src/lib/lsp/server-config.ts +41 -14
  40. package/src/lib/lsp/watcher.ts +51 -17
  41. package/src/lib/path.ts +18 -6
  42. package/src/lib/proc.ts +3 -1
  43. package/src/lib/proxy.ts +9 -3
  44. package/src/lib/request-policy.ts +3 -1
  45. package/src/lib/seq-state.ts +3 -1
  46. package/src/lib/ui.ts +23 -8
  47. package/src/lib/write-guard.ts +21 -7
  48. package/src/openai-cost/config.ts +3 -1
  49. package/src/openai-cost/cost.ts +54 -18
  50. package/src/openai-cost/index.ts +3 -1
  51. package/src/openai-cost/provider.ts +6 -2
  52. package/src/opencode/bash.ts +9 -3
  53. package/src/opencode/edit-engine.ts +24 -8
  54. package/src/opencode/files.ts +30 -10
  55. package/src/opencode/glob.ts +3 -1
  56. package/src/opencode/grep.ts +12 -4
  57. package/src/opencode/question.ts +3 -1
  58. package/src/opencode/ripgrep.ts +18 -6
  59. package/src/opencode/todo.ts +3 -1
  60. package/src/session-name.ts +39 -13
  61. package/src/spawn-agent-agents.ts +18 -6
  62. package/src/spawn-agent.ts +53 -18
  63. package/src/system-prompt/index.ts +9 -3
  64. package/src/talk/core.ts +143 -48
  65. package/src/talk/format.ts +12 -4
  66. package/src/talk/group.ts +9 -3
  67. package/src/talk/index.ts +30 -10
  68. package/src/talk/mailbox.ts +30 -10
  69. package/src/talk/policy.ts +6 -2
  70. package/src/talk/registry.ts +33 -11
  71. package/src/talk/storage.ts +3 -1
  72. package/src/vision-agent.ts +41 -14
  73. package/src/web/config.ts +3 -1
  74. package/src/web/fetch.ts +75 -25
  75. 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) return [];
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) args.push("--archive", archive);
79
- for (const pattern of patterns) args.push("--pattern", pattern);
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()) continue;
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) args.push("--limit", String(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) args.push("--limit", String(limit));
24
- if (status) args.push("--status", status);
25
- if (workflow) args.push("--workflow", workflow);
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) return 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) depth--;
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) continue;
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() === "") end--;
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) return null;
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) throw error;
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) args.push(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,
@@ -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) cur += raw[i + 1];
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) tokens.push(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") return schema["~kind"];
88
- if ("type" in schema && typeof schema.type === "string") return schema.type;
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")) return "boolean";
94
- if (IsKind(schema, "String")) return "string";
95
- if (IsKind(schema, "Number") || IsKind(schema, "Integer")) return "number";
96
- if (IsKind(schema, "Union")) return "enum";
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)) return undefined;
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") return undefined;
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) lines.push("", 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) rows.push({ rawName: "-h, --help", description: "Display this message" });
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) lines.push(` ${r.rawName.padEnd(width)} ${r.description}`);
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) lines.push(` ${e}`);
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") return helpResult(spec, flags, autoHelp);
260
+ if (autoHelp && name === "help") {
261
+ return helpResult(spec, flags, autoHelp);
262
+ }
237
263
  const info = byLong.get(name);
238
- if (!info) return errorResult(spec, `Unknown option '--${name}'`);
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") return helpResult(spec, flags, autoHelp);
289
+ if (autoHelp && c === "h") {
290
+ return helpResult(spec, flags, autoHelp);
291
+ }
262
292
  const info = byShort.get(c);
263
- if (!info) return errorResult(spec, `Unknown option '-${c}'`);
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;
@@ -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)) return undefined;
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)) return filePath;
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) return;
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)) return reads;
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)) reads.set(filePath, 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") continue;
157
- if (!toolNames.has(entry.message.toolName)) continue;
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) continue;
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) parts.push(`repo:${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") throw new Error("state=merged is only valid for PR searches");
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) parts.push(keywords);
122
- if (label) parts.push(`label:${quoteQualifier(label)}`);
123
- if (author) parts.push(`author:${author}`);
124
- if (assignee) parts.push(`assignee:${assignee}`);
125
- if (milestone) parts.push(`milestone:${quoteQualifier(milestone)}`);
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)) return 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
- if (status === 403) return "GitHub rate limit or permissions error (403)";
257
- if (status === 404) return "repository not found, or the token has no access to it (404)";
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)) events.set(id, run.event);
554
+ if (runIds.has(id)) {
555
+ events.set(id, run.event);
556
+ }
534
557
  }
535
558
  }
536
559
  return runs.map((run) => {
@@ -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)) return dirs;
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)))) return dir;
89
+ if (markers.some((marker) => exists(join(dir, marker)))) {
90
+ return dir;
91
+ }
88
92
  }
89
93
  return cwd;
90
94
  }
@@ -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) break;
24
+ if (current === stop) {
25
+ break;
26
+ }
25
27
  const parent = dirname(current);
26
- if (parent === current) break;
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)) return Promise.resolve(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)) return candidate;
77
+ if (existsSync(candidate)) {
78
+ return candidate;
79
+ }
72
80
  }
73
81
  }
74
82
  return undefined;