/** * Config file support for Hasna Skills * * Loads configuration from: * 1. Project-local: ./skills.config.json (highest priority) * 2. Global: ~/.hasna/skills/config.json (JSON format, lowest priority) * (backward compat: also checks ~/.skillsrc) * * Values from the project config override global config. */ /** * There is no deployment "mode" key. * * Skills has one deployment story: you run it. Whether this CLI talks to a * server is not a product variant, it is one fact — whether an API origin is * configured (apiUrl here, or $SKILLS_API_URL). Nothing else may be derived * from a declared label, because a label can disagree with the configuration * it claims to describe. * * One caveat, true at the time of writing and tracked separately: getApiUrl() * in auth-store.ts still falls back to a built-in origin when neither is set, * so "no origin configured" is not yet the same as "sends nothing anywhere" on * the auth path. Removing that fallback belongs to the no-vendor-defaults work, * not here; remote-registry.ts already fails closed and is the model. * * Configs written by older versions may still carry a "mode" key on disk. That is * refused rather than ignored - see lib/retired-settings.ts for why silence is * the worse of the two failures - and `skills config unset mode` removes it. */ export interface SkillsConfig { defaultAgent?: "claude" | "codex" | "gemini" | "pi" | "opencode" | "all"; defaultScope?: "global" | "project"; format?: "compact" | "json" | "csv"; apiUrl?: string; extensionsDir?: string; } export type ConfigScope = "global" | "project"; /** * Environment variable that relocates the skills data directory. * * Exported so that every reader agrees on the name: previously the literal was * duplicated in portable-skills.ts and honoured there but *not* in getDataDir(), * which is what made the override only half work (see getDataDir below). */ export declare const DATA_DIR_ENV = "HASNA_SKILLS_DIR"; /** * Subfolder of the data directory holding the installed skill corpus. * * ~/.hasna/skills is the skills *app* folder, matching every sibling Hasna app: * mementos keeps agents/ beside config.json and mementos.db, accounts keeps * profiles/ beside accounts.json, knowledge keeps artifacts/ and cache/ beside * auth.json. Each puts app data at the app root and content in a named subfolder. * * Skills used to be the exception, writing one folder per skill straight into the * app root next to config.json and skills.db. That is the only reason a denylist * of "entries that look like skills but aren't" ever had to exist; no sibling app * needs one. With the corpus under installed/, a skill may be named `config` or * `custom` without colliding with anything. */ export declare const INSTALLED_SKILLS_DIRNAME = "installed"; /** * Get the data directory for skills global config/data. * Default: ~/.hasna/skills/, overridable with $HASNA_SKILLS_DIR. * Auto-migrates from ~/.skills/ and ~/.skillsrc without deleting legacy data. */ export declare function getDataDir(): string; /** * Get the config file path for a given scope */ export declare function getConfigPath(scope: ConfigScope): string; /** * Load merged config: project-local overrides global */ export declare function loadConfig(): SkillsConfig; /** * Save a single config key-value pair to the specified scope */ export declare function saveConfig(key: string, value: string, scope?: ConfigScope): void; /** * Remove a single config key from the specified scope. * * This is the counterpart that makes "no deployment mode" workable. Running on * this machine is the absence of a configured apiUrl, so there has to be a way * to get back to that state; previously the only way to express the intent was * to set mode=local, and that key is gone. * * Returns whether the key was actually present, so callers can distinguish * "removed" from "there was nothing to remove" instead of guessing. */ export declare function unsetConfig(key: string, scope?: ConfigScope): boolean;