UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

134 lines (133 loc) • 7.12 kB
export type StoreRole = "primary" | "team" | "readonly"; export type StoreSyncMode = "managed-git" | "pull-only"; export interface StoreEntry { /** Immutable 8-char hex identifier. */ id: string; /** Human-readable name (unique within registry). */ name: string; /** Absolute path to the store root directory. */ path: string; /** Store role — determines read/write/sync behavior. */ role: StoreRole; /** Git remote URL (optional). */ remote?: string; /** Sync mode for git operations. */ sync: StoreSyncMode; /** Projects claimed by this store (for write routing in phase 2). */ projects?: string[]; /** * Whether {@link path} exists on this machine. Computed by * {@link resolveAllStores}; never persisted to stores.yaml. `undefined` on * hand-built entries that never went through resolution. */ available?: boolean; } export interface StoreRegistry { version: 1; stores: StoreEntry[]; } /** Bootstrap metadata committed to a team store repo root. */ export interface TeamBootstrap { name: string; description?: string; default_role?: StoreRole; } /** * The synced registry at the store root. It holds the primary store's entry * only, so every machine on this personal store agrees on its id. Team and * readonly stores used to live here too, which pushed one machine's `team * join` onto every other machine and left doctor failing forever there. */ export declare const STORES_FILENAME = "stores.yaml"; /** * The stores attached on this machine, under `.runtime/` (never synced). * Using the personal store must never connect a machine to a team store. */ export declare const ATTACHED_STORES_FILENAME = "attached-stores.yaml"; export declare function storesFilePath(phrenPath: string): string; export declare function attachedStoresFilePath(phrenPath: string): string; export declare function generateStoreId(): string; /** * What a registry read actually found: the primary store from the synced * stores.yaml plus the stores attached on this machine. `registry` is what * could be honored; `problems` says what couldn't; `lossy` means one of the * two files EXISTS but the result does not fully represent it (unreadable, * unparsable, entries skipped, or validation failed). Mutators must refuse to * write while a read is lossy — writing back would silently destroy the * entries we skipped. */ export interface RegistryReadResult { registry: StoreRegistry | null; problems: string[]; lossy: boolean; } export declare function readStoreRegistryDetailed(phrenPath: string, options?: { migrateAttachments?: boolean; }): RegistryReadResult; /** Identity carried by the store itself, never an attachment ID or path hash. * Reading identity must not migrate this machine's attachments. */ export declare function registeredStoreIdentity(phrenPath: string): string | undefined; /** Explicit, idempotent registration for legacy stores. Callers enforce the * existing manage_config permission. No task or attachment is rewritten. */ export declare function registerStoreIdentity(phrenPath: string): string; export declare function readStoreRegistry(phrenPath: string): StoreRegistry | null; /** * Writes this machine's view of the stores: the primary entry to the synced * stores.yaml (only when it changed) and every other store to the machine's * own attached-stores.yaml. */ export declare function writeStoreRegistry(phrenPath: string, registry: StoreRegistry): void; /** True when a store's directory exists on this machine. */ export declare function storePathExists(storePath: string): boolean; /** * Resolve the full list of stores on this machine. This is the **key * backward-compat function**: * - The primary comes from the synced stores.yaml, or is implicit when the * file is missing * - Then every store attached on this machine (.runtime/attached-stores.yaml) * - Then PHREN_FEDERATION_PATHS entries as readonly stores * * Every returned entry carries `available`, recording whether its path exists * here. Attached stores whose folder is gone are deliberately **still * returned**: callers must be able to see that a store claims a project before * deciding what to do, otherwise its writes would land in the primary store. */ export declare function resolveAllStores(phrenPath: string): StoreEntry[]; /** Task discovery must neither migrate attachments nor treat a partial registry * as proof of an unambiguous task identity. No writes or warnings on this path. */ export declare function readTaskStoreEntries(phrenPath: string): StoreEntry[]; /** * Non-primary entries in the synced stores.yaml that this machine does not use: * joined on another machine with an older phren. Attached ones were moved into * attached-stores.yaml; the rest mean nothing here. */ export declare function ignoredSyncedStores(phrenPath: string): StoreEntry[]; /** Stores attached on this machine whose directory is missing. */ export declare function getUnavailableStores(phrenPath: string): StoreEntry[]; /** * One actionable sentence about a store attached on this machine whose folder * is gone. Shared by write routing, `phren status`, and `phren doctor` so all * three name the same store, the same expected path, and the same remedy. */ export declare function describeUnavailableStore(store: StoreEntry): string; /** The primary store (role=primary). Falls back to implicit entry. */ export declare function getPrimaryStore(phrenPath: string): StoreEntry; /** All stores that can be read (all roles). */ export declare function getReadableStores(phrenPath: string): StoreEntry[]; /** Non-primary stores (for federation search, multi-store sync). */ export declare function getNonPrimaryStores(phrenPath: string): StoreEntry[]; /** Find a store by name. */ export declare function findStoreByName(phrenPath: string, name: string): StoreEntry | undefined; /** Get project directories for a store, filtered by the store's subscription list (if set). */ export declare function getStoreProjectDirs(store: StoreEntry): string[]; export declare function readTeamBootstrap(storePath: string): TeamBootstrap | null; /** Attach a store on this machine. Creates stores.yaml for the primary if needed. Uses file locking. */ export declare function addStoreToRegistry(phrenPath: string, entry: StoreEntry): void; /** Remove a store entry by name. Refuses to remove primary. Uses file locking. */ export declare function removeStoreFromRegistry(phrenPath: string, name: string): StoreEntry; /** Update the projects[] claim list for a store. Uses file locking. */ export declare function updateStoreProjects(phrenPath: string, storeName: string, projects: string[]): void; /** Add projects to a store's subscription list. Deduplicates. Uses file locking. */ export declare function subscribeStoreProjects(phrenPath: string, storeName: string, projects: string[]): void; /** Remove projects from a store's subscription list. Uses file locking. */ export declare function unsubscribeStoreProjects(phrenPath: string, storeName: string, projects: string[]): void;