Compare commits
1 commit
dev
...
kit/ns-fil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e61fe8e5f8 |
35 changed files with 834 additions and 839 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
import { File } from "../../../file"
|
import { File } from "../../../file"
|
||||||
import { Ripgrep } from "@/file/ripgrep"
|
import { Ripgrep } from "@/file"
|
||||||
import { bootstrap } from "../../bootstrap"
|
import { bootstrap } from "../../bootstrap"
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { Effect, Stream } from "effect"
|
import { Effect, Stream } from "effect"
|
||||||
import { AppRuntime } from "../../../effect/app-runtime"
|
import { AppRuntime } from "../../../effect/app-runtime"
|
||||||
import { Ripgrep } from "../../../file/ripgrep"
|
import { Ripgrep } from "../../../file"
|
||||||
import { Instance } from "../../../project/instance"
|
import { Instance } from "../../../project/instance"
|
||||||
import { bootstrap } from "../../bootstrap"
|
import { bootstrap } from "../../bootstrap"
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import { Auth } from "@/auth"
|
||||||
import { Account } from "@/account"
|
import { Account } from "@/account"
|
||||||
import { Config } from "@/config"
|
import { Config } from "@/config"
|
||||||
import { Git } from "@/git"
|
import { Git } from "@/git"
|
||||||
import { Ripgrep } from "@/file/ripgrep"
|
import { Ripgrep } from "@/file"
|
||||||
import { FileTime } from "@/file/time"
|
import { FileTime } from "@/file"
|
||||||
import { File } from "@/file"
|
import { File } from "@/file"
|
||||||
import { FileWatcher } from "@/file/watcher"
|
import { FileWatcher } from "@/file"
|
||||||
import { Storage } from "@/storage"
|
import { Storage } from "@/storage"
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { Plugin } from "@/plugin"
|
import { Plugin } from "@/plugin"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { memoMap } from "./run-service"
|
||||||
|
|
||||||
import { Plugin } from "@/plugin"
|
import { Plugin } from "@/plugin"
|
||||||
import { LSP } from "@/lsp"
|
import { LSP } from "@/lsp"
|
||||||
import { FileWatcher } from "@/file/watcher"
|
import { FileWatcher } from "@/file"
|
||||||
import { Format } from "@/format"
|
import { Format } from "@/format"
|
||||||
import { ShareNext } from "@/share"
|
import { ShareNext } from "@/share"
|
||||||
import { File } from "@/file"
|
import { File } from "@/file"
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ import z from "zod"
|
||||||
import { Global } from "../global"
|
import { Global } from "../global"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Log } from "../util"
|
import { Log } from "../util"
|
||||||
import { Protected } from "./protected"
|
import * as Protected from "./protected"
|
||||||
import { Ripgrep } from "./ripgrep"
|
import * as Ripgrep from "./ripgrep"
|
||||||
|
|
||||||
export const Info = z
|
export const Info = z
|
||||||
.object({
|
.object({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { Glob } from "@opencode-ai/shared/util/glob"
|
import { Glob } from "@opencode-ai/shared/util/glob"
|
||||||
|
|
||||||
export namespace FileIgnore {
|
const FOLDERS = new Set([
|
||||||
const FOLDERS = new Set([
|
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"bower_components",
|
"bower_components",
|
||||||
".pnpm-store",
|
".pnpm-store",
|
||||||
|
|
@ -30,9 +29,9 @@ export namespace FileIgnore {
|
||||||
"mypy_cache",
|
"mypy_cache",
|
||||||
".history",
|
".history",
|
||||||
".gradle",
|
".gradle",
|
||||||
])
|
])
|
||||||
|
|
||||||
const FILES = [
|
const FILES = [
|
||||||
"**/*.swp",
|
"**/*.swp",
|
||||||
"**/*.swo",
|
"**/*.swo",
|
||||||
|
|
||||||
|
|
@ -51,17 +50,17 @@ export namespace FileIgnore {
|
||||||
// Coverage/test outputs
|
// Coverage/test outputs
|
||||||
"**/coverage/**",
|
"**/coverage/**",
|
||||||
"**/.nyc_output/**",
|
"**/.nyc_output/**",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const PATTERNS = [...FILES, ...FOLDERS]
|
export const PATTERNS = [...FILES, ...FOLDERS]
|
||||||
|
|
||||||
export function match(
|
export function match(
|
||||||
filepath: string,
|
filepath: string,
|
||||||
opts?: {
|
opts?: {
|
||||||
extra?: string[]
|
extra?: string[]
|
||||||
whitelist?: string[]
|
whitelist?: string[]
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
for (const pattern of opts?.whitelist || []) {
|
for (const pattern of opts?.whitelist || []) {
|
||||||
if (Glob.match(pattern, filepath)) return false
|
if (Glob.match(pattern, filepath)) return false
|
||||||
}
|
}
|
||||||
|
|
@ -77,5 +76,4 @@ export namespace FileIgnore {
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,6 @@
|
||||||
export * as File from "./file"
|
export * as File from "./file"
|
||||||
|
export * as Protected from "./protected"
|
||||||
|
export * as FileIgnore from "./ignore"
|
||||||
|
export * as FileWatcher from "./watcher"
|
||||||
|
export * as FileTime from "./time"
|
||||||
|
export * as Ripgrep from "./ripgrep"
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,15 @@ const DARWIN_ROOT = ["/.DocumentRevisions-V100", "/.Spotlight-V100", "/.Trashes"
|
||||||
|
|
||||||
const WIN32_HOME = ["AppData", "Downloads", "Desktop", "Documents", "Pictures", "Music", "Videos", "OneDrive"]
|
const WIN32_HOME = ["AppData", "Downloads", "Desktop", "Documents", "Pictures", "Music", "Videos", "OneDrive"]
|
||||||
|
|
||||||
export namespace Protected {
|
/** Directory basenames to skip when scanning the home directory. */
|
||||||
/** Directory basenames to skip when scanning the home directory. */
|
export function names(): ReadonlySet<string> {
|
||||||
export function names(): ReadonlySet<string> {
|
|
||||||
if (process.platform === "darwin") return new Set(DARWIN_HOME)
|
if (process.platform === "darwin") return new Set(DARWIN_HOME)
|
||||||
if (process.platform === "win32") return new Set(WIN32_HOME)
|
if (process.platform === "win32") return new Set(WIN32_HOME)
|
||||||
return new Set()
|
return new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Absolute paths that should never be watched, stated, or scanned. */
|
/** Absolute paths that should never be watched, stated, or scanned. */
|
||||||
export function paths(): string[] {
|
export function paths(): string[] {
|
||||||
if (process.platform === "darwin")
|
if (process.platform === "darwin")
|
||||||
return [
|
return [
|
||||||
...DARWIN_HOME.map((n) => path.join(home, n)),
|
...DARWIN_HOME.map((n) => path.join(home, n)),
|
||||||
|
|
@ -55,5 +54,4 @@ export namespace Protected {
|
||||||
]
|
]
|
||||||
if (process.platform === "win32") return WIN32_HOME.map((n) => path.join(home, n))
|
if (process.platform === "win32") return WIN32_HOME.map((n) => path.join(home, n))
|
||||||
return []
|
return []
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@ import { ripgrep } from "ripgrep"
|
||||||
import { Filesystem } from "@/util"
|
import { Filesystem } from "@/util"
|
||||||
import { Log } from "@/util"
|
import { Log } from "@/util"
|
||||||
|
|
||||||
export namespace Ripgrep {
|
const log = Log.create({ service: "ripgrep" })
|
||||||
const log = Log.create({ service: "ripgrep" })
|
|
||||||
|
|
||||||
const Stats = z.object({
|
const Stats = z.object({
|
||||||
elapsed: z.object({
|
elapsed: z.object({
|
||||||
secs: z.number(),
|
secs: z.number(),
|
||||||
nanos: z.number(),
|
nanos: z.number(),
|
||||||
|
|
@ -23,18 +22,18 @@ export namespace Ripgrep {
|
||||||
bytes_printed: z.number(),
|
bytes_printed: z.number(),
|
||||||
matched_lines: z.number(),
|
matched_lines: z.number(),
|
||||||
matches: z.number(),
|
matches: z.number(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Begin = z.object({
|
const Begin = z.object({
|
||||||
type: z.literal("begin"),
|
type: z.literal("begin"),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
path: z.object({
|
path: z.object({
|
||||||
text: z.string(),
|
text: z.string(),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const Match = z.object({
|
export const Match = z.object({
|
||||||
type: z.literal("match"),
|
type: z.literal("match"),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
path: z.object({
|
path: z.object({
|
||||||
|
|
@ -55,9 +54,9 @@ export namespace Ripgrep {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const End = z.object({
|
const End = z.object({
|
||||||
type: z.literal("end"),
|
type: z.literal("end"),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
path: z.object({
|
path: z.object({
|
||||||
|
|
@ -66,9 +65,9 @@ export namespace Ripgrep {
|
||||||
binary_offset: z.number().nullable(),
|
binary_offset: z.number().nullable(),
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Summary = z.object({
|
const Summary = z.object({
|
||||||
type: z.literal("summary"),
|
type: z.literal("summary"),
|
||||||
data: z.object({
|
data: z.object({
|
||||||
elapsed_total: z.object({
|
elapsed_total: z.object({
|
||||||
|
|
@ -78,33 +77,33 @@ export namespace Ripgrep {
|
||||||
}),
|
}),
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Result = z.union([Begin, Match, End, Summary])
|
const Result = z.union([Begin, Match, End, Summary])
|
||||||
|
|
||||||
export type Result = z.infer<typeof Result>
|
export type Result = z.infer<typeof Result>
|
||||||
export type Match = z.infer<typeof Match>
|
export type Match = z.infer<typeof Match>
|
||||||
export type Item = Match["data"]
|
export type Item = Match["data"]
|
||||||
export type Begin = z.infer<typeof Begin>
|
export type Begin = z.infer<typeof Begin>
|
||||||
export type End = z.infer<typeof End>
|
export type End = z.infer<typeof End>
|
||||||
export type Summary = z.infer<typeof Summary>
|
export type Summary = z.infer<typeof Summary>
|
||||||
export type Row = Match["data"]
|
export type Row = Match["data"]
|
||||||
|
|
||||||
export interface SearchResult {
|
export interface SearchResult {
|
||||||
items: Item[]
|
items: Item[]
|
||||||
partial: boolean
|
partial: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FilesInput {
|
export interface FilesInput {
|
||||||
cwd: string
|
cwd: string
|
||||||
glob?: string[]
|
glob?: string[]
|
||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
follow?: boolean
|
follow?: boolean
|
||||||
maxDepth?: number
|
maxDepth?: number
|
||||||
signal?: AbortSignal
|
signal?: AbortSignal
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchInput {
|
export interface SearchInput {
|
||||||
cwd: string
|
cwd: string
|
||||||
pattern: string
|
pattern: string
|
||||||
glob?: string[]
|
glob?: string[]
|
||||||
|
|
@ -112,91 +111,91 @@ export namespace Ripgrep {
|
||||||
follow?: boolean
|
follow?: boolean
|
||||||
file?: string[]
|
file?: string[]
|
||||||
signal?: AbortSignal
|
signal?: AbortSignal
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeInput {
|
export interface TreeInput {
|
||||||
cwd: string
|
cwd: string
|
||||||
limit?: number
|
limit?: number
|
||||||
signal?: AbortSignal
|
signal?: AbortSignal
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly files: (input: FilesInput) => Stream.Stream<string, Error>
|
readonly files: (input: FilesInput) => Stream.Stream<string, Error>
|
||||||
readonly tree: (input: TreeInput) => Effect.Effect<string, Error>
|
readonly tree: (input: TreeInput) => Effect.Effect<string, Error>
|
||||||
readonly search: (input: SearchInput) => Effect.Effect<SearchResult, Error>
|
readonly search: (input: SearchInput) => Effect.Effect<SearchResult, Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
|
||||||
|
|
||||||
type Run = { kind: "files" | "search"; cwd: string; args: string[] }
|
type Run = { kind: "files" | "search"; cwd: string; args: string[] }
|
||||||
|
|
||||||
type WorkerResult = {
|
type WorkerResult = {
|
||||||
type: "result"
|
type: "result"
|
||||||
code: number
|
code: number
|
||||||
stdout: string
|
stdout: string
|
||||||
stderr: string
|
stderr: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerLine = {
|
type WorkerLine = {
|
||||||
type: "line"
|
type: "line"
|
||||||
line: string
|
line: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerDone = {
|
type WorkerDone = {
|
||||||
type: "done"
|
type: "done"
|
||||||
code: number
|
code: number
|
||||||
stderr: string
|
stderr: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerError = {
|
type WorkerError = {
|
||||||
type: "error"
|
type: "error"
|
||||||
error: {
|
error: {
|
||||||
message: string
|
message: string
|
||||||
name?: string
|
name?: string
|
||||||
stack?: string
|
stack?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function env() {
|
function env() {
|
||||||
const env = Object.fromEntries(
|
const env = Object.fromEntries(
|
||||||
Object.entries(process.env).filter((item): item is [string, string] => item[1] !== undefined),
|
Object.entries(process.env).filter((item): item is [string, string] => item[1] !== undefined),
|
||||||
)
|
)
|
||||||
delete env.RIPGREP_CONFIG_PATH
|
delete env.RIPGREP_CONFIG_PATH
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
function text(input: unknown) {
|
function text(input: unknown) {
|
||||||
if (typeof input === "string") return input
|
if (typeof input === "string") return input
|
||||||
if (input instanceof ArrayBuffer) return Buffer.from(input).toString()
|
if (input instanceof ArrayBuffer) return Buffer.from(input).toString()
|
||||||
if (ArrayBuffer.isView(input)) return Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString()
|
if (ArrayBuffer.isView(input)) return Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString()
|
||||||
return String(input)
|
return String(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toError(input: unknown) {
|
function toError(input: unknown) {
|
||||||
if (input instanceof Error) return input
|
if (input instanceof Error) return input
|
||||||
if (typeof input === "string") return new Error(input)
|
if (typeof input === "string") return new Error(input)
|
||||||
return new Error(String(input))
|
return new Error(String(input))
|
||||||
}
|
}
|
||||||
|
|
||||||
function abort(signal?: AbortSignal) {
|
function abort(signal?: AbortSignal) {
|
||||||
const err = signal?.reason
|
const err = signal?.reason
|
||||||
if (err instanceof Error) return err
|
if (err instanceof Error) return err
|
||||||
const out = new Error("Aborted")
|
const out = new Error("Aborted")
|
||||||
out.name = "AbortError"
|
out.name = "AbortError"
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(stderr: string, code: number) {
|
function error(stderr: string, code: number) {
|
||||||
const err = new Error(stderr.trim() || `ripgrep failed with code ${code}`)
|
const err = new Error(stderr.trim() || `ripgrep failed with code ${code}`)
|
||||||
err.name = "RipgrepError"
|
err.name = "RipgrepError"
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean(file: string) {
|
function clean(file: string) {
|
||||||
return path.normalize(file.replace(/^\.[\\/]/, ""))
|
return path.normalize(file.replace(/^\.[\\/]/, ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
function row(data: Row): Row {
|
function row(data: Row): Row {
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
path: {
|
path: {
|
||||||
|
|
@ -204,16 +203,16 @@ export namespace Ripgrep {
|
||||||
text: clean(data.path.text),
|
text: clean(data.path.text),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function opts(cwd: string) {
|
function opts(cwd: string) {
|
||||||
return {
|
return {
|
||||||
env: env(),
|
env: env(),
|
||||||
preopens: { ".": cwd },
|
preopens: { ".": cwd },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function check(cwd: string) {
|
function check(cwd: string) {
|
||||||
return Effect.tryPromise({
|
return Effect.tryPromise({
|
||||||
try: () => fs.stat(cwd).catch(() => undefined),
|
try: () => fs.stat(cwd).catch(() => undefined),
|
||||||
catch: toError,
|
catch: toError,
|
||||||
|
|
@ -230,9 +229,9 @@ export namespace Ripgrep {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filesArgs(input: FilesInput) {
|
function filesArgs(input: FilesInput) {
|
||||||
const args = ["--files", "--glob=!.git/*"]
|
const args = ["--files", "--glob=!.git/*"]
|
||||||
if (input.follow) args.push("--follow")
|
if (input.follow) args.push("--follow")
|
||||||
if (input.hidden !== false) args.push("--hidden")
|
if (input.hidden !== false) args.push("--hidden")
|
||||||
|
|
@ -244,9 +243,9 @@ export namespace Ripgrep {
|
||||||
}
|
}
|
||||||
args.push(".")
|
args.push(".")
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchArgs(input: SearchInput) {
|
function searchArgs(input: SearchInput) {
|
||||||
const args = ["--json", "--hidden", "--glob=!.git/*", "--no-messages"]
|
const args = ["--json", "--hidden", "--glob=!.git/*", "--no-messages"]
|
||||||
if (input.follow) args.push("--follow")
|
if (input.follow) args.push("--follow")
|
||||||
if (input.glob) {
|
if (input.glob) {
|
||||||
|
|
@ -257,20 +256,20 @@ export namespace Ripgrep {
|
||||||
if (input.limit) args.push(`--max-count=${input.limit}`)
|
if (input.limit) args.push(`--max-count=${input.limit}`)
|
||||||
args.push("--", input.pattern, ...(input.file ?? ["."]))
|
args.push("--", input.pattern, ...(input.file ?? ["."]))
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse(stdout: string) {
|
function parse(stdout: string) {
|
||||||
return stdout
|
return stdout
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((line) => Result.parse(JSON.parse(line)))
|
.map((line) => Result.parse(JSON.parse(line)))
|
||||||
.flatMap((item) => (item.type === "match" ? [row(item.data)] : []))
|
.flatMap((item) => (item.type === "match" ? [row(item.data)] : []))
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const OPENCODE_RIPGREP_WORKER_PATH: string
|
declare const OPENCODE_RIPGREP_WORKER_PATH: string
|
||||||
|
|
||||||
function target(): Effect.Effect<string | URL, Error> {
|
function target(): Effect.Effect<string | URL, Error> {
|
||||||
if (typeof OPENCODE_RIPGREP_WORKER_PATH !== "undefined") {
|
if (typeof OPENCODE_RIPGREP_WORKER_PATH !== "undefined") {
|
||||||
return Effect.succeed(OPENCODE_RIPGREP_WORKER_PATH)
|
return Effect.succeed(OPENCODE_RIPGREP_WORKER_PATH)
|
||||||
}
|
}
|
||||||
|
|
@ -279,26 +278,26 @@ export namespace Ripgrep {
|
||||||
try: () => Filesystem.exists(fileURLToPath(js)),
|
try: () => Filesystem.exists(fileURLToPath(js)),
|
||||||
catch: toError,
|
catch: toError,
|
||||||
}).pipe(Effect.map((exists) => (exists ? js : new URL("./ripgrep.worker.ts", import.meta.url))))
|
}).pipe(Effect.map((exists) => (exists ? js : new URL("./ripgrep.worker.ts", import.meta.url))))
|
||||||
}
|
}
|
||||||
|
|
||||||
function worker() {
|
function worker() {
|
||||||
return target().pipe(Effect.flatMap((file) => Effect.sync(() => new Worker(file, { env: env() }))))
|
return target().pipe(Effect.flatMap((file) => Effect.sync(() => new Worker(file, { env: env() }))))
|
||||||
}
|
}
|
||||||
|
|
||||||
function drain(buf: string, chunk: unknown, push: (line: string) => void) {
|
function drain(buf: string, chunk: unknown, push: (line: string) => void) {
|
||||||
const lines = (buf + text(chunk)).split(/\r?\n/)
|
const lines = (buf + text(chunk)).split(/\r?\n/)
|
||||||
buf = lines.pop() || ""
|
buf = lines.pop() || ""
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line) push(line)
|
if (line) push(line)
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
function fail(queue: Queue.Queue<string, Error | Cause.Done>, err: Error) {
|
function fail(queue: Queue.Queue<string, Error | Cause.Done>, err: Error) {
|
||||||
Queue.failCauseUnsafe(queue, Cause.fail(err))
|
Queue.failCauseUnsafe(queue, Cause.fail(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchDirect(input: SearchInput) {
|
function searchDirect(input: SearchInput) {
|
||||||
return Effect.tryPromise({
|
return Effect.tryPromise({
|
||||||
try: () =>
|
try: () =>
|
||||||
ripgrep(searchArgs(input), {
|
ripgrep(searchArgs(input), {
|
||||||
|
|
@ -318,9 +317,9 @@ export namespace Ripgrep {
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchWorker(input: SearchInput) {
|
function searchWorker(input: SearchInput) {
|
||||||
if (input.signal?.aborted) return Effect.fail(abort(input.signal))
|
if (input.signal?.aborted) return Effect.fail(abort(input.signal))
|
||||||
|
|
||||||
return Effect.acquireUseRelease(
|
return Effect.acquireUseRelease(
|
||||||
|
|
@ -377,9 +376,9 @@ export namespace Ripgrep {
|
||||||
}),
|
}),
|
||||||
(w) => Effect.sync(() => w.terminate()),
|
(w) => Effect.sync(() => w.terminate()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filesDirect(input: FilesInput) {
|
function filesDirect(input: FilesInput) {
|
||||||
return Stream.callback<string, Error>(
|
return Stream.callback<string, Error>(
|
||||||
Effect.fnUntraced(function* (queue: Queue.Queue<string, Error | Cause.Done>) {
|
Effect.fnUntraced(function* (queue: Queue.Queue<string, Error | Cause.Done>) {
|
||||||
let buf = ""
|
let buf = ""
|
||||||
|
|
@ -427,9 +426,9 @@ export namespace Ripgrep {
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filesWorker(input: FilesInput) {
|
function filesWorker(input: FilesInput) {
|
||||||
return Stream.callback<string, Error>(
|
return Stream.callback<string, Error>(
|
||||||
Effect.fnUntraced(function* (queue: Queue.Queue<string, Error | Cause.Done>) {
|
Effect.fnUntraced(function* (queue: Queue.Queue<string, Error | Cause.Done>) {
|
||||||
if (input.signal?.aborted) {
|
if (input.signal?.aborted) {
|
||||||
|
|
@ -489,9 +488,9 @@ export namespace Ripgrep {
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const source = (input: FilesInput) => {
|
const source = (input: FilesInput) => {
|
||||||
|
|
@ -569,7 +568,6 @@ export namespace Ripgrep {
|
||||||
|
|
||||||
return Service.of({ files, tree, search })
|
return Service.of({ files, tree, search })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = layer
|
export const defaultLayer = layer
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,39 +5,38 @@ import { Flag } from "@/flag/flag"
|
||||||
import type { SessionID } from "@/session/schema"
|
import type { SessionID } from "@/session/schema"
|
||||||
import { Log } from "../util"
|
import { Log } from "../util"
|
||||||
|
|
||||||
export namespace FileTime {
|
const log = Log.create({ service: "file.time" })
|
||||||
const log = Log.create({ service: "file.time" })
|
|
||||||
|
|
||||||
export type Stamp = {
|
export type Stamp = {
|
||||||
readonly read: Date
|
readonly read: Date
|
||||||
readonly mtime: number | undefined
|
readonly mtime: number | undefined
|
||||||
readonly size: number | undefined
|
readonly size: number | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = (reads: Map<SessionID, Map<string, Stamp>>, sessionID: SessionID) => {
|
const session = (reads: Map<SessionID, Map<string, Stamp>>, sessionID: SessionID) => {
|
||||||
const value = reads.get(sessionID)
|
const value = reads.get(sessionID)
|
||||||
if (value) return value
|
if (value) return value
|
||||||
|
|
||||||
const next = new Map<string, Stamp>()
|
const next = new Map<string, Stamp>()
|
||||||
reads.set(sessionID, next)
|
reads.set(sessionID, next)
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
reads: Map<SessionID, Map<string, Stamp>>
|
reads: Map<SessionID, Map<string, Stamp>>
|
||||||
locks: Map<string, Semaphore.Semaphore>
|
locks: Map<string, Semaphore.Semaphore>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly read: (sessionID: SessionID, file: string) => Effect.Effect<void>
|
readonly read: (sessionID: SessionID, file: string) => Effect.Effect<void>
|
||||||
readonly get: (sessionID: SessionID, file: string) => Effect.Effect<Date | undefined>
|
readonly get: (sessionID: SessionID, file: string) => Effect.Effect<Date | undefined>
|
||||||
readonly assert: (sessionID: SessionID, filepath: string) => Effect.Effect<void>
|
readonly assert: (sessionID: SessionID, filepath: string) => Effect.Effect<void>
|
||||||
readonly withLock: <T>(filepath: string, fn: () => Effect.Effect<T>) => Effect.Effect<T>
|
readonly withLock: <T>(filepath: string, fn: () => Effect.Effect<T>) => Effect.Effect<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/FileTime") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/FileTime") {}
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const fsys = yield* AppFileSystem.Service
|
const fsys = yield* AppFileSystem.Service
|
||||||
|
|
@ -107,7 +106,6 @@ export namespace FileTime {
|
||||||
|
|
||||||
return Service.of({ read, get, assert, withLock })
|
return Service.of({ read, get, assert, withLock })
|
||||||
}),
|
}),
|
||||||
).pipe(Layer.orDie)
|
).pipe(Layer.orDie)
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
|
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,16 @@ import { Git } from "@/git"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { lazy } from "@/util/lazy"
|
import { lazy } from "@/util/lazy"
|
||||||
import { Config } from "../config"
|
import { Config } from "../config"
|
||||||
import { FileIgnore } from "./ignore"
|
import * as FileIgnore from "./ignore"
|
||||||
import { Protected } from "./protected"
|
import * as Protected from "./protected"
|
||||||
import { Log } from "../util"
|
import { Log } from "../util"
|
||||||
|
|
||||||
declare const OPENCODE_LIBC: string | undefined
|
declare const OPENCODE_LIBC: string | undefined
|
||||||
|
|
||||||
export namespace FileWatcher {
|
const log = Log.create({ service: "file.watcher" })
|
||||||
const log = Log.create({ service: "file.watcher" })
|
const SUBSCRIBE_TIMEOUT_MS = 10_000
|
||||||
const SUBSCRIBE_TIMEOUT_MS = 10_000
|
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Updated: BusEvent.define(
|
Updated: BusEvent.define(
|
||||||
"file.watcher.updated",
|
"file.watcher.updated",
|
||||||
z.object({
|
z.object({
|
||||||
|
|
@ -31,9 +30,9 @@ export namespace FileWatcher {
|
||||||
event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
|
event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
|
const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
|
||||||
try {
|
try {
|
||||||
const binding = require(
|
const binding = require(
|
||||||
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${OPENCODE_LIBC || "glibc"}` : ""}`,
|
`@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${OPENCODE_LIBC || "glibc"}` : ""}`,
|
||||||
|
|
@ -43,30 +42,30 @@ export namespace FileWatcher {
|
||||||
log.error("failed to load watcher binding", { error })
|
log.error("failed to load watcher binding", { error })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function getBackend() {
|
function getBackend() {
|
||||||
if (process.platform === "win32") return "windows"
|
if (process.platform === "win32") return "windows"
|
||||||
if (process.platform === "darwin") return "fs-events"
|
if (process.platform === "darwin") return "fs-events"
|
||||||
if (process.platform === "linux") return "inotify"
|
if (process.platform === "linux") return "inotify"
|
||||||
}
|
}
|
||||||
|
|
||||||
function protecteds(dir: string) {
|
function protecteds(dir: string) {
|
||||||
return Protected.paths().filter((item) => {
|
return Protected.paths().filter((item) => {
|
||||||
const rel = path.relative(dir, item)
|
const rel = path.relative(dir, item)
|
||||||
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
|
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasNativeBinding = () => !!watcher()
|
export const hasNativeBinding = () => !!watcher()
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly init: () => Effect.Effect<void>
|
readonly init: () => Effect.Effect<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/FileWatcher") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/FileWatcher") {}
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service
|
const config = yield* Config.Service
|
||||||
|
|
@ -157,7 +156,6 @@ export namespace FileWatcher {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Git.defaultLayer))
|
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(Git.defaultLayer))
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { Bus } from "../bus"
|
||||||
import { Command } from "../command"
|
import { Command } from "../command"
|
||||||
import { Instance } from "./instance"
|
import { Instance } from "./instance"
|
||||||
import { Log } from "@/util"
|
import { Log } from "@/util"
|
||||||
import { FileWatcher } from "@/file/watcher"
|
import { FileWatcher } from "@/file"
|
||||||
import { ShareNext } from "@/share"
|
import { ShareNext } from "@/share"
|
||||||
import * as Effect from "effect/Effect"
|
import * as Effect from "effect/Effect"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Bus } from "@/bus"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { InstanceState } from "@/effect"
|
import { InstanceState } from "@/effect"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { FileWatcher } from "@/file/watcher"
|
import { FileWatcher } from "@/file"
|
||||||
import { Git } from "@/git"
|
import { Git } from "@/git"
|
||||||
import { Log } from "@/util"
|
import { Log } from "@/util"
|
||||||
import { Instance } from "./instance"
|
import { Instance } from "./instance"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Effect } from "effect"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { AppRuntime } from "../../effect/app-runtime"
|
import { AppRuntime } from "../../effect/app-runtime"
|
||||||
import { File } from "../../file"
|
import { File } from "../../file"
|
||||||
import { Ripgrep } from "../../file/ripgrep"
|
import { Ripgrep } from "../../file"
|
||||||
import { LSP } from "../../lsp"
|
import { LSP } from "../../lsp"
|
||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
import { lazy } from "../../util/lazy"
|
import { lazy } from "../../util/lazy"
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import MAX_STEPS from "../session/prompt/max-steps.txt"
|
||||||
import { ToolRegistry } from "../tool/registry"
|
import { ToolRegistry } from "../tool/registry"
|
||||||
import { MCP } from "../mcp"
|
import { MCP } from "../mcp"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { FileTime } from "../file/time"
|
import { FileTime } from "../file"
|
||||||
import { Flag } from "../flag/flag"
|
import { Flag } from "../flag/flag"
|
||||||
import { ulid } from "ulid"
|
import { ulid } from "ulid"
|
||||||
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import * as path from "path"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
import { FileWatcher } from "../file/watcher"
|
import { FileWatcher } from "../file"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Patch } from "../patch"
|
import { Patch } from "../patch"
|
||||||
import { createTwoFilesPatch, diffLines } from "diff"
|
import { createTwoFilesPatch, diffLines } from "diff"
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ import { LSP } from "../lsp"
|
||||||
import { createTwoFilesPatch, diffLines } from "diff"
|
import { createTwoFilesPatch, diffLines } from "diff"
|
||||||
import DESCRIPTION from "./edit.txt"
|
import DESCRIPTION from "./edit.txt"
|
||||||
import { File } from "../file"
|
import { File } from "../file"
|
||||||
import { FileWatcher } from "../file/watcher"
|
import { FileWatcher } from "../file"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
import { Format } from "../format"
|
import { Format } from "../format"
|
||||||
import { FileTime } from "../file/time"
|
import { FileTime } from "../file"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { assertExternalDirectoryEffect } from "./external-directory"
|
import { assertExternalDirectoryEffect } from "./external-directory"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Effect, Option } from "effect"
|
||||||
import * as Stream from "effect/Stream"
|
import * as Stream from "effect/Stream"
|
||||||
import { InstanceState } from "@/effect"
|
import { InstanceState } from "@/effect"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Ripgrep } from "../file/ripgrep"
|
import { Ripgrep } from "../file"
|
||||||
import { assertExternalDirectoryEffect } from "./external-directory"
|
import { assertExternalDirectoryEffect } from "./external-directory"
|
||||||
import DESCRIPTION from "./glob.txt"
|
import DESCRIPTION from "./glob.txt"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import z from "zod"
|
||||||
import { Effect, Option } from "effect"
|
import { Effect, Option } from "effect"
|
||||||
import { InstanceState } from "@/effect"
|
import { InstanceState } from "@/effect"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Ripgrep } from "../file/ripgrep"
|
import { Ripgrep } from "../file"
|
||||||
import { assertExternalDirectoryEffect } from "./external-directory"
|
import { assertExternalDirectoryEffect } from "./external-directory"
|
||||||
import DESCRIPTION from "./grep.txt"
|
import DESCRIPTION from "./grep.txt"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { createInterface } from "readline"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { FileTime } from "../file/time"
|
import { FileTime } from "../file"
|
||||||
import DESCRIPTION from "./read.txt"
|
import DESCRIPTION from "./read.txt"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { assertExternalDirectoryEffect } from "./external-directory"
|
import { assertExternalDirectoryEffect } from "./external-directory"
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ import { Effect, Layer, Context } from "effect"
|
||||||
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
|
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
|
||||||
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
||||||
import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
|
||||||
import { Ripgrep } from "../file/ripgrep"
|
import { Ripgrep } from "../file"
|
||||||
import { Format } from "../format"
|
import { Format } from "../format"
|
||||||
import { InstanceState } from "@/effect"
|
import { InstanceState } from "@/effect"
|
||||||
import { Question } from "../question"
|
import { Question } from "../question"
|
||||||
import { Todo } from "../session/todo"
|
import { Todo } from "../session/todo"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { FileTime } from "../file/time"
|
import { FileTime } from "../file"
|
||||||
import { Instruction } from "../session/instruction"
|
import { Instruction } from "../session/instruction"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import z from "zod"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import * as Stream from "effect/Stream"
|
import * as Stream from "effect/Stream"
|
||||||
import { EffectLogger } from "@/effect"
|
import { EffectLogger } from "@/effect"
|
||||||
import { Ripgrep } from "../file/ripgrep"
|
import { Ripgrep } from "../file"
|
||||||
import { Skill } from "../skill"
|
import { Skill } from "../skill"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import { createTwoFilesPatch } from "diff"
|
||||||
import DESCRIPTION from "./write.txt"
|
import DESCRIPTION from "./write.txt"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
import { File } from "../file"
|
import { File } from "../file"
|
||||||
import { FileWatcher } from "../file/watcher"
|
import { FileWatcher } from "../file"
|
||||||
import { Format } from "../format"
|
import { Format } from "../format"
|
||||||
import { FileTime } from "../file/time"
|
import { FileTime } from "../file"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { trimDiff } from "./edit"
|
import { trimDiff } from "./edit"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { test, expect } from "bun:test"
|
import { test, expect } from "bun:test"
|
||||||
import { FileIgnore } from "../../src/file/ignore"
|
import { FileIgnore } from "../../src/file"
|
||||||
|
|
||||||
test("match nested and non-nested", () => {
|
test("match nested and non-nested", () => {
|
||||||
expect(FileIgnore.match("node_modules/index.js")).toBe(true)
|
expect(FileIgnore.match("node_modules/index.js")).toBe(true)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import * as Stream from "effect/Stream"
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file"
|
||||||
|
|
||||||
const run = <A>(effect: Effect.Effect<A, unknown, Ripgrep.Service>) =>
|
const run = <A>(effect: Effect.Effect<A, unknown, Ripgrep.Service>) =>
|
||||||
effect.pipe(Effect.provide(Ripgrep.defaultLayer), Effect.runPromise)
|
effect.pipe(Effect.provide(Ripgrep.defaultLayer), Effect.runPromise)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import fs from "fs/promises"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { SessionID } from "../../src/session/schema"
|
import { SessionID } from "../../src/session/schema"
|
||||||
import { Filesystem } from "../../src/util"
|
import { Filesystem } from "../../src/util"
|
||||||
|
|
@ -43,7 +43,7 @@ const fail = Effect.fn("FileTimeTest.fail")(function* <A, E, R>(self: Effect.Eff
|
||||||
throw new Error("expected file time effect to fail")
|
throw new Error("expected file time effect to fail")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("file/time", () => {
|
describe("file", () => {
|
||||||
describe("read() and get()", () => {
|
describe("read() and get()", () => {
|
||||||
it.live("stores read timestamp", () =>
|
it.live("stores read timestamp", () =>
|
||||||
provideTmpdirInstance((dir) =>
|
provideTmpdirInstance((dir) =>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { ConfigProvider, Deferred, Effect, Layer, ManagedRuntime, Option } from
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Config } from "../../src/config"
|
import { Config } from "../../src/config"
|
||||||
import { FileWatcher } from "../../src/file/watcher"
|
import { FileWatcher } from "../../src/file"
|
||||||
import { Git } from "../../src/git"
|
import { Git } from "../../src/git"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import fs from "fs/promises"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { AppRuntime } from "../../src/effect/app-runtime"
|
import { AppRuntime } from "../../src/effect/app-runtime"
|
||||||
import { FileWatcher } from "../../src/file/watcher"
|
import { FileWatcher } from "../../src/file"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { GlobalBus } from "../../src/bus/global"
|
import { GlobalBus } from "../../src/bus/global"
|
||||||
import { Vcs } from "../../src/project"
|
import { Vcs } from "../../src/project"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { Agent as AgentSvc } from "../../src/agent/agent"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Command } from "../../src/command"
|
import { Command } from "../../src/command"
|
||||||
import { Config } from "../../src/config"
|
import { Config } from "../../src/config"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { LSP } from "../../src/lsp"
|
import { LSP } from "../../src/lsp"
|
||||||
import { MCP } from "../../src/mcp"
|
import { MCP } from "../../src/mcp"
|
||||||
import { Permission } from "../../src/permission"
|
import { Permission } from "../../src/permission"
|
||||||
|
|
@ -38,7 +38,7 @@ import { ToolRegistry } from "../../src/tool/registry"
|
||||||
import { Truncate } from "../../src/tool/truncate"
|
import { Truncate } from "../../src/tool/truncate"
|
||||||
import { Log } from "../../src/util"
|
import { Log } from "../../src/util"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file"
|
||||||
import { Format } from "../../src/format"
|
import { Format } from "../../src/format"
|
||||||
import { provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture"
|
import { provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import { Agent as AgentSvc } from "../../src/agent/agent"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Command } from "../../src/command"
|
import { Command } from "../../src/command"
|
||||||
import { Config } from "../../src/config"
|
import { Config } from "../../src/config"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { LSP } from "../../src/lsp"
|
import { LSP } from "../../src/lsp"
|
||||||
import { MCP } from "../../src/mcp"
|
import { MCP } from "../../src/mcp"
|
||||||
import { Permission } from "../../src/permission"
|
import { Permission } from "../../src/permission"
|
||||||
|
|
@ -54,7 +54,7 @@ import { ToolRegistry } from "../../src/tool/registry"
|
||||||
import { Truncate } from "../../src/tool/truncate"
|
import { Truncate } from "../../src/tool/truncate"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file"
|
||||||
import { Format } from "../../src/format"
|
import { Format } from "../../src/format"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Effect, Layer, ManagedRuntime } from "effect"
|
||||||
import { EditTool } from "../../src/tool/edit"
|
import { EditTool } from "../../src/tool/edit"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { LSP } from "../../src/lsp"
|
import { LSP } from "../../src/lsp"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Format } from "../../src/format"
|
import { Format } from "../../src/format"
|
||||||
|
|
@ -138,7 +138,7 @@ describe("tool.edit", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
const { FileWatcher } = await import("../../src/file/watcher")
|
const { FileWatcher } = await import("../../src/file")
|
||||||
|
|
||||||
const updated = await onceBus(FileWatcher.Event.Updated)
|
const updated = await onceBus(FileWatcher.Event.Updated)
|
||||||
|
|
||||||
|
|
@ -371,7 +371,7 @@ describe("tool.edit", () => {
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
await readFileTime(ctx.sessionID, filepath)
|
await readFileTime(ctx.sessionID, filepath)
|
||||||
|
|
||||||
const { FileWatcher } = await import("../../src/file/watcher")
|
const { FileWatcher } = await import("../../src/file")
|
||||||
|
|
||||||
const updated = await onceBus(FileWatcher.Event.Updated)
|
const updated = await onceBus(FileWatcher.Event.Updated)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Cause, Effect, Exit, Layer } from "effect"
|
||||||
import { GlobTool } from "../../src/tool/glob"
|
import { GlobTool } from "../../src/tool/glob"
|
||||||
import { SessionID, MessageID } from "../../src/session/schema"
|
import { SessionID, MessageID } from "../../src/session/schema"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { Truncate } from "../../src/tool/truncate"
|
import { Truncate } from "../../src/tool/truncate"
|
||||||
import { Agent } from "../../src/agent/agent"
|
import { Agent } from "../../src/agent/agent"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { SessionID, MessageID } from "../../src/session/schema"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { Truncate } from "../../src/tool/truncate"
|
import { Truncate } from "../../src/tool/truncate"
|
||||||
import { Agent } from "../../src/agent/agent"
|
import { Agent } from "../../src/agent/agent"
|
||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import path from "path"
|
||||||
import { Agent } from "../../src/agent/agent"
|
import { Agent } from "../../src/agent/agent"
|
||||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { LSP } from "../../src/lsp"
|
import { LSP } from "../../src/lsp"
|
||||||
import { Permission } from "../../src/permission"
|
import { Permission } from "../../src/permission"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { WriteTool } from "../../src/tool/write"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { LSP } from "../../src/lsp"
|
import { LSP } from "../../src/lsp"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
import { FileTime } from "../../src/file/time"
|
import { FileTime } from "../../src/file"
|
||||||
import { Bus } from "../../src/bus"
|
import { Bus } from "../../src/bus"
|
||||||
import { Format } from "../../src/format"
|
import { Format } from "../../src/format"
|
||||||
import { Truncate } from "../../src/tool/truncate"
|
import { Truncate } from "../../src/tool/truncate"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue