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 +65 -0
- package/dist/index.js.map +1 -1
- package/dist/tui-app.js +16 -9
- package/dist/tui-app.js.map +1 -1
- package/package.json +2 -2
- package/skill/SKILL.md +136 -0
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:
|
|
65
|
-
searchTerms:
|
|
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
|
|
77
|
+
}, [reader]);
|
|
78
78
|
useEffect(() => {
|
|
79
|
-
|
|
80
|
-
|
|
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)
|
package/dist/tui-app.js.map
CHANGED
|
@@ -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.
|
|
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`.
|