Преглед изворни кода

extract expandHome utility, fix scan error handling scope

Kit Langton пре 1 месец
родитељ
комит
be7a9c4987

+ 1 - 4
packages/opencode/src/config/paths.ts

@@ -1,5 +1,4 @@
 import path from "path"
 import path from "path"
-import os from "os"
 import z from "zod"
 import z from "zod"
 import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
 import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
 import { NamedError } from "@opencode-ai/util/error"
 import { NamedError } from "@opencode-ai/util/error"
@@ -109,9 +108,7 @@ export namespace ConfigPaths {
       }
       }
 
 
       let filePath = token.replace(/^\{file:/, "").replace(/\}$/, "")
       let filePath = token.replace(/^\{file:/, "").replace(/\}$/, "")
-      if (filePath.startsWith("~/")) {
-        filePath = path.join(os.homedir(), filePath.slice(2))
-      }
+      filePath = Filesystem.expandHome(filePath)
 
 
       const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
       const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
       const fileContent = (
       const fileContent = (

+ 1 - 3
packages/opencode/src/session/instruction.ts

@@ -95,9 +95,7 @@ export namespace InstructionPrompt {
     if (config.instructions) {
     if (config.instructions) {
       for (let instruction of config.instructions) {
       for (let instruction of config.instructions) {
         if (instruction.startsWith("https://") || instruction.startsWith("http://")) continue
         if (instruction.startsWith("https://") || instruction.startsWith("http://")) continue
-        if (instruction.startsWith("~/")) {
-          instruction = path.join(os.homedir(), instruction.slice(2))
-        }
+        instruction = Filesystem.expandHome(instruction)
         const matches = path.isAbsolute(instruction)
         const matches = path.isAbsolute(instruction)
           ? await Glob.scan(path.basename(instruction), {
           ? await Glob.scan(path.basename(instruction), {
               cwd: path.dirname(instruction),
               cwd: path.dirname(instruction),

+ 1 - 1
packages/opencode/src/session/prompt.ts

@@ -202,7 +202,7 @@ export namespace SessionPrompt {
         if (seen.has(name)) return
         if (seen.has(name)) return
         seen.add(name)
         seen.add(name)
         const filepath = name.startsWith("~/")
         const filepath = name.startsWith("~/")
-          ? path.join(os.homedir(), name.slice(2))
+          ? Filesystem.expandHome(name)
           : path.resolve(Instance.worktree, name)
           : path.resolve(Instance.worktree, name)
 
 
         const stats = await fs.stat(filepath).catch(() => undefined)
         const stats = await fs.stat(filepath).catch(() => undefined)

+ 12 - 9
packages/opencode/src/skill/skill.ts

@@ -1,4 +1,3 @@
-import os from "os"
 import path from "path"
 import path from "path"
 import { pathToFileURL } from "url"
 import { pathToFileURL } from "url"
 import z from "zod"
 import z from "zod"
@@ -12,6 +11,7 @@ import { runPromiseInstance } from "@/effect/runtime"
 import { Flag } from "@/flag/flag"
 import { Flag } from "@/flag/flag"
 import { Global } from "@/global"
 import { Global } from "@/global"
 import { PermissionNext } from "@/permission"
 import { PermissionNext } from "@/permission"
+import { Filesystem } from "@/util/filesystem"
 import { Config } from "../config/config"
 import { Config } from "../config/config"
 import { ConfigMarkdown } from "../config/markdown"
 import { ConfigMarkdown } from "../config/markdown"
 import { Log } from "../util/log"
 import { Log } from "../util/log"
@@ -127,14 +127,17 @@ export namespace Skill {
               symlink: true,
               symlink: true,
               dot: opts?.dot,
               dot: opts?.dot,
             })
             })
-            .pipe(Effect.orDie)
+            .pipe(
+              Effect.catch((error) => {
+                if (!opts?.scope) return Effect.fail(error)
+                return Effect.sync(() => {
+                  log.error(`failed to scan ${opts.scope} skills`, { dir: root, error })
+                  return [] as string[]
+                })
+              }),
+            )
 
 
-          yield* Effect.forEach(matches, (match) => add(match), { concurrency: "unbounded" }).pipe(
-            Effect.catch((error) => {
-              if (!opts?.scope) return Effect.die(error)
-              return Effect.sync(() => log.error(`failed to scan ${opts.scope} skills`, { dir: root, error }))
-            }),
-          )
+          yield* Effect.forEach(matches, (match) => add(match), { concurrency: "unbounded" })
         })
         })
 
 
         const load = Effect.fn("Skill.load")(function* () {
         const load = Effect.fn("Skill.load")(function* () {
@@ -169,7 +172,7 @@ export namespace Skill {
           // Phase 4: Custom paths
           // Phase 4: Custom paths
           const cfg = yield* Effect.promise(() => Config.get())
           const cfg = yield* Effect.promise(() => Config.get())
           for (const item of cfg.skills?.paths ?? []) {
           for (const item of cfg.skills?.paths ?? []) {
-            const expanded = item.startsWith("~/") ? path.join(os.homedir(), item.slice(2)) : item
+            const expanded = Filesystem.expandHome(item)
             const dir = path.isAbsolute(expanded) ? expanded : path.join(instance.directory, expanded)
             const dir = path.isAbsolute(expanded) ? expanded : path.join(instance.directory, expanded)
             if (!(yield* fs.isDir(dir).pipe(Effect.orDie))) {
             if (!(yield* fs.isDir(dir).pipe(Effect.orDie))) {
               log.warn("skill path not found", { path: dir })
               log.warn("skill path not found", { path: dir })

+ 5 - 0
packages/opencode/src/util/filesystem.ts

@@ -2,6 +2,7 @@ import { chmod, mkdir, readFile, writeFile } from "fs/promises"
 import { createWriteStream, existsSync, statSync } from "fs"
 import { createWriteStream, existsSync, statSync } from "fs"
 import { lookup } from "mime-types"
 import { lookup } from "mime-types"
 import { realpathSync } from "fs"
 import { realpathSync } from "fs"
+import os from "os"
 import { dirname, join, relative, resolve as pathResolve } from "path"
 import { dirname, join, relative, resolve as pathResolve } from "path"
 import { Readable } from "stream"
 import { Readable } from "stream"
 import { pipeline } from "stream/promises"
 import { pipeline } from "stream/promises"
@@ -95,6 +96,10 @@ export namespace Filesystem {
     }
     }
   }
   }
 
 
+  export function expandHome(p: string): string {
+    return p.startsWith("~/") ? join(os.homedir(), p.slice(2)) : p
+  }
+
   export function mimeType(p: string): string {
   export function mimeType(p: string): string {
     return lookup(p) || "application/octet-stream"
     return lookup(p) || "application/octet-stream"
   }
   }