resurf 1.0.6 → 1.0.7

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/dist/index.js CHANGED
@@ -12,6 +12,12 @@ import {
12
12
  stripCompactFields
13
13
  } from "./chunk-MIX6QPSY.js";
14
14
 
15
+ // src/index.ts
16
+ import fs3 from "fs";
17
+ import os2 from "os";
18
+ import path3 from "path";
19
+ import { fileURLToPath } from "url";
20
+
15
21
  // src/reader.ts
16
22
  import * as fs2 from "fs/promises";
17
23
  import * as path2 from "path";
@@ -765,6 +771,50 @@ async function cmdExport(reader, args, flags) {
765
771
  Exported ${captures.length} captures (use --json or --md for piping)`);
766
772
  }
767
773
  }
774
+ var SKILL_NAME = "resurf";
775
+ function getBundledSkillPath() {
776
+ const dir = path3.dirname(fileURLToPath(import.meta.url));
777
+ return path3.join(dir, "..", "skill", "SKILL.md");
778
+ }
779
+ async function cmdInstallSkill(args) {
780
+ const cursor = hasFlag(args, "--cursor");
781
+ const codex = hasFlag(args, "--codex");
782
+ const claude = hasFlag(args, "--claude");
783
+ const all = !cursor && !codex && !claude;
784
+ const toCursor = all || cursor;
785
+ const toCodex = all || codex;
786
+ const toClaude = all || claude;
787
+ const skillPath = getBundledSkillPath();
788
+ if (!fs3.existsSync(skillPath)) {
789
+ console.error(`Skill file not found: ${skillPath}`);
790
+ process.exit(1);
791
+ }
792
+ const content = fs3.readFileSync(skillPath, "utf-8");
793
+ const homedir2 = os2.homedir();
794
+ if (toCursor) {
795
+ const dir = path3.join(homedir2, ".cursor", "skills", SKILL_NAME);
796
+ fs3.mkdirSync(dir, { recursive: true });
797
+ fs3.writeFileSync(path3.join(dir, "SKILL.md"), content, "utf-8");
798
+ console.log(`Installed Resurf skill for Cursor: ${dir}`);
799
+ }
800
+ if (toCodex) {
801
+ const codexHome = process.env.CODEX_HOME ?? path3.join(homedir2, ".codex");
802
+ const dir = path3.join(codexHome, "skills", SKILL_NAME);
803
+ fs3.mkdirSync(dir, { recursive: true });
804
+ fs3.writeFileSync(path3.join(dir, "SKILL.md"), content, "utf-8");
805
+ console.log(`Installed Resurf skill for Codex: ${dir}`);
806
+ }
807
+ if (toClaude) {
808
+ const claudeHome = process.env.CLAUDE_HOME ?? path3.join(homedir2, ".claude");
809
+ const dir = path3.join(claudeHome, "skills", SKILL_NAME);
810
+ fs3.mkdirSync(dir, { recursive: true });
811
+ fs3.writeFileSync(path3.join(dir, "SKILL.md"), content, "utf-8");
812
+ console.log(`Installed Resurf skill for Claude Code: ${dir}`);
813
+ }
814
+ if (toCursor || toCodex || toClaude) {
815
+ console.log("Restart the editor to pick up the skill.");
816
+ }
817
+ }
768
818
  function printHelp() {
769
819
  const help = `
770
820
  resurf - Access Resurf vault data from the command line
@@ -786,6 +836,7 @@ COMMANDS
786
836
  open <id> Open a capture in Resurf app
787
837
  create Create a capture via Resurf protocol
788
838
  export Export captures as JSON
839
+ install-skill Install Resurf skill for Cursor and/or Codex
789
840
 
790
841
  GLOBAL OPTIONS
791
842
  --vault <path> Path to Resurf vault (auto-detected if omitted)
@@ -831,6 +882,12 @@ VAULTS OPTIONS
831
882
  [directory] Scan this directory recursively for vaults
832
883
  --verbose Log skipped inaccessible paths
833
884
 
885
+ INSTALL-SKILL OPTIONS
886
+ --cursor Install only for Cursor (~/.cursor/skills/resurf)
887
+ --codex Install only for Codex ($CODEX_HOME/skills/resurf, default ~/.codex)
888
+ --claude Install only for Claude Code ($CLAUDE_HOME/skills/resurf, default ~/.claude)
889
+ (no flags) Install for Cursor, Codex, and Claude Code
890
+
834
891
  EXAMPLES
835
892
  resurf list --limit 10 --json
836
893
  resurf list --space "UI" --type image --json --compact
@@ -867,6 +924,14 @@ async function main() {
867
924
  await cmdCreate(commandArgs);
868
925
  return;
869
926
  }
927
+ if (command === "install-skill") {
928
+ await cmdInstallSkill(commandArgs);
929
+ return;
930
+ }
931
+ if (command === "skill" && commandArgs[0] === "install") {
932
+ await cmdInstallSkill(commandArgs.slice(1));
933
+ return;
934
+ }
870
935
  if (command === "vaults" || command === "find-vaults") {
871
936
  const scanDir = commandArgs.find((a) => !a.startsWith("-"));
872
937
  const vaults = await findVaults({ scanDir, verbose: flags.verbose });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reader.ts","../src/vault.ts","../src/index.ts"],"sourcesContent":["import * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { type VaultStructure, getVaultStructure } from \"./vault.js\";\n\nexport type CaptureIndex = {\n id: string;\n type: string;\n filePath?: string;\n createdAt: number;\n updatedAt: number;\n contentType?: string | string[];\n title?: string;\n tags?: string[];\n spaceKeys?: string[];\n mimeTypes?: string[];\n note?: string;\n tldr?: string;\n source?: string;\n isPinned?: boolean;\n isHidden?: boolean;\n triageStatus?: string;\n processingStatus?: string;\n};\n\nexport type Space = {\n id: string;\n name: string;\n key: string;\n areaKey?: string;\n icon?: string;\n color?: string;\n createdAt: number;\n updatedAt: number;\n noteCount?: number;\n isPinned?: boolean;\n};\n\nexport type Area = {\n id: string;\n name: string;\n key: string;\n createdAt: number;\n updatedAt: number;\n};\n\nexport type Canvas = {\n id: string;\n name: string;\n createdAt: number;\n updatedAt: number;\n nodes: unknown[];\n connections: unknown[];\n annotations: unknown[];\n};\n\nexport type Capture = CaptureIndex & {\n content: Record<string, unknown>;\n author?: Record<string, unknown>;\n colorPalette?: unknown[];\n embedding?: number[];\n};\n\ntype IndexMap<T> = Record<string, T>;\n\nasync function readJson<T>(filePath: string): Promise<T | null> {\n try {\n const data = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n\nfunction contentToSearchableString(capture: Capture): string {\n const parts: string[] = [];\n const content = capture.content;\n if (!content) return \"\";\n if (typeof content.textContent === \"string\") parts.push(content.textContent);\n if (content.noteContent != null)\n parts.push(\n typeof content.noteContent === \"string\"\n ? content.noteContent\n : JSON.stringify(content.noteContent)\n );\n if (typeof content.url === \"string\") parts.push(content.url);\n if (typeof content.ogTitle === \"string\") parts.push(content.ogTitle);\n if (typeof content.ogDescription === \"string\") parts.push(content.ogDescription);\n if (typeof content.articleContent === \"string\") parts.push(content.articleContent);\n if (typeof content.path === \"string\") parts.push(content.path);\n return parts.join(\" \");\n}\n\nfunction buildIndexHaystack(\n c: CaptureIndex,\n spacesByKey: Map<string, Space>,\n areasByKey: Map<string, Area>\n): string {\n const parts = [\n c.title,\n c.note,\n c.tldr,\n c.source,\n c.filePath,\n ...(c.tags ?? []),\n ].filter((v): v is string => typeof v === \"string\");\n for (const spaceKey of c.spaceKeys ?? []) {\n const space = spacesByKey.get(spaceKey);\n if (space?.name) parts.push(space.name);\n if (space?.areaKey) {\n const area = areasByKey.get(space.areaKey);\n if (area?.name) parts.push(area.name);\n }\n }\n return parts.join(\" \").toLowerCase();\n}\n\nexport class VaultReader {\n private structure: VaultStructure;\n\n constructor(vaultPath: string) {\n this.structure = getVaultStructure(vaultPath);\n }\n\n get vaultPath(): string {\n return this.structure.root;\n }\n\n async getCaptureIndex(): Promise<CaptureIndex[]> {\n const indexPath = path.join(this.structure.index, \"index.captures.json\");\n const data = await readJson<IndexMap<CaptureIndex>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getSpaces(): Promise<Space[]> {\n const indexPath = path.join(this.structure.index, \"index.spaces.json\");\n const data = await readJson<IndexMap<Space>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getAreas(): Promise<Area[]> {\n const indexPath = path.join(this.structure.index, \"index.areas.json\");\n const data = await readJson<IndexMap<Area>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getCanvases(): Promise<Canvas[]> {\n const indexPath = path.join(this.structure.index, \"index.canvases.json\");\n const data = await readJson<IndexMap<Canvas>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getTags(): Promise<string[]> {\n const indexPath = path.join(this.structure.index, \"index.tags.json\");\n const data = await readJson<string[]>(indexPath);\n return data ?? [];\n }\n\n async getCaptureById(id: string): Promise<Capture | null> {\n const capturePath = path.join(this.structure.captures, `${id}.json`);\n return readJson<Capture>(capturePath);\n }\n\n async listCaptures(\n options: {\n limit?: number;\n offset?: number;\n spaceKey?: string;\n tag?: string;\n contentType?: string;\n search?: string;\n searchTerms?: string[];\n searchInContent?: boolean;\n pinned?: boolean;\n sortBy?: \"created\" | \"updated\";\n sortOrder?: \"asc\" | \"desc\";\n } = {}\n ): Promise<{ captures: CaptureIndex[]; total: number }> {\n let captures = await this.getCaptureIndex();\n\n captures = captures.filter((c) => !c.isHidden);\n\n if (options.spaceKey) {\n captures = captures.filter((c) => c.spaceKeys?.includes(options.spaceKey!));\n }\n\n if (options.tag) {\n captures = captures.filter((c) => c.tags?.includes(options.tag!));\n }\n\n if (options.contentType) {\n captures = captures.filter((c) => {\n const ct = c.contentType;\n if (Array.isArray(ct)) return ct.includes(options.contentType!);\n return ct === options.contentType;\n });\n }\n\n if (options.pinned !== undefined) {\n captures = captures.filter((c) => c.isPinned === options.pinned);\n }\n\n const terms = options.searchTerms?.filter((t) => t.trim().length > 0).map((t) => t.trim()) ?? [];\n const singleSearch = options.search?.trim();\n const hasSearch = terms.length > 0 || (singleSearch != null && singleSearch.length > 0);\n\n const normalizeTerm = (t: string): string =>\n (t.startsWith(\"@\") ? t.slice(1) : t).toLowerCase();\n const termsNormalized = terms.map(normalizeTerm);\n const singleNorm = singleSearch ? singleSearch.toLowerCase() : null;\n\n let spacesByKey = new Map<string, Space>();\n let areasByKey = new Map<string, Area>();\n if (hasSearch) {\n const [spaces, areas] = await Promise.all([this.getSpaces(), this.getAreas()]);\n spacesByKey = new Map(spaces.map((s) => [s.key, s]));\n areasByKey = new Map(areas.map((a) => [a.key, a]));\n }\n const indexHaystack = (c: CaptureIndex): string =>\n buildIndexHaystack(c, spacesByKey, areasByKey);\n\n if (hasSearch && options.searchInContent) {\n const sortField = options.sortBy === \"updated\" ? \"updatedAt\" : \"createdAt\";\n const sortDir = options.sortOrder === \"asc\" ? 1 : -1;\n captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);\n const toMatch = termsNormalized.length > 0 ? termsNormalized : singleNorm ? [singleNorm] : [];\n const matches: CaptureIndex[] = [];\n const maxScan = 2000;\n for (let i = 0; i < Math.min(captures.length, maxScan); i++) {\n const c = captures[i];\n const full = await this.getCaptureById(c.id);\n if (!full) continue;\n const contentHaystack = contentToSearchableString(full);\n const haystack = (indexHaystack(c) + \" \" + contentHaystack).toLowerCase();\n const hit = toMatch.some((q) => haystack.includes(q));\n if (hit) matches.push(c);\n }\n const total = matches.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 20;\n return {\n captures: matches.slice(offset, offset + limit),\n total,\n };\n }\n\n if (terms.length > 0) {\n captures = captures.filter((c) => {\n const haystack = indexHaystack(c);\n return terms.some((term) => haystack.includes(normalizeTerm(term)));\n });\n } else if (singleSearch) {\n const q = singleSearch.toLowerCase();\n captures = captures.filter((c) => indexHaystack(c).includes(q));\n }\n\n const sortField = options.sortBy === \"updated\" ? \"updatedAt\" : \"createdAt\";\n const sortDir = options.sortOrder === \"asc\" ? 1 : -1;\n captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);\n\n const total = captures.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 20;\n\n return {\n captures: captures.slice(offset, offset + limit),\n total,\n };\n }\n\n async getStats(): Promise<{\n totalCaptures: number;\n totalSpaces: number;\n totalAreas: number;\n totalCanvases: number;\n totalTags: number;\n byContentType: Record<string, number>;\n byTriageStatus: Record<string, number>;\n }> {\n const [captures, spaces, areas, canvases, tags] = await Promise.all([\n this.getCaptureIndex(),\n this.getSpaces(),\n this.getAreas(),\n this.getCanvases(),\n this.getTags(),\n ]);\n\n const byContentType: Record<string, number> = {};\n const byTriageStatus: Record<string, number> = {};\n\n for (const c of captures) {\n const types = Array.isArray(c.contentType)\n ? c.contentType\n : c.contentType\n ? [c.contentType]\n : [\"unknown\"];\n for (const t of types) {\n byContentType[t] = (byContentType[t] ?? 0) + 1;\n }\n const status = c.triageStatus ?? \"none\";\n byTriageStatus[status] = (byTriageStatus[status] ?? 0) + 1;\n }\n\n return {\n totalCaptures: captures.length,\n totalSpaces: spaces.length,\n totalAreas: areas.length,\n totalCanvases: canvases.length,\n totalTags: tags.length,\n byContentType,\n byTriageStatus,\n };\n }\n\n async getVaultSize(): Promise<{ files: number; bytes: number }> {\n let files = 0;\n let bytes = 0;\n\n const walk = async (dir: string): Promise<void> => {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n } else {\n files++;\n try {\n const stat = await fs.stat(full);\n bytes += stat.size;\n } catch {}\n }\n }\n } catch {}\n };\n\n await walk(this.structure.root);\n return { files, bytes };\n }\n}\n","import * as os from \"os\";\nimport * as path from \"path\";\nimport * as fs from \"fs/promises\";\n\nconst VAULT_NAMES = [\"Resurf Vault\", \"Resurf Vault Dev\"];\nconst APP_NAMES = [\n \"so.resurf.app\",\n \"so.resurf.app.dev\",\n \"Resurf\",\n \"Resurf Dev\",\n \"desktop\",\n];\nconst CONFIG_FILES = [\"app.json\", \"app.dev.json\"];\n\nexport type VaultStructure = {\n root: string;\n captures: string;\n attachments: string;\n index: string;\n config: string;\n trash: string;\n};\n\nexport function getVaultStructure(vaultPath: string): VaultStructure {\n const root = path.resolve(vaultPath);\n return {\n root,\n captures: path.join(root, \"captures\"),\n attachments: path.join(root, \"attachments\"),\n index: path.join(root, \".index\"),\n config: path.join(root, \".config\"),\n trash: path.join(root, \".trash\"),\n };\n}\n\nexport async function hasVaultConfig(dir: string): Promise<boolean> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n await fs.access(configPath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function getVaultNameFromConfig(dir: string): Promise<string> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { name?: string };\n return config.name ?? path.basename(dir);\n } catch {\n return path.basename(dir);\n }\n}\n\nasync function getCurrentVaultFromAppConfig(): Promise<{\n path: string;\n valid: boolean;\n} | null> {\n const home = os.homedir();\n const base = path.join(home, \"Library\", \"Application Support\");\n for (const appName of APP_NAMES) {\n for (const configFile of CONFIG_FILES) {\n const configPath = path.join(base, appName, configFile);\n try {\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { vaultPath?: string };\n const vaultPath = config.vaultPath?.trim();\n if (vaultPath) {\n const valid = await hasVaultConfig(vaultPath);\n return { path: vaultPath, valid };\n }\n } catch {\n // ignore\n }\n }\n }\n return null;\n}\n\nfunction getDefaultSearchDirs(): string[] {\n const home = os.homedir();\n const dirs: string[] = [];\n const documents = path.join(home, \"Documents\");\n dirs.push(documents);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(documents, name));\n }\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n dirs.push(iCloud);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(iCloud, name));\n }\n return dirs;\n}\n\nasync function scanDirForVaults(\n dir: string,\n vaults: VaultInfo[],\n seen: Set<string>,\n currentResolved: string | null,\n verbose: boolean\n): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const ent of entries) {\n if (!ent.isDirectory() || ent.name.startsWith(\".\")) continue;\n const sub = path.join(dir, ent.name);\n try {\n if (await hasVaultConfig(sub)) {\n const resolved = path.resolve(sub);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(sub);\n vaults.push({\n path: resolved,\n name,\n current: currentResolved === resolved,\n });\n seen.add(resolved);\n }\n } else {\n const nested = await fs.readdir(sub, { withFileTypes: true }).catch(() => []);\n for (const n of nested) {\n if (!n.isDirectory() || n.name.startsWith(\".\")) continue;\n const subSub = path.join(sub, n.name);\n if (await hasVaultConfig(subSub)) {\n const resolved = path.resolve(subSub);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(subSub);\n vaults.push({\n path: resolved,\n name,\n current: currentResolved === resolved,\n });\n seen.add(resolved);\n }\n }\n }\n }\n } catch {\n if (verbose) console.error(\"Skipping (inaccessible):\", sub);\n }\n }\n } catch (err) {\n if (verbose) console.error(\"Skipping (inaccessible):\", dir, err);\n }\n}\n\nexport type VaultInfo = {\n path: string;\n name: string;\n current: boolean;\n};\n\nexport async function findVaults(options?: {\n scanDir?: string;\n verbose?: boolean;\n}): Promise<VaultInfo[]> {\n const vaults: VaultInfo[] = [];\n const seen = new Set<string>();\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n const name = await getVaultNameFromConfig(current.path);\n vaults.push({ path: path.resolve(current.path), name, current: true });\n seen.add(path.resolve(current.path));\n }\n\n const currentResolvedForScan = current?.path ? path.resolve(current.path) : null;\n const verbose = options?.verbose ?? false;\n const searchDirs = options?.scanDir ? [options.scanDir] : getDefaultSearchDirs();\n\n for (const dir of searchDirs) {\n try {\n await fs.access(dir);\n } catch {\n if (verbose) console.error(\"Skipping (inaccessible):\", dir);\n continue;\n }\n if (await hasVaultConfig(dir)) {\n const resolved = path.resolve(dir);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(dir);\n vaults.push({\n path: resolved,\n name,\n current: currentResolvedForScan === resolved,\n });\n seen.add(resolved);\n }\n } else if (options?.scanDir) {\n await findVaultsRecursive(dir, vaults, seen, currentResolvedForScan);\n } else {\n const base = path.basename(dir);\n if (base === \"Documents\" || base === \"com~apple~CloudDocs\") {\n await scanDirForVaults(dir, vaults, seen, currentResolvedForScan, verbose);\n }\n }\n }\n\n const currentResolved = current?.path ? path.resolve(current.path) : null;\n const normalized = vaults.map((v) => ({\n ...v,\n path: path.resolve(v.path),\n current: currentResolved === path.resolve(v.path),\n }));\n return Array.from(new Map(normalized.map((v) => [v.path, v])).values());\n}\n\nasync function findVaultsRecursive(\n dir: string,\n results: VaultInfo[],\n seen: Set<string>,\n currentResolved: string | null\n): Promise<void> {\n try {\n const stat = await fs.stat(dir);\n if (!stat.isDirectory()) return;\n if (seen.has(dir)) return;\n seen.add(dir);\n\n if (await hasVaultConfig(dir)) {\n const name = await getVaultNameFromConfig(dir);\n results.push({\n path: path.resolve(dir),\n name,\n current: currentResolved === path.resolve(dir),\n });\n return;\n }\n\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);\n for (const ent of entries) {\n if (ent.isDirectory() && !ent.name.startsWith(\".\")) {\n const child = path.join(dir, ent.name);\n await findVaultsRecursive(child, results, seen, currentResolved);\n }\n }\n } catch {\n // skip inaccessible dirs\n }\n}\n\nexport async function discoverVaultPath(override?: string): Promise<string> {\n if (override?.trim()) {\n const resolved = path.resolve(override);\n const valid = await hasVaultConfig(resolved);\n if (!valid) {\n throw new Error(`Not a valid Resurf vault: ${resolved}`);\n }\n return resolved;\n }\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n return path.resolve(current.path);\n }\n\n const home = os.homedir();\n const documents = path.join(home, \"Documents\");\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n\n for (const name of VAULT_NAMES) {\n for (const parent of [documents, iCloud]) {\n const candidate = path.join(parent, name);\n try {\n await fs.access(candidate);\n if (await hasVaultConfig(candidate)) {\n return path.resolve(candidate);\n }\n } catch {\n // skip\n }\n }\n }\n\n const fallback = path.join(documents, VAULT_NAMES[0]);\n throw new Error(\n `No Resurf vault found. Set one with --vault <path>, or create a vault at ${fallback}`\n );\n}\n","import { VaultReader } from \"./reader.js\";\nimport { discoverVaultPath, findVaults } from \"./vault.js\";\nimport {\n type FormatOptions,\n type OutputFormat,\n captureWithAbsoluteAttachmentPaths,\n formatAreas,\n formatCanvases,\n formatCapture,\n formatCaptureList,\n formatSpaces,\n formatStats,\n formatTags,\n formatVaults,\n stripCompactFields,\n} from \"./format.js\";\n\nconst VERSION = \"1.0.0\";\n\ntype GlobalFlags = {\n vault?: string;\n format: OutputFormat;\n verbose?: boolean;\n compact?: boolean;\n};\n\nfunction parseGlobalFlags(args: string[]): { flags: GlobalFlags; rest: string[] } {\n const flags: GlobalFlags = { format: \"pretty\" };\n const rest: string[] = [];\n let i = 0;\n\n while (i < args.length) {\n const arg = args[i];\n if (arg === \"--vault\" && i + 1 < args.length) {\n flags.vault = args[i + 1];\n i += 2;\n } else if (arg === \"--json\") {\n flags.format = \"json\";\n i++;\n } else if (arg === \"--md\" || arg === \"--markdown\") {\n flags.format = \"md\";\n i++;\n } else if (arg === \"--table\") {\n flags.format = \"table\";\n i++;\n } else if (arg === \"--format\" && i + 1 < args.length) {\n flags.format = args[i + 1] as OutputFormat;\n i += 2;\n } else if (arg === \"--verbose\") {\n flags.verbose = true;\n i++;\n } else if (arg === \"--compact\") {\n flags.compact = true;\n i++;\n } else {\n rest.push(arg);\n i++;\n }\n }\n\n return { flags, rest };\n}\n\nfunction extractFlag(args: string[], flag: string): string | undefined {\n const idx = args.indexOf(flag);\n if (idx === -1 || idx + 1 >= args.length) return undefined;\n return args[idx + 1];\n}\n\nfunction extractFlags(args: string[], flag: string): string[] {\n const out: string[] = [];\n for (let i = 0; i < args.length - 1; i++) {\n if (args[i] === flag) out.push(args[i + 1]);\n }\n return out;\n}\n\nfunction hasFlag(args: string[], flag: string): boolean {\n return args.includes(flag);\n}\n\nfunction extractNumber(args: string[], flag: string, defaultVal: number): number {\n const val = extractFlag(args, flag);\n if (!val) return defaultVal;\n const n = parseInt(val, 10);\n return isNaN(n) ? defaultVal : n;\n}\n\nimport type { Capture, CaptureIndex } from \"./reader.js\";\n\nasync function resolveSpaceKey(\n reader: VaultReader,\n input: string | undefined\n): Promise<string | undefined> {\n if (!input) return undefined;\n const spaces = await reader.getSpaces();\n if (spaces.some((s) => s.key === input)) return input;\n const lower = input.toLowerCase();\n const matches = spaces.filter((s) => s.name.toLowerCase() === lower);\n if (matches.length === 1) return matches[0].key;\n if (matches.length > 1) {\n const keys = matches.map((s) => s.key).join(\", \");\n console.error(`Ambiguous space name \"${input}\". Matching keys: ${keys}`);\n process.exit(1);\n }\n const partial = spaces.filter((s) => s.name.toLowerCase().includes(lower));\n if (partial.length === 1) return partial[0].key;\n if (partial.length > 1) {\n const suggestions = partial.map((s) => `${s.key} (${s.name})`).join(\", \");\n console.error(`Ambiguous space name \"${input}\". Did you mean: ${suggestions}`);\n process.exit(1);\n }\n return input;\n}\n\nasync function loadFullCaptures(\n reader: VaultReader,\n indexItems: CaptureIndex[]\n): Promise<Capture[]> {\n const results = await Promise.all(\n indexItems.map((item) => reader.getCaptureById(item.id))\n );\n return results\n .filter((c): c is Capture => c !== null)\n .map((c) => captureWithAbsoluteAttachmentPaths(c, reader.vaultPath));\n}\n\nasync function resolveAttachmentPaths(\n reader: VaultReader,\n indexItems: CaptureIndex[]\n): Promise<string[]> {\n const captures = await loadFullCaptures(reader, indexItems);\n return captures\n .filter((c) => c.content?.type === \"attachment\" && typeof c.content.path === \"string\")\n .map((c) => String(c.content.path));\n}\n\nasync function cmdList(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const limit = extractNumber(args, \"--limit\", 20);\n const offset = extractNumber(args, \"--offset\", 0);\n const spaceKey = await resolveSpaceKey(reader, extractFlag(args, \"--space\"));\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n const searchTermsList = [\n ...extractFlags(args, \"--search\"),\n ...extractFlags(args, \"-s\"),\n ];\n const searchTerms =\n searchTermsList.length > 0 ? searchTermsList : undefined;\n const search =\n searchTerms == null ? (extractFlag(args, \"--search\") ?? extractFlag(args, \"-s\")) : undefined;\n const searchInContent = hasFlag(args, \"--search-content\");\n const pinned = hasFlag(args, \"--pinned\") ? true : undefined;\n const sortBy = (extractFlag(args, \"--sort\") as \"created\" | \"updated\") ?? \"created\";\n const sortOrder = hasFlag(args, \"--asc\") ? (\"asc\" as const) : (\"desc\" as const);\n const includeContent = hasFlag(args, \"--include-content\");\n const pathsOnly = hasFlag(args, \"--paths\");\n\n const result = await reader.listCaptures({\n limit,\n offset,\n spaceKey,\n tag,\n contentType,\n search,\n searchTerms,\n searchInContent,\n pinned,\n sortBy,\n sortOrder,\n });\n\n if (pathsOnly) {\n const paths = await resolveAttachmentPaths(reader, result.captures);\n console.log(paths.join(\"\\n\"));\n return;\n }\n\n if (includeContent) {\n const fullCaptures = await loadFullCaptures(reader, result.captures);\n const fmtOpts: FormatOptions = { compact: flags.compact, vaultPath: reader.vaultPath };\n console.log(formatCaptureList(fullCaptures, result.total, flags.format, fmtOpts));\n return;\n }\n\n const fmtOpts: FormatOptions = { compact: flags.compact };\n console.log(formatCaptureList(result.captures, result.total, flags.format, fmtOpts));\n}\n\nasync function resolveCaptureById(reader: VaultReader, id: string): Promise<Capture | null> {\n let capture = await reader.getCaptureById(id);\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) capture = await reader.getCaptureById(match.id);\n }\n return capture;\n}\n\nasync function cmdGet(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const ids = args.filter((a) => !a.startsWith(\"-\"));\n if (ids.length === 0) {\n console.error(\"Usage: resurf get <capture-id> [id2 ...]\");\n process.exit(1);\n }\n\n const fmtOpts: FormatOptions = { compact: flags.compact, vaultPath: reader.vaultPath };\n\n if (ids.length === 1) {\n const capture = await resolveCaptureById(reader, ids[0]);\n if (!capture) {\n console.error(`Capture not found: ${ids[0]}`);\n process.exit(1);\n }\n console.log(formatCapture(capture, flags.format, fmtOpts));\n return;\n }\n\n const captures = await Promise.all(ids.map((id) => resolveCaptureById(reader, id)));\n const found = captures.filter(Boolean) as Capture[];\n\n if (found.length === 0) {\n console.error(\"No captures found for the given IDs.\");\n process.exit(1);\n }\n\n if (flags.format === \"json\") {\n let items: Record<string, unknown>[] = found.map((c) =>\n captureWithAbsoluteAttachmentPaths(c, reader.vaultPath)\n );\n if (flags.compact) items = items.map((c) => stripCompactFields(c));\n console.log(JSON.stringify(items, null, 2));\n } else {\n for (const c of found) {\n console.log(formatCapture(c, flags.format, fmtOpts));\n console.log(\"\");\n }\n }\n}\n\nasync function cmdSearch(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const positional: string[] = [];\n let i = 0;\n while (i < args.length) {\n if (args[i].startsWith(\"--\") && i + 1 < args.length) {\n i += 2;\n } else if (args[i].startsWith(\"--\")) {\n i++;\n } else {\n positional.push(args[i]);\n i++;\n }\n }\n\n const limit = extractNumber(args, \"--limit\", 20);\n const searchInContent = hasFlag(args, \"--search-content\");\n if (positional.length === 0) {\n console.error(\"Usage: resurf search <term> [term2 ...]\");\n process.exit(1);\n }\n\n const result = await reader.listCaptures({\n searchTerms: positional,\n searchInContent,\n limit,\n });\n const fmtOpts: FormatOptions = { compact: flags.compact };\n console.log(formatCaptureList(result.captures, result.total, flags.format, fmtOpts));\n}\n\nasync function cmdSpaces(\n reader: VaultReader,\n _args: string[],\n flags: GlobalFlags\n): Promise<void> {\n const spaces = await reader.getSpaces();\n console.log(formatSpaces(spaces, flags.format));\n}\n\nasync function cmdAreas(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const areas = await reader.getAreas();\n console.log(formatAreas(areas, flags.format));\n}\n\nasync function cmdTags(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const tags = await reader.getTags();\n console.log(formatTags(tags, flags.format));\n}\n\nasync function cmdCanvases(\n reader: VaultReader,\n _args: string[],\n flags: GlobalFlags\n): Promise<void> {\n const canvases = await reader.getCanvases();\n console.log(formatCanvases(canvases, flags.format));\n}\n\nasync function cmdStats(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const stats = await reader.getStats();\n console.log(formatStats(stats, reader.vaultPath, flags.format));\n}\n\nasync function cmdOpen(reader: VaultReader, args: string[]): Promise<void> {\n const id = args[0];\n if (!id) {\n console.error(\"Usage: resurf open <capture-id>\");\n process.exit(1);\n }\n\n let captureId = id;\n const capture = await reader.getCaptureById(id);\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) captureId = match.id;\n }\n\n const { exec } = await import(\"child_process\");\n const url = `resurf://capture/${captureId}`;\n exec(`open \"${url}\"`);\n console.log(`Opening capture in Resurf: ${captureId}`);\n}\n\nasync function cmdCreate(args: string[]): Promise<void> {\n const content = extractFlag(args, \"--content\") ?? extractFlag(args, \"--text\");\n const url = extractFlag(args, \"--url\");\n const title = extractFlag(args, \"--title\");\n const tags = extractFlag(args, \"--tags\");\n const source = extractFlag(args, \"--source\");\n const space = extractFlag(args, \"--space\");\n\n if (!content && !url) {\n console.error(\n \"Usage: resurf create --content <text> [--url <url>] [--title <title>] [--tags <tags>]\"\n );\n process.exit(1);\n }\n\n const params = new URLSearchParams();\n if (content) params.set(\"content\", content);\n if (url) params.set(\"url\", url);\n if (title) params.set(\"title\", title);\n if (tags) params.set(\"tags\", tags);\n if (source) params.set(\"source\", source);\n if (space) params.set(\"space\", space);\n\n const protocolUrl = `resurf://new?${params.toString()}`;\n const { exec } = await import(\"child_process\");\n exec(`open \"${protocolUrl}\"`);\n console.log(\"Creating capture via Resurf protocol...\");\n}\n\nasync function cmdExport(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const spaceKey = await resolveSpaceKey(reader, extractFlag(args, \"--space\"));\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n\n const result = await reader.listCaptures({\n limit: 999999,\n spaceKey,\n tag,\n contentType,\n });\n\n const fullCaptures = await loadFullCaptures(reader, result.captures);\n let captures: Record<string, unknown>[] = fullCaptures;\n if (flags.compact) captures = captures.map((c) => stripCompactFields(c));\n\n if (flags.format === \"json\") {\n console.log(JSON.stringify(captures, null, 2));\n } else if (flags.format === \"md\") {\n const lines: string[] = [];\n for (const c of captures) {\n const cap = c as Record<string, unknown>;\n const title = (cap.title as string) || \"Untitled\";\n const id = cap.id as string;\n lines.push(`## ${title}`);\n lines.push(`- **ID:** \\`${id}\\``);\n if (cap.contentType) lines.push(`- **Type:** ${cap.contentType}`);\n if (cap.tldr) lines.push(`- **TLDR:** ${cap.tldr}`);\n if (cap.note) lines.push(`- **Note:** ${cap.note}`);\n lines.push(\"\");\n }\n lines.push(`_Exported ${captures.length} captures_`);\n console.log(lines.join(\"\\n\"));\n } else {\n console.log(JSON.stringify(captures, null, 2));\n console.error(`\\nExported ${captures.length} captures (use --json or --md for piping)`);\n }\n}\n\nfunction printHelp(): void {\n const help = `\nresurf - Access Resurf vault data from the command line\n\nUSAGE\n resurf <command> [options]\n\nCOMMANDS\n list List captures (default)\n get <id> [id2 ...] Get captures by ID (supports partial IDs, multiple)\n search <term> [...] Search captures (OR logic; terms can be @path-like)\n spaces List all spaces\n areas List all areas\n tags List all tags\n canvases List all canvases\n stats Show vault statistics\n vaults List available Resurf vaults on this computer\n tui Interactive TUI to browse and open captures\n open <id> Open a capture in Resurf app\n create Create a capture via Resurf protocol\n export Export captures as JSON\n\nGLOBAL OPTIONS\n --vault <path> Path to Resurf vault (auto-detected if omitted)\n --json Output as JSON\n --md, --markdown Output as Markdown\n --table Output as table\n --format <fmt> Output format: json, md, table, pretty (default: pretty)\n --compact Strip heavy fields (colorPalette, embedding, etc.) from JSON\n\nLIST OPTIONS\n --limit <n> Max results (default: 20)\n --offset <n> Skip first N results\n --space <name|key> Filter by space name or key\n --tag <tag> Filter by tag\n --type <type> Filter by content type (note, link, image, video, etc.)\n --search <term> Filter by text (repeat for OR: --search a --search b)\n --search-content Also search inside note/link content (slower)\n --pinned Show only pinned captures\n --sort <field> Sort by: created, updated (default: created)\n --asc Sort ascending (default: descending)\n --include-content Load full capture content (not just index)\n --paths Output one absolute attachment path per line\n\nSEARCH OPTIONS\n <term> [term2 ...] Multiple terms: OR logic (e.g. @path/to/file.ts term2)\n --search-content Search inside note body, link URL/description (slower)\n --limit <n> Max results (default: 20)\n\nCREATE OPTIONS\n --content <text> Text content for the capture\n --url <url> URL to capture\n --title <title> Title for the capture\n --tags <tags> Comma-separated tags\n --source <source> Source identifier\n --space <key> Space key to assign\n\nEXPORT OPTIONS\n --space <name|key> Filter by space name or key\n --tag <tag> Filter by tag\n --type <type> Filter by content type\n\nVAULTS OPTIONS\n [directory] Scan this directory recursively for vaults\n --verbose Log skipped inaccessible paths\n\nEXAMPLES\n resurf list --limit 10 --json\n resurf list --space \"UI\" --type image --json --compact\n resurf list --space \"Mobile App Inspirations\" --type image --json --compact --include-content\n resurf list --space \"UI\" --type image --paths\n resurf get abc123 def456 --json --compact\n resurf search \"meeting notes\" --json --compact\n resurf search custom-indexer vault --json --compact\n resurf search @apps/desktop/src/vault/custom-indexer.ts @apps/app/src/lib/utils/search-helpers.ts --search-content --json\n resurf spaces --json\n resurf tags\n resurf stats\n resurf export --space \"Research\" --json --compact > research.json\n\nVERSION\n ${VERSION}\n`;\n console.log(help.trim());\n}\n\nasync function main(): Promise<void> {\n const rawArgs = process.argv.slice(2);\n\n if (rawArgs.length === 0 || rawArgs[0] === \"--help\" || rawArgs[0] === \"-h\") {\n printHelp();\n return;\n }\n\n if (rawArgs[0] === \"--version\" || rawArgs[0] === \"-v\") {\n console.log(VERSION);\n return;\n }\n\n const { flags, rest } = parseGlobalFlags(rawArgs);\n const command = rest[0] ?? \"list\";\n const commandArgs = rest.slice(1);\n\n if (command === \"create\") {\n await cmdCreate(commandArgs);\n return;\n }\n\n if (command === \"vaults\" || command === \"find-vaults\") {\n const scanDir = commandArgs.find((a) => !a.startsWith(\"-\"));\n const vaults = await findVaults({ scanDir, verbose: flags.verbose });\n console.log(formatVaults(vaults, flags.format));\n return;\n }\n\n const vaultPath = await discoverVaultPath(flags.vault);\n const reader = new VaultReader(vaultPath);\n\n const commands: Record<string, (r: VaultReader, a: string[], f: GlobalFlags) => Promise<void>> = {\n list: cmdList,\n ls: cmdList,\n get: cmdGet,\n show: cmdGet,\n search: cmdSearch,\n find: cmdSearch,\n spaces: cmdSpaces,\n areas: cmdAreas,\n tags: cmdTags,\n canvases: cmdCanvases,\n stats: cmdStats,\n info: cmdStats,\n export: cmdExport,\n };\n\n if (command === \"open\") {\n await cmdOpen(reader, commandArgs);\n return;\n }\n\n if (command === \"tui\") {\n const { runTui } = await import(\"./tui-app.js\");\n runTui({ reader });\n return;\n }\n\n const handler = commands[command];\n if (!handler) {\n console.error(`Unknown command: ${command}`);\n console.error(`Run 'resurf --help' for usage.`);\n process.exit(1);\n }\n\n await handler(reader, commandArgs, flags);\n}\n\nmain().catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n"],"mappings":"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","names":["fs","path","stat","sortField","sortDir","total","offset","limit","stat","fmtOpts"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/reader.ts","../src/vault.ts"],"sourcesContent":["import fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { VaultReader } from \"./reader.js\";\nimport { discoverVaultPath, findVaults } from \"./vault.js\";\nimport {\n type FormatOptions,\n type OutputFormat,\n captureWithAbsoluteAttachmentPaths,\n formatAreas,\n formatCanvases,\n formatCapture,\n formatCaptureList,\n formatSpaces,\n formatStats,\n formatTags,\n formatVaults,\n stripCompactFields,\n} from \"./format.js\";\n\nconst VERSION = \"1.0.0\";\n\ntype GlobalFlags = {\n vault?: string;\n format: OutputFormat;\n verbose?: boolean;\n compact?: boolean;\n};\n\nfunction parseGlobalFlags(args: string[]): { flags: GlobalFlags; rest: string[] } {\n const flags: GlobalFlags = { format: \"pretty\" };\n const rest: string[] = [];\n let i = 0;\n\n while (i < args.length) {\n const arg = args[i];\n if (arg === \"--vault\" && i + 1 < args.length) {\n flags.vault = args[i + 1];\n i += 2;\n } else if (arg === \"--json\") {\n flags.format = \"json\";\n i++;\n } else if (arg === \"--md\" || arg === \"--markdown\") {\n flags.format = \"md\";\n i++;\n } else if (arg === \"--table\") {\n flags.format = \"table\";\n i++;\n } else if (arg === \"--format\" && i + 1 < args.length) {\n flags.format = args[i + 1] as OutputFormat;\n i += 2;\n } else if (arg === \"--verbose\") {\n flags.verbose = true;\n i++;\n } else if (arg === \"--compact\") {\n flags.compact = true;\n i++;\n } else {\n rest.push(arg);\n i++;\n }\n }\n\n return { flags, rest };\n}\n\nfunction extractFlag(args: string[], flag: string): string | undefined {\n const idx = args.indexOf(flag);\n if (idx === -1 || idx + 1 >= args.length) return undefined;\n return args[idx + 1];\n}\n\nfunction extractFlags(args: string[], flag: string): string[] {\n const out: string[] = [];\n for (let i = 0; i < args.length - 1; i++) {\n if (args[i] === flag) out.push(args[i + 1]);\n }\n return out;\n}\n\nfunction hasFlag(args: string[], flag: string): boolean {\n return args.includes(flag);\n}\n\nfunction extractNumber(args: string[], flag: string, defaultVal: number): number {\n const val = extractFlag(args, flag);\n if (!val) return defaultVal;\n const n = parseInt(val, 10);\n return isNaN(n) ? defaultVal : n;\n}\n\nimport type { Capture, CaptureIndex } from \"./reader.js\";\n\nasync function resolveSpaceKey(\n reader: VaultReader,\n input: string | undefined\n): Promise<string | undefined> {\n if (!input) return undefined;\n const spaces = await reader.getSpaces();\n if (spaces.some((s) => s.key === input)) return input;\n const lower = input.toLowerCase();\n const matches = spaces.filter((s) => s.name.toLowerCase() === lower);\n if (matches.length === 1) return matches[0].key;\n if (matches.length > 1) {\n const keys = matches.map((s) => s.key).join(\", \");\n console.error(`Ambiguous space name \"${input}\". Matching keys: ${keys}`);\n process.exit(1);\n }\n const partial = spaces.filter((s) => s.name.toLowerCase().includes(lower));\n if (partial.length === 1) return partial[0].key;\n if (partial.length > 1) {\n const suggestions = partial.map((s) => `${s.key} (${s.name})`).join(\", \");\n console.error(`Ambiguous space name \"${input}\". Did you mean: ${suggestions}`);\n process.exit(1);\n }\n return input;\n}\n\nasync function loadFullCaptures(\n reader: VaultReader,\n indexItems: CaptureIndex[]\n): Promise<Capture[]> {\n const results = await Promise.all(\n indexItems.map((item) => reader.getCaptureById(item.id))\n );\n return results\n .filter((c): c is Capture => c !== null)\n .map((c) => captureWithAbsoluteAttachmentPaths(c, reader.vaultPath));\n}\n\nasync function resolveAttachmentPaths(\n reader: VaultReader,\n indexItems: CaptureIndex[]\n): Promise<string[]> {\n const captures = await loadFullCaptures(reader, indexItems);\n return captures\n .filter((c) => c.content?.type === \"attachment\" && typeof c.content.path === \"string\")\n .map((c) => String(c.content.path));\n}\n\nasync function cmdList(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const limit = extractNumber(args, \"--limit\", 20);\n const offset = extractNumber(args, \"--offset\", 0);\n const spaceKey = await resolveSpaceKey(reader, extractFlag(args, \"--space\"));\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n const searchTermsList = [\n ...extractFlags(args, \"--search\"),\n ...extractFlags(args, \"-s\"),\n ];\n const searchTerms =\n searchTermsList.length > 0 ? searchTermsList : undefined;\n const search =\n searchTerms == null ? (extractFlag(args, \"--search\") ?? extractFlag(args, \"-s\")) : undefined;\n const searchInContent = hasFlag(args, \"--search-content\");\n const pinned = hasFlag(args, \"--pinned\") ? true : undefined;\n const sortBy = (extractFlag(args, \"--sort\") as \"created\" | \"updated\") ?? \"created\";\n const sortOrder = hasFlag(args, \"--asc\") ? (\"asc\" as const) : (\"desc\" as const);\n const includeContent = hasFlag(args, \"--include-content\");\n const pathsOnly = hasFlag(args, \"--paths\");\n\n const result = await reader.listCaptures({\n limit,\n offset,\n spaceKey,\n tag,\n contentType,\n search,\n searchTerms,\n searchInContent,\n pinned,\n sortBy,\n sortOrder,\n });\n\n if (pathsOnly) {\n const paths = await resolveAttachmentPaths(reader, result.captures);\n console.log(paths.join(\"\\n\"));\n return;\n }\n\n if (includeContent) {\n const fullCaptures = await loadFullCaptures(reader, result.captures);\n const fmtOpts: FormatOptions = { compact: flags.compact, vaultPath: reader.vaultPath };\n console.log(formatCaptureList(fullCaptures, result.total, flags.format, fmtOpts));\n return;\n }\n\n const fmtOpts: FormatOptions = { compact: flags.compact };\n console.log(formatCaptureList(result.captures, result.total, flags.format, fmtOpts));\n}\n\nasync function resolveCaptureById(reader: VaultReader, id: string): Promise<Capture | null> {\n let capture = await reader.getCaptureById(id);\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) capture = await reader.getCaptureById(match.id);\n }\n return capture;\n}\n\nasync function cmdGet(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const ids = args.filter((a) => !a.startsWith(\"-\"));\n if (ids.length === 0) {\n console.error(\"Usage: resurf get <capture-id> [id2 ...]\");\n process.exit(1);\n }\n\n const fmtOpts: FormatOptions = { compact: flags.compact, vaultPath: reader.vaultPath };\n\n if (ids.length === 1) {\n const capture = await resolveCaptureById(reader, ids[0]);\n if (!capture) {\n console.error(`Capture not found: ${ids[0]}`);\n process.exit(1);\n }\n console.log(formatCapture(capture, flags.format, fmtOpts));\n return;\n }\n\n const captures = await Promise.all(ids.map((id) => resolveCaptureById(reader, id)));\n const found = captures.filter(Boolean) as Capture[];\n\n if (found.length === 0) {\n console.error(\"No captures found for the given IDs.\");\n process.exit(1);\n }\n\n if (flags.format === \"json\") {\n let items: Record<string, unknown>[] = found.map((c) =>\n captureWithAbsoluteAttachmentPaths(c, reader.vaultPath)\n );\n if (flags.compact) items = items.map((c) => stripCompactFields(c));\n console.log(JSON.stringify(items, null, 2));\n } else {\n for (const c of found) {\n console.log(formatCapture(c, flags.format, fmtOpts));\n console.log(\"\");\n }\n }\n}\n\nasync function cmdSearch(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const positional: string[] = [];\n let i = 0;\n while (i < args.length) {\n if (args[i].startsWith(\"--\") && i + 1 < args.length) {\n i += 2;\n } else if (args[i].startsWith(\"--\")) {\n i++;\n } else {\n positional.push(args[i]);\n i++;\n }\n }\n\n const limit = extractNumber(args, \"--limit\", 20);\n const searchInContent = hasFlag(args, \"--search-content\");\n if (positional.length === 0) {\n console.error(\"Usage: resurf search <term> [term2 ...]\");\n process.exit(1);\n }\n\n const result = await reader.listCaptures({\n searchTerms: positional,\n searchInContent,\n limit,\n });\n const fmtOpts: FormatOptions = { compact: flags.compact };\n console.log(formatCaptureList(result.captures, result.total, flags.format, fmtOpts));\n}\n\nasync function cmdSpaces(\n reader: VaultReader,\n _args: string[],\n flags: GlobalFlags\n): Promise<void> {\n const spaces = await reader.getSpaces();\n console.log(formatSpaces(spaces, flags.format));\n}\n\nasync function cmdAreas(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const areas = await reader.getAreas();\n console.log(formatAreas(areas, flags.format));\n}\n\nasync function cmdTags(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const tags = await reader.getTags();\n console.log(formatTags(tags, flags.format));\n}\n\nasync function cmdCanvases(\n reader: VaultReader,\n _args: string[],\n flags: GlobalFlags\n): Promise<void> {\n const canvases = await reader.getCanvases();\n console.log(formatCanvases(canvases, flags.format));\n}\n\nasync function cmdStats(reader: VaultReader, _args: string[], flags: GlobalFlags): Promise<void> {\n const stats = await reader.getStats();\n console.log(formatStats(stats, reader.vaultPath, flags.format));\n}\n\nasync function cmdOpen(reader: VaultReader, args: string[]): Promise<void> {\n const id = args[0];\n if (!id) {\n console.error(\"Usage: resurf open <capture-id>\");\n process.exit(1);\n }\n\n let captureId = id;\n const capture = await reader.getCaptureById(id);\n if (!capture) {\n const index = await reader.getCaptureIndex();\n const match = index.find((c) => c.id.startsWith(id));\n if (match) captureId = match.id;\n }\n\n const { exec } = await import(\"child_process\");\n const url = `resurf://capture/${captureId}`;\n exec(`open \"${url}\"`);\n console.log(`Opening capture in Resurf: ${captureId}`);\n}\n\nasync function cmdCreate(args: string[]): Promise<void> {\n const content = extractFlag(args, \"--content\") ?? extractFlag(args, \"--text\");\n const url = extractFlag(args, \"--url\");\n const title = extractFlag(args, \"--title\");\n const tags = extractFlag(args, \"--tags\");\n const source = extractFlag(args, \"--source\");\n const space = extractFlag(args, \"--space\");\n\n if (!content && !url) {\n console.error(\n \"Usage: resurf create --content <text> [--url <url>] [--title <title>] [--tags <tags>]\"\n );\n process.exit(1);\n }\n\n const params = new URLSearchParams();\n if (content) params.set(\"content\", content);\n if (url) params.set(\"url\", url);\n if (title) params.set(\"title\", title);\n if (tags) params.set(\"tags\", tags);\n if (source) params.set(\"source\", source);\n if (space) params.set(\"space\", space);\n\n const protocolUrl = `resurf://new?${params.toString()}`;\n const { exec } = await import(\"child_process\");\n exec(`open \"${protocolUrl}\"`);\n console.log(\"Creating capture via Resurf protocol...\");\n}\n\nasync function cmdExport(reader: VaultReader, args: string[], flags: GlobalFlags): Promise<void> {\n const spaceKey = await resolveSpaceKey(reader, extractFlag(args, \"--space\"));\n const tag = extractFlag(args, \"--tag\");\n const contentType = extractFlag(args, \"--type\");\n\n const result = await reader.listCaptures({\n limit: 999999,\n spaceKey,\n tag,\n contentType,\n });\n\n const fullCaptures = await loadFullCaptures(reader, result.captures);\n let captures: Record<string, unknown>[] = fullCaptures;\n if (flags.compact) captures = captures.map((c) => stripCompactFields(c));\n\n if (flags.format === \"json\") {\n console.log(JSON.stringify(captures, null, 2));\n } else if (flags.format === \"md\") {\n const lines: string[] = [];\n for (const c of captures) {\n const cap = c as Record<string, unknown>;\n const title = (cap.title as string) || \"Untitled\";\n const id = cap.id as string;\n lines.push(`## ${title}`);\n lines.push(`- **ID:** \\`${id}\\``);\n if (cap.contentType) lines.push(`- **Type:** ${cap.contentType}`);\n if (cap.tldr) lines.push(`- **TLDR:** ${cap.tldr}`);\n if (cap.note) lines.push(`- **Note:** ${cap.note}`);\n lines.push(\"\");\n }\n lines.push(`_Exported ${captures.length} captures_`);\n console.log(lines.join(\"\\n\"));\n } else {\n console.log(JSON.stringify(captures, null, 2));\n console.error(`\\nExported ${captures.length} captures (use --json or --md for piping)`);\n }\n}\n\nconst SKILL_NAME = \"resurf\";\n\nfunction getBundledSkillPath(): string {\n const dir = path.dirname(fileURLToPath(import.meta.url));\n return path.join(dir, \"..\", \"skill\", \"SKILL.md\");\n}\n\nasync function cmdInstallSkill(args: string[]): Promise<void> {\n const cursor = hasFlag(args, \"--cursor\");\n const codex = hasFlag(args, \"--codex\");\n const claude = hasFlag(args, \"--claude\");\n const all = !cursor && !codex && !claude;\n const toCursor = all || cursor;\n const toCodex = all || codex;\n const toClaude = all || claude;\n\n const skillPath = getBundledSkillPath();\n if (!fs.existsSync(skillPath)) {\n console.error(`Skill file not found: ${skillPath}`);\n process.exit(1);\n }\n const content = fs.readFileSync(skillPath, \"utf-8\");\n\n const homedir = os.homedir();\n if (toCursor) {\n const dir = path.join(homedir, \".cursor\", \"skills\", SKILL_NAME);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, \"SKILL.md\"), content, \"utf-8\");\n console.log(`Installed Resurf skill for Cursor: ${dir}`);\n }\n if (toCodex) {\n const codexHome = process.env.CODEX_HOME ?? path.join(homedir, \".codex\");\n const dir = path.join(codexHome, \"skills\", SKILL_NAME);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, \"SKILL.md\"), content, \"utf-8\");\n console.log(`Installed Resurf skill for Codex: ${dir}`);\n }\n if (toClaude) {\n const claudeHome = process.env.CLAUDE_HOME ?? path.join(homedir, \".claude\");\n const dir = path.join(claudeHome, \"skills\", SKILL_NAME);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, \"SKILL.md\"), content, \"utf-8\");\n console.log(`Installed Resurf skill for Claude Code: ${dir}`);\n }\n if (toCursor || toCodex || toClaude) {\n console.log(\"Restart the editor to pick up the skill.\");\n }\n}\n\nfunction printHelp(): void {\n const help = `\nresurf - Access Resurf vault data from the command line\n\nUSAGE\n resurf <command> [options]\n\nCOMMANDS\n list List captures (default)\n get <id> [id2 ...] Get captures by ID (supports partial IDs, multiple)\n search <term> [...] Search captures (OR logic; terms can be @path-like)\n spaces List all spaces\n areas List all areas\n tags List all tags\n canvases List all canvases\n stats Show vault statistics\n vaults List available Resurf vaults on this computer\n tui Interactive TUI to browse and open captures\n open <id> Open a capture in Resurf app\n create Create a capture via Resurf protocol\n export Export captures as JSON\n install-skill Install Resurf skill for Cursor and/or Codex\n\nGLOBAL OPTIONS\n --vault <path> Path to Resurf vault (auto-detected if omitted)\n --json Output as JSON\n --md, --markdown Output as Markdown\n --table Output as table\n --format <fmt> Output format: json, md, table, pretty (default: pretty)\n --compact Strip heavy fields (colorPalette, embedding, etc.) from JSON\n\nLIST OPTIONS\n --limit <n> Max results (default: 20)\n --offset <n> Skip first N results\n --space <name|key> Filter by space name or key\n --tag <tag> Filter by tag\n --type <type> Filter by content type (note, link, image, video, etc.)\n --search <term> Filter by text (repeat for OR: --search a --search b)\n --search-content Also search inside note/link content (slower)\n --pinned Show only pinned captures\n --sort <field> Sort by: created, updated (default: created)\n --asc Sort ascending (default: descending)\n --include-content Load full capture content (not just index)\n --paths Output one absolute attachment path per line\n\nSEARCH OPTIONS\n <term> [term2 ...] Multiple terms: OR logic (e.g. @path/to/file.ts term2)\n --search-content Search inside note body, link URL/description (slower)\n --limit <n> Max results (default: 20)\n\nCREATE OPTIONS\n --content <text> Text content for the capture\n --url <url> URL to capture\n --title <title> Title for the capture\n --tags <tags> Comma-separated tags\n --source <source> Source identifier\n --space <key> Space key to assign\n\nEXPORT OPTIONS\n --space <name|key> Filter by space name or key\n --tag <tag> Filter by tag\n --type <type> Filter by content type\n\nVAULTS OPTIONS\n [directory] Scan this directory recursively for vaults\n --verbose Log skipped inaccessible paths\n\nINSTALL-SKILL OPTIONS\n --cursor Install only for Cursor (~/.cursor/skills/resurf)\n --codex Install only for Codex ($CODEX_HOME/skills/resurf, default ~/.codex)\n --claude Install only for Claude Code ($CLAUDE_HOME/skills/resurf, default ~/.claude)\n (no flags) Install for Cursor, Codex, and Claude Code\n\nEXAMPLES\n resurf list --limit 10 --json\n resurf list --space \"UI\" --type image --json --compact\n resurf list --space \"Mobile App Inspirations\" --type image --json --compact --include-content\n resurf list --space \"UI\" --type image --paths\n resurf get abc123 def456 --json --compact\n resurf search \"meeting notes\" --json --compact\n resurf search custom-indexer vault --json --compact\n resurf search @apps/desktop/src/vault/custom-indexer.ts @apps/app/src/lib/utils/search-helpers.ts --search-content --json\n resurf spaces --json\n resurf tags\n resurf stats\n resurf export --space \"Research\" --json --compact > research.json\n\nVERSION\n ${VERSION}\n`;\n console.log(help.trim());\n}\n\nasync function main(): Promise<void> {\n const rawArgs = process.argv.slice(2);\n\n if (rawArgs.length === 0 || rawArgs[0] === \"--help\" || rawArgs[0] === \"-h\") {\n printHelp();\n return;\n }\n\n if (rawArgs[0] === \"--version\" || rawArgs[0] === \"-v\") {\n console.log(VERSION);\n return;\n }\n\n const { flags, rest } = parseGlobalFlags(rawArgs);\n const command = rest[0] ?? \"list\";\n const commandArgs = rest.slice(1);\n\n if (command === \"create\") {\n await cmdCreate(commandArgs);\n return;\n }\n\n if (command === \"install-skill\") {\n await cmdInstallSkill(commandArgs);\n return;\n }\n if (command === \"skill\" && commandArgs[0] === \"install\") {\n await cmdInstallSkill(commandArgs.slice(1));\n return;\n }\n\n if (command === \"vaults\" || command === \"find-vaults\") {\n const scanDir = commandArgs.find((a) => !a.startsWith(\"-\"));\n const vaults = await findVaults({ scanDir, verbose: flags.verbose });\n console.log(formatVaults(vaults, flags.format));\n return;\n }\n\n const vaultPath = await discoverVaultPath(flags.vault);\n const reader = new VaultReader(vaultPath);\n\n const commands: Record<string, (r: VaultReader, a: string[], f: GlobalFlags) => Promise<void>> = {\n list: cmdList,\n ls: cmdList,\n get: cmdGet,\n show: cmdGet,\n search: cmdSearch,\n find: cmdSearch,\n spaces: cmdSpaces,\n areas: cmdAreas,\n tags: cmdTags,\n canvases: cmdCanvases,\n stats: cmdStats,\n info: cmdStats,\n export: cmdExport,\n };\n\n if (command === \"open\") {\n await cmdOpen(reader, commandArgs);\n return;\n }\n\n if (command === \"tui\") {\n const { runTui } = await import(\"./tui-app.js\");\n runTui({ reader });\n return;\n }\n\n const handler = commands[command];\n if (!handler) {\n console.error(`Unknown command: ${command}`);\n console.error(`Run 'resurf --help' for usage.`);\n process.exit(1);\n }\n\n await handler(reader, commandArgs, flags);\n}\n\nmain().catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n","import * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { type VaultStructure, getVaultStructure } from \"./vault.js\";\n\nexport type CaptureIndex = {\n id: string;\n type: string;\n filePath?: string;\n createdAt: number;\n updatedAt: number;\n contentType?: string | string[];\n title?: string;\n tags?: string[];\n spaceKeys?: string[];\n mimeTypes?: string[];\n note?: string;\n tldr?: string;\n source?: string;\n isPinned?: boolean;\n isHidden?: boolean;\n triageStatus?: string;\n processingStatus?: string;\n};\n\nexport type Space = {\n id: string;\n name: string;\n key: string;\n areaKey?: string;\n icon?: string;\n color?: string;\n createdAt: number;\n updatedAt: number;\n noteCount?: number;\n isPinned?: boolean;\n};\n\nexport type Area = {\n id: string;\n name: string;\n key: string;\n createdAt: number;\n updatedAt: number;\n};\n\nexport type Canvas = {\n id: string;\n name: string;\n createdAt: number;\n updatedAt: number;\n nodes: unknown[];\n connections: unknown[];\n annotations: unknown[];\n};\n\nexport type Capture = CaptureIndex & {\n content: Record<string, unknown>;\n author?: Record<string, unknown>;\n colorPalette?: unknown[];\n embedding?: number[];\n};\n\ntype IndexMap<T> = Record<string, T>;\n\nasync function readJson<T>(filePath: string): Promise<T | null> {\n try {\n const data = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n\nfunction contentToSearchableString(capture: Capture): string {\n const parts: string[] = [];\n const content = capture.content;\n if (!content) return \"\";\n if (typeof content.textContent === \"string\") parts.push(content.textContent);\n if (content.noteContent != null)\n parts.push(\n typeof content.noteContent === \"string\"\n ? content.noteContent\n : JSON.stringify(content.noteContent)\n );\n if (typeof content.url === \"string\") parts.push(content.url);\n if (typeof content.ogTitle === \"string\") parts.push(content.ogTitle);\n if (typeof content.ogDescription === \"string\") parts.push(content.ogDescription);\n if (typeof content.articleContent === \"string\") parts.push(content.articleContent);\n if (typeof content.path === \"string\") parts.push(content.path);\n return parts.join(\" \");\n}\n\nfunction buildIndexHaystack(\n c: CaptureIndex,\n spacesByKey: Map<string, Space>,\n areasByKey: Map<string, Area>\n): string {\n const parts = [\n c.title,\n c.note,\n c.tldr,\n c.source,\n c.filePath,\n ...(c.tags ?? []),\n ].filter((v): v is string => typeof v === \"string\");\n for (const spaceKey of c.spaceKeys ?? []) {\n const space = spacesByKey.get(spaceKey);\n if (space?.name) parts.push(space.name);\n if (space?.areaKey) {\n const area = areasByKey.get(space.areaKey);\n if (area?.name) parts.push(area.name);\n }\n }\n return parts.join(\" \").toLowerCase();\n}\n\nexport class VaultReader {\n private structure: VaultStructure;\n\n constructor(vaultPath: string) {\n this.structure = getVaultStructure(vaultPath);\n }\n\n get vaultPath(): string {\n return this.structure.root;\n }\n\n async getCaptureIndex(): Promise<CaptureIndex[]> {\n const indexPath = path.join(this.structure.index, \"index.captures.json\");\n const data = await readJson<IndexMap<CaptureIndex>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getSpaces(): Promise<Space[]> {\n const indexPath = path.join(this.structure.index, \"index.spaces.json\");\n const data = await readJson<IndexMap<Space>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getAreas(): Promise<Area[]> {\n const indexPath = path.join(this.structure.index, \"index.areas.json\");\n const data = await readJson<IndexMap<Area>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getCanvases(): Promise<Canvas[]> {\n const indexPath = path.join(this.structure.index, \"index.canvases.json\");\n const data = await readJson<IndexMap<Canvas>>(indexPath);\n if (!data) return [];\n return Object.values(data);\n }\n\n async getTags(): Promise<string[]> {\n const indexPath = path.join(this.structure.index, \"index.tags.json\");\n const data = await readJson<string[]>(indexPath);\n return data ?? [];\n }\n\n async getCaptureById(id: string): Promise<Capture | null> {\n const capturePath = path.join(this.structure.captures, `${id}.json`);\n return readJson<Capture>(capturePath);\n }\n\n async listCaptures(\n options: {\n limit?: number;\n offset?: number;\n spaceKey?: string;\n tag?: string;\n contentType?: string;\n search?: string;\n searchTerms?: string[];\n searchInContent?: boolean;\n pinned?: boolean;\n sortBy?: \"created\" | \"updated\";\n sortOrder?: \"asc\" | \"desc\";\n } = {}\n ): Promise<{ captures: CaptureIndex[]; total: number }> {\n let captures = await this.getCaptureIndex();\n\n captures = captures.filter((c) => !c.isHidden);\n\n if (options.spaceKey) {\n captures = captures.filter((c) => c.spaceKeys?.includes(options.spaceKey!));\n }\n\n if (options.tag) {\n captures = captures.filter((c) => c.tags?.includes(options.tag!));\n }\n\n if (options.contentType) {\n captures = captures.filter((c) => {\n const ct = c.contentType;\n if (Array.isArray(ct)) return ct.includes(options.contentType!);\n return ct === options.contentType;\n });\n }\n\n if (options.pinned !== undefined) {\n captures = captures.filter((c) => c.isPinned === options.pinned);\n }\n\n const terms = options.searchTerms?.filter((t) => t.trim().length > 0).map((t) => t.trim()) ?? [];\n const singleSearch = options.search?.trim();\n const hasSearch = terms.length > 0 || (singleSearch != null && singleSearch.length > 0);\n\n const normalizeTerm = (t: string): string =>\n (t.startsWith(\"@\") ? t.slice(1) : t).toLowerCase();\n const termsNormalized = terms.map(normalizeTerm);\n const singleNorm = singleSearch ? singleSearch.toLowerCase() : null;\n\n let spacesByKey = new Map<string, Space>();\n let areasByKey = new Map<string, Area>();\n if (hasSearch) {\n const [spaces, areas] = await Promise.all([this.getSpaces(), this.getAreas()]);\n spacesByKey = new Map(spaces.map((s) => [s.key, s]));\n areasByKey = new Map(areas.map((a) => [a.key, a]));\n }\n const indexHaystack = (c: CaptureIndex): string =>\n buildIndexHaystack(c, spacesByKey, areasByKey);\n\n if (hasSearch && options.searchInContent) {\n const sortField = options.sortBy === \"updated\" ? \"updatedAt\" : \"createdAt\";\n const sortDir = options.sortOrder === \"asc\" ? 1 : -1;\n captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);\n const toMatch = termsNormalized.length > 0 ? termsNormalized : singleNorm ? [singleNorm] : [];\n const matches: CaptureIndex[] = [];\n const maxScan = 2000;\n for (let i = 0; i < Math.min(captures.length, maxScan); i++) {\n const c = captures[i];\n const full = await this.getCaptureById(c.id);\n if (!full) continue;\n const contentHaystack = contentToSearchableString(full);\n const haystack = (indexHaystack(c) + \" \" + contentHaystack).toLowerCase();\n const hit = toMatch.some((q) => haystack.includes(q));\n if (hit) matches.push(c);\n }\n const total = matches.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 20;\n return {\n captures: matches.slice(offset, offset + limit),\n total,\n };\n }\n\n if (terms.length > 0) {\n captures = captures.filter((c) => {\n const haystack = indexHaystack(c);\n return terms.some((term) => haystack.includes(normalizeTerm(term)));\n });\n } else if (singleSearch) {\n const q = singleSearch.toLowerCase();\n captures = captures.filter((c) => indexHaystack(c).includes(q));\n }\n\n const sortField = options.sortBy === \"updated\" ? \"updatedAt\" : \"createdAt\";\n const sortDir = options.sortOrder === \"asc\" ? 1 : -1;\n captures.sort((a, b) => (b[sortField] - a[sortField]) * sortDir);\n\n const total = captures.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 20;\n\n return {\n captures: captures.slice(offset, offset + limit),\n total,\n };\n }\n\n async getStats(): Promise<{\n totalCaptures: number;\n totalSpaces: number;\n totalAreas: number;\n totalCanvases: number;\n totalTags: number;\n byContentType: Record<string, number>;\n byTriageStatus: Record<string, number>;\n }> {\n const [captures, spaces, areas, canvases, tags] = await Promise.all([\n this.getCaptureIndex(),\n this.getSpaces(),\n this.getAreas(),\n this.getCanvases(),\n this.getTags(),\n ]);\n\n const byContentType: Record<string, number> = {};\n const byTriageStatus: Record<string, number> = {};\n\n for (const c of captures) {\n const types = Array.isArray(c.contentType)\n ? c.contentType\n : c.contentType\n ? [c.contentType]\n : [\"unknown\"];\n for (const t of types) {\n byContentType[t] = (byContentType[t] ?? 0) + 1;\n }\n const status = c.triageStatus ?? \"none\";\n byTriageStatus[status] = (byTriageStatus[status] ?? 0) + 1;\n }\n\n return {\n totalCaptures: captures.length,\n totalSpaces: spaces.length,\n totalAreas: areas.length,\n totalCanvases: canvases.length,\n totalTags: tags.length,\n byContentType,\n byTriageStatus,\n };\n }\n\n async getVaultSize(): Promise<{ files: number; bytes: number }> {\n let files = 0;\n let bytes = 0;\n\n const walk = async (dir: string): Promise<void> => {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n } else {\n files++;\n try {\n const stat = await fs.stat(full);\n bytes += stat.size;\n } catch {}\n }\n }\n } catch {}\n };\n\n await walk(this.structure.root);\n return { files, bytes };\n }\n}\n","import * as os from \"os\";\nimport * as path from \"path\";\nimport * as fs from \"fs/promises\";\n\nconst VAULT_NAMES = [\"Resurf Vault\", \"Resurf Vault Dev\"];\nconst APP_NAMES = [\n \"so.resurf.app\",\n \"so.resurf.app.dev\",\n \"Resurf\",\n \"Resurf Dev\",\n \"desktop\",\n];\nconst CONFIG_FILES = [\"app.json\", \"app.dev.json\"];\n\nexport type VaultStructure = {\n root: string;\n captures: string;\n attachments: string;\n index: string;\n config: string;\n trash: string;\n};\n\nexport function getVaultStructure(vaultPath: string): VaultStructure {\n const root = path.resolve(vaultPath);\n return {\n root,\n captures: path.join(root, \"captures\"),\n attachments: path.join(root, \"attachments\"),\n index: path.join(root, \".index\"),\n config: path.join(root, \".config\"),\n trash: path.join(root, \".trash\"),\n };\n}\n\nexport async function hasVaultConfig(dir: string): Promise<boolean> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n await fs.access(configPath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function getVaultNameFromConfig(dir: string): Promise<string> {\n try {\n const configPath = path.join(dir, \".config\", \"vault.json\");\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { name?: string };\n return config.name ?? path.basename(dir);\n } catch {\n return path.basename(dir);\n }\n}\n\nasync function getCurrentVaultFromAppConfig(): Promise<{\n path: string;\n valid: boolean;\n} | null> {\n const home = os.homedir();\n const base = path.join(home, \"Library\", \"Application Support\");\n for (const appName of APP_NAMES) {\n for (const configFile of CONFIG_FILES) {\n const configPath = path.join(base, appName, configFile);\n try {\n const data = await fs.readFile(configPath, \"utf-8\");\n const config = JSON.parse(data) as { vaultPath?: string };\n const vaultPath = config.vaultPath?.trim();\n if (vaultPath) {\n const valid = await hasVaultConfig(vaultPath);\n return { path: vaultPath, valid };\n }\n } catch {\n // ignore\n }\n }\n }\n return null;\n}\n\nfunction getDefaultSearchDirs(): string[] {\n const home = os.homedir();\n const dirs: string[] = [];\n const documents = path.join(home, \"Documents\");\n dirs.push(documents);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(documents, name));\n }\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n dirs.push(iCloud);\n for (const name of VAULT_NAMES) {\n dirs.push(path.join(iCloud, name));\n }\n return dirs;\n}\n\nasync function scanDirForVaults(\n dir: string,\n vaults: VaultInfo[],\n seen: Set<string>,\n currentResolved: string | null,\n verbose: boolean\n): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const ent of entries) {\n if (!ent.isDirectory() || ent.name.startsWith(\".\")) continue;\n const sub = path.join(dir, ent.name);\n try {\n if (await hasVaultConfig(sub)) {\n const resolved = path.resolve(sub);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(sub);\n vaults.push({\n path: resolved,\n name,\n current: currentResolved === resolved,\n });\n seen.add(resolved);\n }\n } else {\n const nested = await fs.readdir(sub, { withFileTypes: true }).catch(() => []);\n for (const n of nested) {\n if (!n.isDirectory() || n.name.startsWith(\".\")) continue;\n const subSub = path.join(sub, n.name);\n if (await hasVaultConfig(subSub)) {\n const resolved = path.resolve(subSub);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(subSub);\n vaults.push({\n path: resolved,\n name,\n current: currentResolved === resolved,\n });\n seen.add(resolved);\n }\n }\n }\n }\n } catch {\n if (verbose) console.error(\"Skipping (inaccessible):\", sub);\n }\n }\n } catch (err) {\n if (verbose) console.error(\"Skipping (inaccessible):\", dir, err);\n }\n}\n\nexport type VaultInfo = {\n path: string;\n name: string;\n current: boolean;\n};\n\nexport async function findVaults(options?: {\n scanDir?: string;\n verbose?: boolean;\n}): Promise<VaultInfo[]> {\n const vaults: VaultInfo[] = [];\n const seen = new Set<string>();\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n const name = await getVaultNameFromConfig(current.path);\n vaults.push({ path: path.resolve(current.path), name, current: true });\n seen.add(path.resolve(current.path));\n }\n\n const currentResolvedForScan = current?.path ? path.resolve(current.path) : null;\n const verbose = options?.verbose ?? false;\n const searchDirs = options?.scanDir ? [options.scanDir] : getDefaultSearchDirs();\n\n for (const dir of searchDirs) {\n try {\n await fs.access(dir);\n } catch {\n if (verbose) console.error(\"Skipping (inaccessible):\", dir);\n continue;\n }\n if (await hasVaultConfig(dir)) {\n const resolved = path.resolve(dir);\n if (!seen.has(resolved)) {\n const name = await getVaultNameFromConfig(dir);\n vaults.push({\n path: resolved,\n name,\n current: currentResolvedForScan === resolved,\n });\n seen.add(resolved);\n }\n } else if (options?.scanDir) {\n await findVaultsRecursive(dir, vaults, seen, currentResolvedForScan);\n } else {\n const base = path.basename(dir);\n if (base === \"Documents\" || base === \"com~apple~CloudDocs\") {\n await scanDirForVaults(dir, vaults, seen, currentResolvedForScan, verbose);\n }\n }\n }\n\n const currentResolved = current?.path ? path.resolve(current.path) : null;\n const normalized = vaults.map((v) => ({\n ...v,\n path: path.resolve(v.path),\n current: currentResolved === path.resolve(v.path),\n }));\n return Array.from(new Map(normalized.map((v) => [v.path, v])).values());\n}\n\nasync function findVaultsRecursive(\n dir: string,\n results: VaultInfo[],\n seen: Set<string>,\n currentResolved: string | null\n): Promise<void> {\n try {\n const stat = await fs.stat(dir);\n if (!stat.isDirectory()) return;\n if (seen.has(dir)) return;\n seen.add(dir);\n\n if (await hasVaultConfig(dir)) {\n const name = await getVaultNameFromConfig(dir);\n results.push({\n path: path.resolve(dir),\n name,\n current: currentResolved === path.resolve(dir),\n });\n return;\n }\n\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);\n for (const ent of entries) {\n if (ent.isDirectory() && !ent.name.startsWith(\".\")) {\n const child = path.join(dir, ent.name);\n await findVaultsRecursive(child, results, seen, currentResolved);\n }\n }\n } catch {\n // skip inaccessible dirs\n }\n}\n\nexport async function discoverVaultPath(override?: string): Promise<string> {\n if (override?.trim()) {\n const resolved = path.resolve(override);\n const valid = await hasVaultConfig(resolved);\n if (!valid) {\n throw new Error(`Not a valid Resurf vault: ${resolved}`);\n }\n return resolved;\n }\n\n const current = await getCurrentVaultFromAppConfig();\n if (current?.path && current.valid) {\n return path.resolve(current.path);\n }\n\n const home = os.homedir();\n const documents = path.join(home, \"Documents\");\n const iCloud = path.join(home, \"Library\", \"Mobile Documents\", \"com~apple~CloudDocs\");\n\n for (const name of VAULT_NAMES) {\n for (const parent of [documents, iCloud]) {\n const candidate = path.join(parent, name);\n try {\n await fs.access(candidate);\n if (await hasVaultConfig(candidate)) {\n return path.resolve(candidate);\n }\n } catch {\n // skip\n }\n }\n }\n\n const fallback = path.join(documents, VAULT_NAMES[0]);\n throw new Error(\n `No Resurf vault found. Set one with --vault <path>, or create a vault at ${fallback}`\n );\n}\n"],"mappings":"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","names":["fs","os","path","fs","path","stat","sortField","sortDir","total","offset","limit","stat","fmtOpts","path","fs","homedir","os"]}
package/dist/tui-app.js CHANGED
@@ -57,12 +57,12 @@ function ResurfTui({ reader }) {
57
57
  const [spaceNamesByKey, setSpaceNamesByKey] = useState(/* @__PURE__ */ new Map());
58
58
  const [imagePreview, setImagePreview] = useState(null);
59
59
  const imageLoadRef = useRef(0);
60
- const loadCaptures = useCallback(async () => {
60
+ const loadCaptures = useCallback(async (query) => {
61
61
  setLoading(true);
62
62
  const [result, spaces] = await Promise.all([
63
63
  reader.listCaptures({
64
- search: search || void 0,
65
- searchTerms: search.trim() ? [search.trim()] : void 0,
64
+ search: query || void 0,
65
+ searchTerms: query.trim() ? [query.trim()] : void 0,
66
66
  limit: 100,
67
67
  sortBy: "updated",
68
68
  sortOrder: "desc"
@@ -74,11 +74,10 @@ function ResurfTui({ reader }) {
74
74
  setSelectedIndex(0);
75
75
  setSpaceNamesByKey(new Map(spaces.map((s) => [s.key, s.name])));
76
76
  setLoading(false);
77
- }, [reader, search]);
77
+ }, [reader]);
78
78
  useEffect(() => {
79
- const t = setTimeout(loadCaptures, search ? 250 : 0);
80
- return () => clearTimeout(t);
81
- }, [loadCaptures, search]);
79
+ loadCaptures("");
80
+ }, [loadCaptures]);
82
81
  const selectedCapture = captures[selectedIndex];
83
82
  const isImageType = selectedCapture && (selectedCapture.contentType === "image" || Array.isArray(selectedCapture.contentType) && selectedCapture.contentType.includes("image"));
84
83
  useEffect(() => {
@@ -112,6 +111,10 @@ function ResurfTui({ reader }) {
112
111
  }
113
112
  if (focus === "search") {
114
113
  if (key.tab) setFocus("list");
114
+ if (key.return) {
115
+ loadCaptures(search);
116
+ setFocus("list");
117
+ }
115
118
  return;
116
119
  }
117
120
  if (key.tab) {
@@ -135,12 +138,16 @@ function ResurfTui({ reader }) {
135
138
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, "Resurf"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2014 "), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, path.basename(reader.vaultPath))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 0 }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "gray" }, " Search "), /* @__PURE__ */ React.createElement(
136
139
  TextInput,
137
140
  {
138
- placeholder: "Search by title, note, space, tag...",
141
+ placeholder: "Search by title, note, space, tag... (Enter to search)",
139
142
  value: search,
140
143
  onChange: setSearch,
144
+ onSubmit: (value) => {
145
+ loadCaptures(value);
146
+ setFocus("list");
147
+ },
141
148
  isDisabled: focus !== "search"
142
149
  }
143
- )), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Tab: focus \xB7 \u2191\u2193: move \xB7 Enter: open \xB7 q: quit")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1, minHeight: LIST_HEIGHT + 2, gap: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", flexGrow: 1, minWidth: 0 }, loading ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Loading\u2026") : captures.length === 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "No captures found.") : (() => {
150
+ )), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Tab: focus \xB7 Enter (search): run \xB7 \u2191\u2193: move \xB7 Enter (list): open \xB7 q: quit")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1, minHeight: LIST_HEIGHT + 2, gap: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", flexGrow: 1, minWidth: 0 }, loading ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Loading\u2026") : captures.length === 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "No captures found.") : (() => {
144
151
  const start = Math.max(
145
152
  0,
146
153
  Math.min(selectedIndex, captures.length - LIST_HEIGHT)
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tui-app.tsx"],"sourcesContent":["import { exec } from \"child_process\";\nimport path from \"path\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Box, render, Text, useApp, useInput } from \"ink\";\nimport { TextInput } from \"@inkjs/ui\";\nimport type { CaptureIndex } from \"./reader.js\";\nimport type { VaultReader } from \"./reader.js\";\nimport { captureWithAbsoluteAttachmentPaths } from \"./format.js\";\n\nconst LIST_HEIGHT = 12;\nconst IMAGE_WIDTH = 36;\n\nfunction relativeTime(ts: number): string {\n const diff = Date.now() - ts;\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo ago`;\n return `${Math.floor(months / 12)}y ago`;\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return str.slice(0, max - 1) + \"…\";\n}\n\nfunction contentTypeLabel(c: CaptureIndex): string {\n const ct = c.contentType;\n if (Array.isArray(ct)) {\n if (ct.includes(\"image\")) return \"🖼\";\n if (ct.includes(\"link\")) return \"🔗\";\n if (ct.includes(\"note\")) return \"📝\";\n return \"📄\";\n }\n if (ct === \"image\") return \"🖼\";\n if (ct === \"link\") return \"🔗\";\n if (ct === \"note\") return \"📝\";\n return \"📄\";\n}\n\nfunction openCapture(id: string): void {\n const url = `resurf://capture/${id}`;\n exec(`open \"${url}\"`);\n}\n\ntype ResurfTuiProps = {\n reader: VaultReader;\n};\n\nfunction ResurfTui({ reader }: ResurfTuiProps) {\n const { exit } = useApp();\n const [search, setSearch] = useState(\"\");\n const [captures, setCaptures] = useState<CaptureIndex[]>([]);\n const [total, setTotal] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [focus, setFocus] = useState<\"search\" | \"list\">(\"search\");\n const [loading, setLoading] = useState(true);\n const [spaceNamesByKey, setSpaceNamesByKey] = useState<Map<string, string>>(new Map());\n const [imagePreview, setImagePreview] = useState<string | null>(null);\n const imageLoadRef = useRef<number>(0);\n\n const loadCaptures = useCallback(async () => {\n setLoading(true);\n const [result, spaces] = await Promise.all([\n reader.listCaptures({\n search: search || undefined,\n searchTerms: search.trim() ? [search.trim()] : undefined,\n limit: 100,\n sortBy: \"updated\",\n sortOrder: \"desc\",\n }),\n reader.getSpaces(),\n ]);\n setCaptures(result.captures);\n setTotal(result.total);\n setSelectedIndex(0);\n setSpaceNamesByKey(new Map(spaces.map((s) => [s.key, s.name])));\n setLoading(false);\n }, [reader, search]);\n\n useEffect(() => {\n const t = setTimeout(loadCaptures, search ? 250 : 0);\n return () => clearTimeout(t);\n }, [loadCaptures, search]);\n\n const selectedCapture = captures[selectedIndex];\n const isImageType =\n selectedCapture &&\n (selectedCapture.contentType === \"image\" ||\n (Array.isArray(selectedCapture.contentType) &&\n selectedCapture.contentType.includes(\"image\")));\n\n useEffect(() => {\n if (!isImageType || !selectedCapture) {\n setImagePreview(null);\n return;\n }\n const loadId = ++imageLoadRef.current;\n reader.getCaptureById(selectedCapture.id).then((full) => {\n if (loadId !== imageLoadRef.current) return;\n if (!full?.content || full.content.type !== \"attachment\" || typeof full.content.path !== \"string\") {\n setImagePreview(null);\n return;\n }\n const withPath = captureWithAbsoluteAttachmentPaths(full, reader.vaultPath);\n const absPath = (withPath.content as { path: string }).path;\n import(\"terminal-image\")\n .then((mod) => mod.default.file(absPath, { width: IMAGE_WIDTH }))\n .then((str) => {\n if (loadId === imageLoadRef.current) setImagePreview(str);\n })\n .catch(() => {\n if (loadId === imageLoadRef.current) setImagePreview(null);\n });\n });\n return () => {\n setImagePreview(null);\n };\n }, [reader, selectedCapture?.id, isImageType]);\n\n useInput((input, key) => {\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n exit();\n return;\n }\n if (focus === \"search\") {\n if (key.tab) setFocus(\"list\");\n return;\n }\n if (key.tab) {\n setFocus(\"search\");\n return;\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n return;\n }\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(captures.length - 1, i + 1));\n return;\n }\n if (key.return && captures[selectedIndex]) {\n openCapture(captures[selectedIndex].id);\n exit();\n }\n });\n\n const maxTitleWidth = 48;\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Resurf\n </Text>\n <Text dimColor> — </Text>\n <Text dimColor>{path.basename(reader.vaultPath)}</Text>\n </Box>\n <Box flexDirection=\"column\" gap={0}>\n <Box>\n <Text color=\"gray\"> Search </Text>\n <TextInput\n placeholder=\"Search by title, note, space, tag...\"\n value={search}\n onChange={setSearch}\n isDisabled={focus !== \"search\"}\n />\n </Box>\n <Text dimColor>\n Tab: focus · ↑↓: move · Enter: open · q: quit\n </Text>\n </Box>\n <Box flexDirection=\"row\" marginTop={1} minHeight={LIST_HEIGHT + 2} gap={2}>\n <Box flexDirection=\"column\" flexGrow={1} minWidth={0}>\n {loading ? (\n <Text dimColor>Loading…</Text>\n ) : captures.length === 0 ? (\n <Text dimColor>No captures found.</Text>\n ) : (\n (() => {\n const start = Math.max(\n 0,\n Math.min(selectedIndex, captures.length - LIST_HEIGHT)\n );\n const visible = captures.slice(start, start + LIST_HEIGHT);\n return visible.map((c, i) => {\n const actualIndex = start + i;\n const selected = focus === \"list\" && actualIndex === selectedIndex;\n const title = (c.title || \"Untitled\").trim() || \"—\";\n const time = relativeTime(c.updatedAt ?? c.createdAt);\n const spaceNames = (c.spaceKeys ?? [])\n .map((k) => spaceNamesByKey.get(k))\n .filter(Boolean)\n .join(\", \");\n return (\n <Box key={c.id}>\n <Text color={selected ? \"cyan\" : undefined} bold={selected}>\n {selected ? \"▸ \" : \" \"}\n {contentTypeLabel(c)}{\" \"}\n {truncate(title, maxTitleWidth)}\n </Text>\n <Text dimColor>\n {\" \".repeat(\n Math.max(\n 0,\n maxTitleWidth - truncate(title, maxTitleWidth).length + 2\n )\n )}\n {spaceNames ? ` ${spaceNames} · ` : \" \"}\n {time}\n </Text>\n </Box>\n );\n });\n })()\n )}\n </Box>\n {imagePreview ? (\n <Box borderStyle=\"single\" borderColor=\"gray\" paddingX={1} paddingY={0}>\n <Text>{imagePreview}</Text>\n </Box>\n ) : null}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n {total} capture{total !== 1 ? \"s\" : \"\"}\n {search ? ` matching \"${truncate(search, 30)}\"` : \"\"}\n </Text>\n </Box>\n </Box>\n );\n}\n\nexport type RunTuiOptions = {\n reader: VaultReader;\n};\n\nexport function runTui(options: RunTuiOptions): void {\n render(React.createElement(ResurfTui, { reader: options.reader }));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/tui-app.tsx"],"sourcesContent":["import { exec } from \"child_process\";\nimport path from \"path\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Box, render, Text, useApp, useInput } from \"ink\";\nimport { TextInput } from \"@inkjs/ui\";\nimport type { CaptureIndex } from \"./reader.js\";\nimport type { VaultReader } from \"./reader.js\";\nimport { captureWithAbsoluteAttachmentPaths } from \"./format.js\";\n\nconst LIST_HEIGHT = 12;\nconst IMAGE_WIDTH = 36;\n\nfunction relativeTime(ts: number): string {\n const diff = Date.now() - ts;\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo ago`;\n return `${Math.floor(months / 12)}y ago`;\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return str.slice(0, max - 1) + \"…\";\n}\n\nfunction contentTypeLabel(c: CaptureIndex): string {\n const ct = c.contentType;\n if (Array.isArray(ct)) {\n if (ct.includes(\"image\")) return \"🖼\";\n if (ct.includes(\"link\")) return \"🔗\";\n if (ct.includes(\"note\")) return \"📝\";\n return \"📄\";\n }\n if (ct === \"image\") return \"🖼\";\n if (ct === \"link\") return \"🔗\";\n if (ct === \"note\") return \"📝\";\n return \"📄\";\n}\n\nfunction openCapture(id: string): void {\n const url = `resurf://capture/${id}`;\n exec(`open \"${url}\"`);\n}\n\ntype ResurfTuiProps = {\n reader: VaultReader;\n};\n\nfunction ResurfTui({ reader }: ResurfTuiProps) {\n const { exit } = useApp();\n const [search, setSearch] = useState(\"\");\n const [captures, setCaptures] = useState<CaptureIndex[]>([]);\n const [total, setTotal] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [focus, setFocus] = useState<\"search\" | \"list\">(\"search\");\n const [loading, setLoading] = useState(true);\n const [spaceNamesByKey, setSpaceNamesByKey] = useState<Map<string, string>>(new Map());\n const [imagePreview, setImagePreview] = useState<string | null>(null);\n const imageLoadRef = useRef<number>(0);\n\n const loadCaptures = useCallback(async (query: string) => {\n setLoading(true);\n const [result, spaces] = await Promise.all([\n reader.listCaptures({\n search: query || undefined,\n searchTerms: query.trim() ? [query.trim()] : undefined,\n limit: 100,\n sortBy: \"updated\",\n sortOrder: \"desc\",\n }),\n reader.getSpaces(),\n ]);\n setCaptures(result.captures);\n setTotal(result.total);\n setSelectedIndex(0);\n setSpaceNamesByKey(new Map(spaces.map((s) => [s.key, s.name])));\n setLoading(false);\n }, [reader]);\n\n useEffect(() => {\n loadCaptures(\"\");\n }, [loadCaptures]);\n\n const selectedCapture = captures[selectedIndex];\n const isImageType =\n selectedCapture &&\n (selectedCapture.contentType === \"image\" ||\n (Array.isArray(selectedCapture.contentType) &&\n selectedCapture.contentType.includes(\"image\")));\n\n useEffect(() => {\n if (!isImageType || !selectedCapture) {\n setImagePreview(null);\n return;\n }\n const loadId = ++imageLoadRef.current;\n reader.getCaptureById(selectedCapture.id).then((full) => {\n if (loadId !== imageLoadRef.current) return;\n if (!full?.content || full.content.type !== \"attachment\" || typeof full.content.path !== \"string\") {\n setImagePreview(null);\n return;\n }\n const withPath = captureWithAbsoluteAttachmentPaths(full, reader.vaultPath);\n const absPath = (withPath.content as { path: string }).path;\n import(\"terminal-image\")\n .then((mod) => mod.default.file(absPath, { width: IMAGE_WIDTH }))\n .then((str) => {\n if (loadId === imageLoadRef.current) setImagePreview(str);\n })\n .catch(() => {\n if (loadId === imageLoadRef.current) setImagePreview(null);\n });\n });\n return () => {\n setImagePreview(null);\n };\n }, [reader, selectedCapture?.id, isImageType]);\n\n useInput((input, key) => {\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n exit();\n return;\n }\n if (focus === \"search\") {\n if (key.tab) setFocus(\"list\");\n if (key.return) {\n loadCaptures(search);\n setFocus(\"list\");\n }\n return;\n }\n if (key.tab) {\n setFocus(\"search\");\n return;\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n return;\n }\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(captures.length - 1, i + 1));\n return;\n }\n if (key.return && captures[selectedIndex]) {\n openCapture(captures[selectedIndex].id);\n exit();\n }\n });\n\n const maxTitleWidth = 48;\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Resurf\n </Text>\n <Text dimColor> — </Text>\n <Text dimColor>{path.basename(reader.vaultPath)}</Text>\n </Box>\n <Box flexDirection=\"column\" gap={0}>\n <Box>\n <Text color=\"gray\"> Search </Text>\n <TextInput\n placeholder=\"Search by title, note, space, tag... (Enter to search)\"\n value={search}\n onChange={setSearch}\n onSubmit={(value) => {\n loadCaptures(value);\n setFocus(\"list\");\n }}\n isDisabled={focus !== \"search\"}\n />\n </Box>\n <Text dimColor>\n Tab: focus · Enter (search): run · ↑↓: move · Enter (list): open · q: quit\n </Text>\n </Box>\n <Box flexDirection=\"row\" marginTop={1} minHeight={LIST_HEIGHT + 2} gap={2}>\n <Box flexDirection=\"column\" flexGrow={1} minWidth={0}>\n {loading ? (\n <Text dimColor>Loading…</Text>\n ) : captures.length === 0 ? (\n <Text dimColor>No captures found.</Text>\n ) : (\n (() => {\n const start = Math.max(\n 0,\n Math.min(selectedIndex, captures.length - LIST_HEIGHT)\n );\n const visible = captures.slice(start, start + LIST_HEIGHT);\n return visible.map((c, i) => {\n const actualIndex = start + i;\n const selected = focus === \"list\" && actualIndex === selectedIndex;\n const title = (c.title || \"Untitled\").trim() || \"—\";\n const time = relativeTime(c.updatedAt ?? c.createdAt);\n const spaceNames = (c.spaceKeys ?? [])\n .map((k) => spaceNamesByKey.get(k))\n .filter(Boolean)\n .join(\", \");\n return (\n <Box key={c.id}>\n <Text color={selected ? \"cyan\" : undefined} bold={selected}>\n {selected ? \"▸ \" : \" \"}\n {contentTypeLabel(c)}{\" \"}\n {truncate(title, maxTitleWidth)}\n </Text>\n <Text dimColor>\n {\" \".repeat(\n Math.max(\n 0,\n maxTitleWidth - truncate(title, maxTitleWidth).length + 2\n )\n )}\n {spaceNames ? ` ${spaceNames} · ` : \" \"}\n {time}\n </Text>\n </Box>\n );\n });\n })()\n )}\n </Box>\n {imagePreview ? (\n <Box borderStyle=\"single\" borderColor=\"gray\" paddingX={1} paddingY={0}>\n <Text>{imagePreview}</Text>\n </Box>\n ) : null}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n {total} capture{total !== 1 ? \"s\" : \"\"}\n {search ? ` matching \"${truncate(search, 30)}\"` : \"\"}\n </Text>\n </Box>\n </Box>\n );\n}\n\nexport type RunTuiOptions = {\n reader: VaultReader;\n};\n\nexport function runTui(options: RunTuiOptions): void {\n render(React.createElement(ResurfTui, { reader: options.reader }));\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "resurf",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "CLI to access Resurf vault data from any app or terminal",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "resurf": "./dist/index.js"
9
9
  },
10
- "files": ["dist"],
10
+ "files": ["dist", "skill"],
11
11
  "scripts": {
12
12
  "build": "tsup",
13
13
  "dev": "tsup --watch",
package/skill/SKILL.md ADDED
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: resurf
3
+ description: Access the user's Resurf knowledge base (captures, notes, links, images, bookmarks) via the `resurf` CLI. Use when the user mentions Resurf, captures, saved links, bookmarks, knowledge base, or asks to find/search/list their saved content. Also use when the user wants to create a new capture, open a capture, or export data from Resurf.
4
+ ---
5
+
6
+ # Resurf CLI
7
+
8
+ Query the user's local Resurf vault (knowledge base) directly from the terminal. No API keys or network required -- all data is local JSON files.
9
+
10
+ ## When to Use
11
+
12
+ - Use this skill when the user mentions Resurf, captures, saved links, bookmarks, or knowledge base
13
+ - Use when the user wants to find, search, or list their saved content
14
+ - Use when the user wants to create a new capture, open a capture, or export data from Resurf
15
+
16
+ ## Install the CLI
17
+
18
+ ```bash
19
+ npm install -g resurf
20
+ ```
21
+
22
+ Or with Bun:
23
+
24
+ ```bash
25
+ bun install -g resurf
26
+ ```
27
+
28
+ ## Agent best practice: always use `--json --compact`
29
+
30
+ For AI agent use, **always** pass `--json --compact`. This strips heavy fields (colorPalette, embedding, etc.) and reduces token usage by ~60-70%.
31
+
32
+ ```bash
33
+ resurf list --json --compact
34
+ resurf get <id> --json --compact
35
+ resurf export --json --compact
36
+ ```
37
+
38
+ ## Available Commands
39
+
40
+ ```bash
41
+ resurf list [options] # List captures
42
+ resurf get <id> [id2 ...] # Get captures by ID (supports partial IDs, multiple)
43
+ resurf search <query> # Search by title, note, TLDR, tags, source
44
+ resurf spaces # List all spaces
45
+ resurf areas # List all areas
46
+ resurf tags # List all tags
47
+ resurf canvases # List all canvases
48
+ resurf stats # Vault statistics
49
+ resurf vaults # List available vaults
50
+ resurf tui # Interactive TUI to browse and open captures
51
+ resurf open <id> # Open capture in Resurf app
52
+ resurf create # Create a capture via resurf:// protocol
53
+ resurf export # Export captures as JSON
54
+ ```
55
+
56
+ ## Key Flags
57
+
58
+ | Flag | On | Effect |
59
+ | --- | --- | --- |
60
+ | `--json --compact` | global | JSON output with heavy fields stripped (always use for agents) |
61
+ | `--include-content` | list | Load full capture content, not just index (includes `content.path`) |
62
+ | `--paths` | list | Output one absolute attachment path per line |
63
+ | `--space <name or key>` | list, export | Filter by space name or key (name resolved automatically) |
64
+ | multiple IDs | get | `resurf get id1 id2 --json` returns array |
65
+
66
+ ## Filtering & Searching
67
+
68
+ ```bash
69
+ resurf list --space "Space Name" --type <type> --tag <tag> --pinned --limit 50
70
+ resurf list --search "keyword" --sort updated --asc
71
+ resurf search "react hooks" --limit 10
72
+ ```
73
+
74
+ `--space` accepts either a space key (e.g. `space-Bm8mCbJ`) or a space name (e.g. `"Mobile App Inspirations"`). Name is resolved automatically (case-insensitive).
75
+
76
+ Content types: `note`, `link`, `tweet`, `youtube`, `image`, `video`, `audio`, `recording`, `todo`, `pdf`
77
+
78
+ ## Data Shape (JSON)
79
+
80
+ **Capture fields**: `id`, `type` (note/link/attachment), `contentType`, `title`, `tags[]`, `spaceKeys[]`, `source`, `note`, `tldr`, `isPinned`, `triageStatus` (inbox/later/archive), `createdAt`, `updatedAt`, `content` (nested object with type-specific data)
81
+
82
+ **Content types**:
83
+ - `note`: `content.textContent` (plain text), `content.noteContent` (ProseMirror JSON)
84
+ - `link`: `content.url`, `content.ogTitle`, `content.ogDescription`, `content.articleContent`
85
+ - `attachment`: `content.mimeType`, `content.size`, `content.path` (**always absolute path** in CLI JSON output)
86
+
87
+ **Space fields**: `id`, `name`, `key`, `areaKey`, `icon`, `color`, `noteCount`, `isPinned`
88
+
89
+ **Exit codes**: `0` = success, `1` = error (not found, usage, or vault invalid)
90
+
91
+ ## AI Agent Workflows (optimized for minimal tool calls)
92
+
93
+ ### Get all images in a space with full content (1 call)
94
+ ```bash
95
+ resurf list --space "Mobile App Inspirations" --type image --json --compact --include-content
96
+ ```
97
+ Returns full captures with absolute `content.path` for each attachment.
98
+
99
+ ### Get just the file paths (1 call, zero JSON parsing)
100
+ ```bash
101
+ resurf list --space "UI" --type image --paths
102
+ ```
103
+ Outputs one absolute file path per line.
104
+
105
+ ### Get multiple captures at once (1 call)
106
+ ```bash
107
+ resurf get id1 id2 id3 --json --compact
108
+ ```
109
+ Returns a JSON array of full captures.
110
+
111
+ ### Search and get results with content
112
+ ```bash
113
+ resurf search "design systems" --json --compact
114
+ ```
115
+
116
+ ### Export a space
117
+ ```bash
118
+ resurf export --space "Research" --json --compact
119
+ ```
120
+
121
+ ### Open attachment in default app (macOS)
122
+ ```bash
123
+ open "$(resurf list --space "UI" --type image --paths | head -1)"
124
+ ```
125
+
126
+ ## Common Workflows
127
+
128
+ ### Create a capture from the CLI
129
+ ```bash
130
+ resurf create --content "Note text" --title "My Note" --tags "tag1,tag2" --space <key>
131
+ resurf create --url "https://example.com" --title "Bookmark"
132
+ ```
133
+
134
+ ## Vault Path
135
+
136
+ Auto-detected from `~/Library/Application Support/Resurf/app.json` or default locations. Override with `--vault <path>`. List vaults: `resurf vaults --json`.