73 lines
2.8 KiB
JavaScript
73 lines
2.8 KiB
JavaScript
/**
|
|
* Where this plugin keeps things on disk.
|
|
*
|
|
* Everything lives under `$DSH_HOME`, never inside the workspace. That is the
|
|
* whole point of the plugin's economics: the index holds a vector per code
|
|
* chunk, which for a 300k-line repository is tens of megabytes — several times
|
|
* the size of the source it describes. A multi-megabyte binary file appearing
|
|
* in a repository the user might commit, or that a `git status` might surface
|
|
* mid-task, is a worse outcome than the tokens it saves.
|
|
*
|
|
* The index is also disposable by construction. It is keyed by content hash, so
|
|
* deleting it costs one re-embed and nothing else — which is what makes it safe
|
|
* to treat as a cache rather than a document.
|
|
*/
|
|
|
|
import { createHash } from 'node:crypto'
|
|
import { homedir } from 'node:os'
|
|
import { basename, join, resolve } from 'node:path'
|
|
|
|
/** Plugin name. Also the `$DSH_HOME/<name>` directory. */
|
|
export const PLUGIN_NAME = 'dsh-recall'
|
|
|
|
/** The dsh home directory: `$DSH_HOME`, else `~/.dsh`. */
|
|
export function resolveDshHome(env = process.env) {
|
|
const configured = typeof env.DSH_HOME === 'string' ? env.DSH_HOME.trim() : ''
|
|
return configured === '' ? join(homedir(), '.dsh') : resolve(configured)
|
|
}
|
|
|
|
/**
|
|
* A stable, filesystem-safe key for one workspace.
|
|
*
|
|
* The basename is for a human reading the directory listing; the hash is what
|
|
* actually identifies it, so two checkouts both named `app` cannot collide and
|
|
* one checkout moved between parents does not silently inherit the other's
|
|
* index.
|
|
*/
|
|
export function workspaceKey(cwd) {
|
|
const absolute = resolve(cwd)
|
|
const name =
|
|
basename(absolute)
|
|
.replace(/[^A-Za-z0-9._-]+/g, '-')
|
|
.replace(/^[-.]+|-+$/g, '') || 'workspace'
|
|
const hash = createHash('sha256').update(absolute).digest('hex').slice(0, 8)
|
|
return `${name}-${hash}`
|
|
}
|
|
|
|
/** Root of this plugin's own data, independent of any workspace. */
|
|
export function pluginDataDir(dshHome) {
|
|
return join(dshHome, PLUGIN_NAME)
|
|
}
|
|
|
|
/** Per-workspace state directory. */
|
|
export function workspaceStateDir(dshHome, cwd) {
|
|
return join(pluginDataDir(dshHome), 'workspaces', workspaceKey(cwd))
|
|
}
|
|
|
|
/** The SQLite index for one workspace. */
|
|
/**
|
|
* The SQLite index for one workspace, optionally narrowed to one focus.
|
|
*
|
|
* The workspace-wide index deliberately keeps its original path. Focus is
|
|
* additive: a deployment that never sets one must find the index it already
|
|
* built, not silently re-embed the repository into a new file.
|
|
*
|
|
* @param {string} dshHome harness home
|
|
* @param {string} cwd workspace root
|
|
* @param {string} [focus] focus key, `.` for the workspace itself
|
|
*/
|
|
export function indexFile(dshHome, cwd, focus = '.') {
|
|
const base = workspaceStateDir(dshHome, cwd)
|
|
return focus === '.' ? join(base, 'index.sqlite') : join(base, 'focus', focus, 'index.sqlite')
|
|
}
|