From 8544327f422d48d48972de952a40fbfa80e71c79 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 28 Sep 2025 15:53:45 -0700 Subject: [PATCH] cd and pwd --- app/nose/bin/cat.ts | 3 ++- app/nose/bin/cd.ts | 26 ++++++++++++++++++++++++++ app/nose/bin/edit.tsx | 3 ++- app/nose/bin/ls.ts | 3 ++- app/nose/bin/mkdir.ts | 9 ++++++--- app/nose/bin/pwd.ts | 8 ++++++++ app/nose/bin/rm.ts | 11 ++++++----- app/nose/bin/rmdir.ts | 11 ++++++----- app/nose/bin/touch.ts | 9 ++++++--- app/src/project.ts | 2 +- app/src/session.ts | 16 ++++++++++++++-- 11 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 app/nose/bin/cd.ts create mode 100644 app/nose/bin/pwd.ts diff --git a/app/nose/bin/cat.ts b/app/nose/bin/cat.ts index 4cca806..2c9527a 100644 --- a/app/nose/bin/cat.ts +++ b/app/nose/bin/cat.ts @@ -7,10 +7,11 @@ import { NOSE_WWW } from "app/src/config" import { isBinaryFile } from "app/src/utils" import { highlight } from "../lib/highlight" import { projectName, projectDir } from "@/project" +import { getState } from "@/session" export default async function (path: string) { const project = projectName() - const root = projectDir() + const root = getState("cwd") || projectDir() let files: string[] = [] diff --git a/app/nose/bin/cd.ts b/app/nose/bin/cd.ts new file mode 100644 index 0000000..60d3704 --- /dev/null +++ b/app/nose/bin/cd.ts @@ -0,0 +1,26 @@ +import { join, dirname } from "path" +import { readdirSync } from "fs" +import { projectDir } from "@/project" +import { getState, setState } from "@/session" + +export default async function (path: string) { + if (path.endsWith("/")) path = path.slice(0, path.length - 1) + const cwd = getState("cwd") + const root = cwd || projectDir() + + if (path == "..") { + if (root === projectDir()) return + + setState("cwd", dirname(cwd)) + return + } + + for (const file of readdirSync(root, { withFileTypes: true })) { + if (file.name === path && file.isDirectory()) { + setState("cwd", join(root, file.name)) + return + } + } + + return { error: `${path} doesn't exist` } +} \ No newline at end of file diff --git a/app/nose/bin/edit.tsx b/app/nose/bin/edit.tsx index b748b03..469921e 100644 --- a/app/nose/bin/edit.tsx +++ b/app/nose/bin/edit.tsx @@ -6,10 +6,11 @@ import { NOSE_WWW } from "app/src/config" import { isBinaryFile } from "app/src/utils" import { countChar } from "app/src/shared/utils" import { projectName, projectDir } from "@/project" +import { getState } from "@/session" export default async function (path: string) { const project = projectName() - const root = projectDir() + const root = getState("cwd") || projectDir() let files: string[] = [] diff --git a/app/nose/bin/ls.ts b/app/nose/bin/ls.ts index 78027aa..2a310fb 100644 --- a/app/nose/bin/ls.ts +++ b/app/nose/bin/ls.ts @@ -1,10 +1,11 @@ import { readdirSync } from "fs" import { NOSE_WWW } from "app/src/config" import { projectName, projectDir } from "@/project" +import { getState } from "@/session" export default function () { const project = projectName() - const root = projectDir() + const root = getState("cwd") || projectDir() let files: string[] = [] diff --git a/app/nose/bin/mkdir.ts b/app/nose/bin/mkdir.ts index 83e19dd..f884bdd 100644 --- a/app/nose/bin/mkdir.ts +++ b/app/nose/bin/mkdir.ts @@ -1,13 +1,16 @@ import { mkdirSync } from "fs" import { join } from "path" -import { projectDir, projectFiles } from "@/project" +import { projectDir } from "@/project" +import { readdirSync } from "fs" +import { getState } from "@/session" export default async function (path: string) { if (path.endsWith("/")) path = path.slice(0, path.length - 1) - for (const file of projectFiles()) + const root = getState("cwd") || projectDir() + for (const file of readdirSync(root, { withFileTypes: true })) if (file.name === path) throw `${path} exists` - mkdirSync(join(projectDir(), path), { recursive: true }) + mkdirSync(join(root, path), { recursive: true }) return `${path} created` } \ No newline at end of file diff --git a/app/nose/bin/pwd.ts b/app/nose/bin/pwd.ts new file mode 100644 index 0000000..72aa856 --- /dev/null +++ b/app/nose/bin/pwd.ts @@ -0,0 +1,8 @@ +import { dirname } from "path" +import { projectDir } from "@/project" +import { getState } from "@/session" + +export default async function () { + const root = projectDir() + return (getState("cwd") || root).replace(dirname(root), "") +} \ No newline at end of file diff --git a/app/nose/bin/rm.ts b/app/nose/bin/rm.ts index b49be23..5efd262 100644 --- a/app/nose/bin/rm.ts +++ b/app/nose/bin/rm.ts @@ -1,12 +1,14 @@ -import { unlinkSync } from "fs" +import { unlinkSync, readdirSync } from "fs" import { join } from "path" -import { projectDir, projectFiles } from "@/project" +import { projectDir } from "@/project" +import { getState } from "@/session" export default function (path: string) { let target = "" if (path.endsWith("/")) path = path.slice(0, path.length - 1) - for (const file of projectFiles()) { + const root = getState("cwd") || projectDir() + for (const file of readdirSync(root, { withFileTypes: true })) if (file.name === path) { if (file.isDirectory()) return { error: "Use `rmdir` to remove directory" } @@ -14,11 +16,10 @@ export default function (path: string) { target = file.name break } - } if (!target) return { error: `${path} not found` } - unlinkSync(join(projectDir(), path)) + unlinkSync(join(root, path)) return `${path} removed` } \ No newline at end of file diff --git a/app/nose/bin/rmdir.ts b/app/nose/bin/rmdir.ts index 480d030..f8f0d97 100644 --- a/app/nose/bin/rmdir.ts +++ b/app/nose/bin/rmdir.ts @@ -1,12 +1,14 @@ -import { rmdirSync } from "fs" +import { rmdirSync, readdirSync } from "fs" import { join } from "path" -import { projectDir, projectFiles } from "@/project" +import { projectDir } from "@/project" +import { getState } from "@/session" export default function (path: string) { let target = "" if (path.endsWith("/")) path = path.slice(0, path.length - 1) - for (const file of projectFiles()) { + const root = getState("cwd") || projectDir() + for (const file of readdirSync(root, { withFileTypes: true })) if (file.name === path) { if (file.isFile()) return { error: "Use `rm` to remove files" } @@ -14,11 +16,10 @@ export default function (path: string) { target = file.name break } - } if (!target) return { error: `${path} not found` } - rmdirSync(join(projectDir(), path), { recursive: true }) + rmdirSync(join(root, path), { recursive: true }) return `${path} removed` } \ No newline at end of file diff --git a/app/nose/bin/touch.ts b/app/nose/bin/touch.ts index 404926a..6770e8c 100644 --- a/app/nose/bin/touch.ts +++ b/app/nose/bin/touch.ts @@ -1,12 +1,15 @@ import { join } from "path" -import { projectDir, projectFiles } from "@/project" +import { readdirSync } from "fs" +import { projectDir } from "@/project" +import { getState } from "@/session" export default async function (path: string) { if (path.endsWith("/")) path = path.slice(0, path.length - 1) - for (const file of projectFiles()) + const root = getState("cwd") || projectDir() + for (const file of readdirSync(root, { withFileTypes: true })) if (file.name === path) throw `${path} exists` - await Bun.write(join(projectDir(), path), "") + await Bun.write(join(root, path), "") return `${path} created` } \ No newline at end of file diff --git a/app/src/project.ts b/app/src/project.ts index 91ce1ec..800d07c 100644 --- a/app/src/project.ts +++ b/app/src/project.ts @@ -23,5 +23,5 @@ export function projectDir(): string { } export function projectFiles(): Dirent[] { - return readdirSync(projectDir(), { withFileTypes: true }) + return readdirSync(projectDir(), { recursive: true, withFileTypes: true }) } \ No newline at end of file diff --git a/app/src/session.ts b/app/src/session.ts index e6caa8a..a42a52f 100644 --- a/app/src/session.ts +++ b/app/src/session.ts @@ -7,6 +7,7 @@ export type Session = { taskId?: string sessionId?: string project?: string + cwd?: string ws?: any } @@ -14,6 +15,17 @@ export type Session = { const g = globalThis as typeof globalThis & { __thread?: AsyncLocalStorage } export const ALS = g.__thread ??= new AsyncLocalStorage() -export function getState(): Session | undefined { - return ALS.getStore() +export function getState(key?: keyof Session): Session | any | undefined { + const store = ALS.getStore() + if (!store) return + + if (key) return store[key] + + return store +} + +export function setState(key: keyof Session, value: any) { + const store = ALS.getStore() + if (!store) return + store[key] = value } \ No newline at end of file