diff --git a/src/commands/ai.ts b/src/commands/ai.ts index 4653adc5..31aadd04 100644 --- a/src/commands/ai.ts +++ b/src/commands/ai.ts @@ -11,14 +11,13 @@ */ import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = join(REPO_ROOT, "profiles"); -const REGISTRY_PATH = join(REPO_ROOT, "docs", "registry", "index.json"); +const PROFILES_DIR = join(repoRoot(), "profiles"); +const REGISTRY_PATH = join(repoRoot(), "docs", "registry", "index.json"); interface MatchedProfile { name: string; diff --git a/src/commands/ask.ts b/src/commands/ask.ts index ef576f36..fa6eb31b 100644 --- a/src/commands/ask.ts +++ b/src/commands/ask.ts @@ -3,11 +3,10 @@ */ import { readFileSync, existsSync, readdirSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); export async function run(args: string[]): Promise { const query = args.filter(a => !a.startsWith("-")).join(" "); diff --git a/src/commands/benchmark.ts b/src/commands/benchmark.ts index b4b7c992..55b2e1e4 100644 --- a/src/commands/benchmark.ts +++ b/src/commands/benchmark.ts @@ -12,13 +12,11 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { homedir } from "node:os"; -import { resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; import { parse as parseYaml } from "yaml"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); interface BenchmarkResult { profile: string; @@ -154,7 +152,7 @@ function scanSessions(profileName: string): BenchmarkResult { // Load profile to find unused skills try { - const profilePath = join(REPO_ROOT, "profiles", profileName, "profile.yaml"); + const profilePath = join(repoRoot(), "profiles", profileName, "profile.yaml"); if (existsSync(profilePath)) { const prof = parseYaml(readFileSync(profilePath, "utf8")); const declared = (prof.skills?.local ?? []).map((s: string | { id: string }) => @@ -226,7 +224,7 @@ Metrics: } if (all) { - const profilesDir = join(REPO_ROOT, "profiles"); + const profilesDir = join(repoRoot(), "profiles"); const profiles = readdirSync(profilesDir) .filter(d => !d.startsWith("_") && existsSync(join(profilesDir, d, "profile.yaml"))); diff --git a/src/commands/builtin.ts b/src/commands/builtin.ts index d57a6000..89bcd14c 100644 --- a/src/commands/builtin.ts +++ b/src/commands/builtin.ts @@ -8,11 +8,10 @@ */ import { readFileSync, writeFileSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); const CORE_YAML = join(PROFILES_DIR, "core", "profile.yaml"); function getCoreSkills(): string[] { diff --git a/src/commands/clean.ts b/src/commands/clean.ts index 2871edf3..bbeead83 100644 --- a/src/commands/clean.ts +++ b/src/commands/clean.ts @@ -3,15 +3,14 @@ */ import { existsSync, readdirSync, rmSync, statSync, readFileSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { listProfiles } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); const RUNTIME_ROOT = join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", "runtime"); -const CACHE_ROOT = join(REPO_ROOT, "profiles", "_cache", "npx"); +const CACHE_ROOT = join(repoRoot(), "profiles", "_cache", "npx"); export async function run(args: string[]): Promise { if (args.includes("-h") || args.includes("--help")) { diff --git a/src/commands/cli.ts b/src/commands/cli.ts index 974c989a..b4c0c678 100644 --- a/src/commands/cli.ts +++ b/src/commands/cli.ts @@ -16,16 +16,15 @@ import { spawnSync } from "node:child_process"; import { readFileSync } from "node:fs"; -import { join, dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir, platform } from "node:os"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; import { requiredClisFor } from "../lib/cli-extractor"; import { listProfiles } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const RECIPES_PATH = join(REPO_ROOT, "resources", "cli-recipes.json"); +const RECIPES_PATH = join(repoRoot(), "resources", "cli-recipes.json"); type Recipe = Partial>; diff --git a/src/commands/cloud.ts b/src/commands/cloud.ts index 6e909138..d130369a 100644 --- a/src/commands/cloud.ts +++ b/src/commands/cloud.ts @@ -8,12 +8,11 @@ */ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); const CONFIG_DIR = join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue"); const CREDS_FILE = join(CONFIG_DIR, "credentials.json"); const API_BASE = process.env.CUE_API_URL ?? "https://api.getcue.dev"; diff --git a/src/commands/cost.ts b/src/commands/cost.ts index 7944104e..5be8c7a7 100644 --- a/src/commands/cost.ts +++ b/src/commands/cost.ts @@ -3,11 +3,11 @@ */ import { readFileSync, existsSync, readdirSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile, listProfiles } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; import { skillAlwaysOnTokens, skillBodyTokens, @@ -15,7 +15,6 @@ import { SKILLS_ROOT, } from "../lib/profile-metrics"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); // Expand wildcard (*/*) to all actual skill IDs on disk. function expandSkillIds(ids: string[]): string[] { @@ -39,7 +38,7 @@ function expandSkillIds(ids: string[]): string[] { } return result; } -const MCP_CONFIGS_DIR = join(REPO_ROOT, "resources", "mcps", "configs"); +const MCP_CONFIGS_DIR = join(repoRoot(), "resources", "mcps", "configs"); // Baseline always-on CLAUDE.md cost for a profile that hasn't been materialized // yet. Dominated by the shared `core` persona + integrity protocol, so it's diff --git a/src/commands/create-profile.ts b/src/commands/create-profile.ts index 1357f741..3b34679f 100644 --- a/src/commands/create-profile.ts +++ b/src/commands/create-profile.ts @@ -13,10 +13,9 @@ */ import { mkdir, writeFile, access } from "node:fs/promises"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); interface ParsedArgs { name: string | null; @@ -101,7 +100,7 @@ export async function run(args: string[]): Promise { return 1; } - const profilesDir = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); + const profilesDir = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); const profileDir = join(profilesDir, parsed.name); const yamlPath = join(profileDir, "profile.yaml"); diff --git a/src/commands/debug.ts b/src/commands/debug.ts index 0848376d..57f09b29 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -11,15 +11,14 @@ import { resolve, join, dirname } from "node:path"; import { existsSync, readFileSync, lstatSync, readlinkSync } from "node:fs"; -import { fileURLToPath } from "node:url"; import { homedir } from "node:os"; import { loadProfile } from "../lib/profile-loader"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const MCP_CONFIGS_DIR = join(REPO_ROOT, "resources", "mcps", "configs"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); +const MCP_CONFIGS_DIR = join(repoRoot(), "resources", "mcps", "configs"); const RUNTIME_ROOT = join(homedir(), ".config", "cue", "runtime"); export async function run(args: string[]): Promise { @@ -119,7 +118,7 @@ export async function run(args: string[]): Promise { } // 4b. Rules / Commands / Hooks - const RESOURCES_ROOT = join(REPO_ROOT, "resources"); + const RESOURCES_ROOT = join(repoRoot(), "resources"); let resourceIssues = 0; for (const [kind, refs, base] of [ ["Rules", profile.rules, join(RESOURCES_ROOT, "rules")], diff --git a/src/commands/diff.ts b/src/commands/diff.ts index 1beacfc1..47904a5b 100644 --- a/src/commands/diff.ts +++ b/src/commands/diff.ts @@ -4,14 +4,13 @@ */ import { readFileSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); function estimateTokens(text: string): number { return Math.ceil(text.length / 4); } function getSkillTokens(id: string): number { diff --git a/src/commands/discover.ts b/src/commands/discover.ts index 3d908e14..95ef056f 100644 --- a/src/commands/discover.ts +++ b/src/commands/discover.ts @@ -12,8 +12,7 @@ import { spawnSync } from "node:child_process"; import { readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; import { homedir } from "node:os"; import { listProfiles } from "../lib/profile-loader"; @@ -21,8 +20,8 @@ import { clusterByKeywords, clusterByEmbeddings, unclustered, type Cluster, type import { findRealClaudeBin } from "../lib/claude-binary"; import { fetchCompanionFiles, detectSkillPath } from "../lib/companion-fetch"; import { gateFreshSkill } from "./security"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); // Cache path resolved lazily so tests can redirect via XDG_CONFIG_HOME without // re-importing the module. (Bun shares module state across test files; const // values captured at import time wouldn't see runtime env changes.) @@ -30,7 +29,7 @@ function cacheDir(): string { return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", "discover"); } function cacheFile(): string { return join(cacheDir(), "gems.json"); } -const DEFAULT_EXPORT = join(REPO_ROOT, "docs", "discovered.md"); +const DEFAULT_EXPORT = join(repoRoot(), "docs", "discovered.md"); // --------------------------------------------------------------------------- // Types @@ -438,7 +437,7 @@ export function tierColorFor(score: number): string { * Cheap enough to call per-gem (cached once per session via the closure pattern * in renderers). */ -export function getInstalledIn(gem: GemRepo, profilesDir = join(REPO_ROOT, "profiles")): string[] { +export function getInstalledIn(gem: GemRepo, profilesDir = join(repoRoot(), "profiles")): string[] { if (!existsSync(profilesDir)) return []; const hits: string[] = []; const slugs = [gem.name.toLowerCase(), gem.full_name.toLowerCase()]; @@ -2179,7 +2178,7 @@ async function cmdInstall(opts: { profile?: string; minScore: number; minQuality autoInstallClis(gem.name, { yes: opts.yes }); // Add to profile.yaml - const profileYaml = join(REPO_ROOT, "profiles", targetProfile, "profile.yaml"); + const profileYaml = join(repoRoot(), "profiles", targetProfile, "profile.yaml"); if (existsSync(profileYaml)) { const content = readFileSync(profileYaml, "utf8"); // Find the skill ID (use repo name as fallback) @@ -2598,7 +2597,7 @@ async function cmdDiscoverMcps(opts: { limit: number; minScore: number; json: bo if (opts.install) { const targetProfile = opts.profile ?? getActiveProfile() ?? "core"; - const profileYaml = join(REPO_ROOT, "profiles", targetProfile, "profile.yaml"); + const profileYaml = join(repoRoot(), "profiles", targetProfile, "profile.yaml"); if (!existsSync(profileYaml)) { process.stderr.write(` ⚠️ Profile "${targetProfile}" not found at ${profileYaml}\n`); return 1; @@ -2854,7 +2853,7 @@ Examples: const minSize = minSizeIdx >= 0 ? parseInt(args[minSizeIdx + 1] ?? "3", 10) : 3; const outIdx = args.indexOf("--out"); const outDir = outIdx >= 0 && args[outIdx + 1] ? args[outIdx + 1]! - : join(REPO_ROOT, ".cue-suggestions"); + : join(repoRoot(), ".cue-suggestions"); const dryRun = args.includes("--dry-run"); const noClaude = args.includes("--no-claude"); const embeddings = args.includes("--embeddings"); diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index b8dd8dcf..80cb8aa4 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -18,7 +18,6 @@ import { readFileSync, existsSync, lstatSync, readlinkSync, readdirSync } from "node:fs"; import { readFile, writeFile, rm } from "node:fs/promises"; import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; import { spawnSync } from "node:child_process"; import { listProfiles, loadProfile } from "../lib/profile-loader"; @@ -28,12 +27,12 @@ import { detectMissingDependencies } from "../lib/skill-dependencies"; import { shimInstalled, runInstall } from "./shell"; import { findRealClaudeBin } from "../lib/claude-binary"; import { homedir } from "node:os"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const MCP_CONFIGS_DIR = join(REPO_ROOT, "resources", "mcps", "configs"); -const QUALITY_GATES_DIR = join(REPO_ROOT, "resources", "quality-gates"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); +const MCP_CONFIGS_DIR = join(repoRoot(), "resources", "mcps", "configs"); +const QUALITY_GATES_DIR = join(repoRoot(), "resources", "quality-gates"); const RUNTIME_ROOT = join(process.env.HOME ?? "~", ".config", "cue", "runtime"); interface Issue { @@ -555,7 +554,7 @@ async function runCliDoctor(profileName: string | null, json: boolean): Promise< } // Load the optimizer's CLI extraction logic - const SKILLS_ROOT_PATH = join(REPO_ROOT, "resources", "skills", "skills"); + const SKILLS_ROOT_PATH = join(repoRoot(), "resources", "skills", "skills"); const HOME_SKILLS_PATH = join(process.env.HOME ?? "~", ".claude", "skills"); // Load profile to get skills diff --git a/src/commands/eval-behavior.ts b/src/commands/eval-behavior.ts index 5b30cbf4..aff51a04 100644 --- a/src/commands/eval-behavior.ts +++ b/src/commands/eval-behavior.ts @@ -14,16 +14,15 @@ */ import { readFileSync, } from "node:fs"; -import { join, resolve, dirname, isAbsolute } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, isAbsolute } from "node:path"; import { homedir } from "node:os"; import { loadProfile, listProfiles } from "../lib/profile-loader"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; import type { ResolvedProfile } from "../../profiles/_types"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const EVALS_ROOT = join(REPO_ROOT, "resources", "evals"); +const EVALS_ROOT = join(repoRoot(), "resources", "evals"); const bold = (s: string) => `\x1b[1m${s}\x1b[0m`; const green = (s: string) => `\x1b[32m${s}\x1b[0m`; diff --git a/src/commands/eval.ts b/src/commands/eval.ts index 5da401da..db77afb4 100644 --- a/src/commands/eval.ts +++ b/src/commands/eval.ts @@ -15,21 +15,20 @@ * cost-per-message and the score now use perMessage so they reflect reality. */ -import { resolve, join, dirname, isAbsolute } from "node:path"; +import { join, isAbsolute } from "node:path"; import { readFileSync, } from "node:fs"; -import { fileURLToPath } from "node:url"; import { homedir } from "node:os"; import { loadProfile, listProfiles } from "../lib/profile-loader"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; import { computeStats } from "../lib/analytics"; import type { ResolvedProfile } from "../../profiles/_types"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const RULES_ROOT = join(REPO_ROOT, "resources", "rules"); -const COMMANDS_ROOT = join(REPO_ROOT, "resources", "commands"); -const HOOKS_ROOT = join(REPO_ROOT, "resources", "hooks"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); +const RULES_ROOT = join(repoRoot(), "resources", "rules"); +const COMMANDS_ROOT = join(repoRoot(), "resources", "commands"); +const HOOKS_ROOT = join(repoRoot(), "resources", "hooks"); // Approximate fixed per-message cost of one hook entry in settings.json // (matcher + command path + description). Hook scripts themselves never enter diff --git a/src/commands/export-docker.ts b/src/commands/export-docker.ts index c6684cc5..dc12d2f1 100644 --- a/src/commands/export-docker.ts +++ b/src/commands/export-docker.ts @@ -6,14 +6,13 @@ */ import { writeFileSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; import { homedir } from "node:os"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); export async function run(args: string[]): Promise { if (args.includes("-h") || args.includes("--help")) { @@ -67,7 +66,7 @@ Options: } function extractCliDeps(profile: { skills: { local: { id: string }[] } }): string[] { - const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); + const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const { readFileSync } = require("node:fs"); const clis = new Set(); diff --git a/src/commands/icon.ts b/src/commands/icon.ts index f85e9859..711a646e 100644 --- a/src/commands/icon.ts +++ b/src/commands/icon.ts @@ -3,16 +3,15 @@ */ import { readFile, writeFile } from "node:fs/promises"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import * as p from "@clack/prompts"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; import { loadProfile } from "../lib/profile-loader"; import { homedir } from "node:os"; import { configDir } from "../lib/config-paths"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); const ICONS = [ "🐻", "🦋", "🦜", "🦉", "🐺", "🦚", "🐝", "🐆", "🐢", "🦄", @@ -28,7 +27,7 @@ export async function run(args: string[]): Promise { return 1; } - const profilesDir = process.env.CUE_PROFILES_DIR ?? process.env.SOUL_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); + const profilesDir = process.env.CUE_PROFILES_DIR ?? process.env.SOUL_PROFILES_DIR ?? join(repoRoot(), "profiles"); const yamlPath = join(profilesDir, profileName, "profile.yaml"); let text: string; diff --git a/src/commands/import-profile.ts b/src/commands/import-profile.ts index 85c0e110..e4a7ccc6 100644 --- a/src/commands/import-profile.ts +++ b/src/commands/import-profile.ts @@ -4,13 +4,12 @@ */ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs"; -import { resolve, dirname, join, } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); export async function run(args: string[]): Promise { const sub = args[0]; @@ -104,7 +103,7 @@ async function cmdExport(args: string[]): Promise { const profile = await loadProfile(profileName); const yaml = require("yaml"); - const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); + const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const base: Record = { name: profile.name, diff --git a/src/commands/install.ts b/src/commands/install.ts index 2605fa09..752178f6 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -30,6 +30,7 @@ import { } from "../lib/runtime-install"; import { resolveLocalSkill } from "../lib/resolver-local"; import { run as cliRun } from "./cli"; +import { repoRoot } from "../lib/repo-root"; type AnyAgent = AgentKind; @@ -73,9 +74,8 @@ interface RepoArgs { force: boolean; } -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(import.meta.dirname, "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? process.env.SOUL_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? process.env.SOUL_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const BYTE_CEILING = 160_000; function bold(s: string): string { return `\x1b[1m${s}\x1b[0m`; } diff --git a/src/commands/lock.ts b/src/commands/lock.ts index 9c368093..a060cb01 100644 --- a/src/commands/lock.ts +++ b/src/commands/lock.ts @@ -3,13 +3,12 @@ */ import { readFileSync, writeFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { createHash } from "node:crypto"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); export function isProfileLocked(profileName: string): { locked: boolean; by?: string; reason?: string } { const yamlPath = join(PROFILES_DIR, profileName, "profile.yaml"); diff --git a/src/commands/marketplace.ts b/src/commands/marketplace.ts index f8bbda03..b1351e9f 100644 --- a/src/commands/marketplace.ts +++ b/src/commands/marketplace.ts @@ -12,17 +12,16 @@ */ import { spawn, spawnSync } from "node:child_process"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { readFileSync, existsSync } from "node:fs"; import { resolveActiveProfile } from "../lib/cwd-resolver"; import { fetchCompanionFiles, detectSkillPath } from "../lib/companion-fetch"; import type { FileChange } from "../lib/pr-poster"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const REGISTRY_PATH = join(REPO_ROOT, "docs", "registry", "index.json"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const REGISTRY_PATH = join(repoRoot(), "docs", "registry", "index.json"); const REGISTRY_URL = "https://opencue.github.io/cue/registry/index.json"; // --------------------------------------------------------------------------- diff --git a/src/commands/materialize.ts b/src/commands/materialize.ts index 574a2513..3ba8848d 100644 --- a/src/commands/materialize.ts +++ b/src/commands/materialize.ts @@ -13,16 +13,15 @@ */ import { readFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; import { getAdapter, AGENT_IDS, } from "../lib/agent-adapters"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const MCP_CONFIGS_DIR = join(REPO_ROOT, "resources", "mcps", "configs"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); +const MCP_CONFIGS_DIR = join(repoRoot(), "resources", "mcps", "configs"); function loadSkillContent(id: string): { id: string; content: string } | null { const path = join(SKILLS_ROOT, id, "SKILL.md"); diff --git a/src/commands/mcps.ts b/src/commands/mcps.ts index c8c7a160..6426ce87 100644 --- a/src/commands/mcps.ts +++ b/src/commands/mcps.ts @@ -11,17 +11,16 @@ import { readFileSync } from "node:fs"; import { readFile, writeFile } from "node:fs/promises"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { spawnSync } from "node:child_process"; import { loadProfile } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const MCP_CONFIGS_DIR = join(REPO_ROOT, "resources", "mcps", "configs"); -const MCP_DOCS_DIR = join(REPO_ROOT, "resources", "mcps", "mcps"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const MCP_CONFIGS_DIR = join(repoRoot(), "resources", "mcps", "configs"); +const MCP_DOCS_DIR = join(repoRoot(), "resources", "mcps", "mcps"); // --------------------------------------------------------------------------- // Helpers diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index 33776780..cd15b1a8 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -3,12 +3,11 @@ */ import { existsSync, readFileSync, writeFileSync, readdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = join(REPO_ROOT, "profiles"); +const PROFILES_DIR = join(repoRoot(), "profiles"); const CURRENT_SCHEMA_VERSION = 2; interface Migration { diff --git a/src/commands/optimizer.ts b/src/commands/optimizer.ts index fa926a27..b752df68 100644 --- a/src/commands/optimizer.ts +++ b/src/commands/optimizer.ts @@ -8,18 +8,17 @@ */ import { readFileSync, readdirSync, existsSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { spawnSync } from "node:child_process"; import { listProfiles } from "../lib/profile-loader"; import { isKittyTerminal, transmitKittyImage, kittyPlaceholderLabel } from "../lib/kitty-image"; import { getSkillIcon, getMcpIcon, getRepoIcon, getCliIcon } from "../lib/brand-icons"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const HOME_SKILLS = join(homedir(), ".claude", "skills"); // --------------------------------------------------------------------------- diff --git a/src/commands/packs.ts b/src/commands/packs.ts index 1c9c3a6d..23de5186 100644 --- a/src/commands/packs.ts +++ b/src/commands/packs.ts @@ -8,13 +8,12 @@ */ import { writeFileSync, mkdirSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { listPacks, loadPack } from "../lib/pack-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PACKS_DIR = join(REPO_ROOT, "resources", "skill-packs"); +const PACKS_DIR = join(repoRoot(), "resources", "skill-packs"); export async function run(args: string[]): Promise { const sub = args[0] ?? "list"; diff --git a/src/commands/playground.ts b/src/commands/playground.ts index 3bf7a97b..9edb8dbe 100644 --- a/src/commands/playground.ts +++ b/src/commands/playground.ts @@ -6,15 +6,14 @@ */ import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, } from "node:path"; import { execSync, spawn } from "node:child_process"; import { tmpdir } from "node:os"; import { loadProfile } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); export async function run(args: string[]): Promise { if (args.includes("-h") || args.includes("--help") || !args[0] || args[0].startsWith("-")) { diff --git a/src/commands/profile-draft-skill.ts b/src/commands/profile-draft-skill.ts index 1c63f00f..76883d17 100644 --- a/src/commands/profile-draft-skill.ts +++ b/src/commands/profile-draft-skill.ts @@ -21,15 +21,14 @@ import { spawnSync } from "node:child_process"; import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { clusterByKeywords, type ClusterItem } from "../lib/cluster-skills"; import { readEvents, } from "../lib/analytics"; import { findRealClaudeBin } from "../lib/claude-binary"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); const FIRST_PROMPTS_DIR = join( process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", "first-prompts", @@ -210,7 +209,7 @@ Output: draft SKILL.md files under .cue-skill-drafts// for manual review. const minSize = minSizeIdx >= 0 ? parseInt(args[minSizeIdx + 1] ?? "3", 10) : 3; const outIdx = args.indexOf("--out"); const outDir = outIdx >= 0 && args[outIdx + 1] ? args[outIdx + 1]! - : join(REPO_ROOT, ".cue-skill-drafts"); + : join(repoRoot(), ".cue-skill-drafts"); const dryRun = args.includes("--dry-run"); const noClaude = args.includes("--no-claude"); diff --git a/src/commands/profile-suggest.ts b/src/commands/profile-suggest.ts index 7560aa96..1242e200 100644 --- a/src/commands/profile-suggest.ts +++ b/src/commands/profile-suggest.ts @@ -10,8 +10,7 @@ */ import { readdirSync, readFileSync, existsSync, statSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { parse as parseYaml } from "yaml"; @@ -23,15 +22,15 @@ import { type ClusterItem, } from "../lib/cluster-skills"; import { loadProfile } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; import { computeContextBudget, resolveContextWindow, splitSkillBytes, } from "../lib/token-budget"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = join(REPO_ROOT, "profiles"); -const SKILLS_DIR = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = join(repoRoot(), "profiles"); +const SKILLS_DIR = join(repoRoot(), "resources", "skills", "skills"); const DISCOVER_CACHE = join( process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", "discover", "gems.json", diff --git a/src/commands/score.ts b/src/commands/score.ts index f9660724..8e2f30e3 100644 --- a/src/commands/score.ts +++ b/src/commands/score.ts @@ -13,8 +13,6 @@ */ import { writeFileSync } from "node:fs"; -import { resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; import { loadProfile, listProfiles } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; @@ -24,7 +22,6 @@ import { firedSkills, } from "../lib/profile-metrics"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); // Baseline always-on CLAUDE.md tokens for an un-materialized profile (shared // core persona + integrity protocol dominate). Mirrors cost.ts. diff --git a/src/commands/security-audit.ts b/src/commands/security-audit.ts index f3be4a4e..61e18c61 100644 --- a/src/commands/security-audit.ts +++ b/src/commands/security-audit.ts @@ -9,15 +9,14 @@ */ import { readFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; import { parseAllowedTools } from "../lib/skill-sandbox"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); type Severity = "critical" | "high" | "medium" | "low"; @@ -79,7 +78,7 @@ function auditProfile(profile: Awaited>): AuditFi // MEDIUM: Hooks that can be bypassed for (const hook of profile.hooks) { - const hookPath = join(REPO_ROOT, "resources", "hooks", hook); + const hookPath = join(repoRoot(), "resources", "hooks", hook); if (existsSync(hookPath)) { try { const content = readFileSync(hookPath, "utf8"); diff --git a/src/commands/security.ts b/src/commands/security.ts index bd460b5f..51ea4a8e 100644 --- a/src/commands/security.ts +++ b/src/commands/security.ts @@ -12,15 +12,14 @@ */ import { readFileSync, readdirSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { listAllSkillIds } from "../lib/resolver-local"; import { loadProfile, } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const GLOBAL_SKILLS_ROOT = join(homedir(), ".claude", "skills"); export interface SecurityIssue { diff --git a/src/commands/skills-lint.ts b/src/commands/skills-lint.ts index 8c66f452..bfcb2811 100644 --- a/src/commands/skills-lint.ts +++ b/src/commands/skills-lint.ts @@ -12,13 +12,12 @@ */ import { readFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { listAllSkillIds } from "../lib/resolver-local"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); interface LintIssue { code: string; diff --git a/src/commands/skills-new.ts b/src/commands/skills-new.ts index 738c1016..217c1310 100644 --- a/src/commands/skills-new.ts +++ b/src/commands/skills-new.ts @@ -8,13 +8,12 @@ */ import { mkdirSync, writeFileSync, existsSync, cpSync, readFileSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { lint } from "../lib/skill-linter"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); function getArg(args: string[], flag: string): string | undefined { const idx = args.indexOf(flag); diff --git a/src/commands/skills-pin.ts b/src/commands/skills-pin.ts index 373fd9c1..74c59565 100644 --- a/src/commands/skills-pin.ts +++ b/src/commands/skills-pin.ts @@ -6,12 +6,11 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; import { spawnSync } from "node:child_process"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; import { homedir } from "node:os"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const PIN_HISTORY_PATH = join( process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", @@ -35,7 +34,7 @@ function saveHistory(h: PinHistory): void { function getCurrentCommit(skillId: string): string | null { const skillDir = join(SKILLS_ROOT, skillId); const res = spawnSync("git", ["log", "-1", "--format=%H", "--", skillDir], { - cwd: join(REPO_ROOT, "resources", "skills"), + cwd: join(repoRoot(), "resources", "skills"), encoding: "utf8", }); return res.status === 0 ? res.stdout.trim().slice(0, 7) : null; diff --git a/src/commands/skills.ts b/src/commands/skills.ts index 3caa235b..845b6f47 100644 --- a/src/commands/skills.ts +++ b/src/commands/skills.ts @@ -13,8 +13,7 @@ import { spawnSync } from "node:child_process"; import { readFileSync, readdirSync, existsSync } from "node:fs"; import { readFile, writeFile } from "node:fs/promises"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { styleText } from "node:util"; import { parse as parseYaml } from "yaml"; @@ -25,9 +24,8 @@ import { listAllSkillIds } from "../lib/resolver-local"; import { fetchCompanionFiles, readSourceFile, findIncompleteSkills } from "../lib/companion-fetch"; import { gateFreshSkill } from "./security"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); // --------------------------------------------------------------------------- // Skill metadata parsing @@ -338,6 +336,7 @@ async function cmdRemoveFromProfile(id: string): Promise { import * as p from "@clack/prompts"; import { copyFileSync } from "node:fs"; import { homedir } from "node:os"; +import { repoRoot } from "../lib/repo-root"; const INSTALL_DIRS = [ join(homedir(), ".claude", "skills"), diff --git a/src/commands/snapshot.ts b/src/commands/snapshot.ts index c771e82d..460e4b13 100644 --- a/src/commands/snapshot.ts +++ b/src/commands/snapshot.ts @@ -4,13 +4,12 @@ */ import { writeFileSync, readFileSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); export async function run(args: string[]): Promise { const sub = args[0]; @@ -79,7 +78,7 @@ async function cmdRestore(args: string[]): Promise { return 1; } - const profileDir = join(REPO_ROOT, "profiles", snapshot.profile.name); + const profileDir = join(repoRoot(), "profiles", snapshot.profile.name); const { mkdirSync } = require("node:fs"); mkdirSync(profileDir, { recursive: true }); diff --git a/src/commands/sources.ts b/src/commands/sources.ts index b859c6d6..505f4567 100644 --- a/src/commands/sources.ts +++ b/src/commands/sources.ts @@ -9,13 +9,12 @@ import { readFileSync, existsSync, readdirSync } from "node:fs"; import { spawnSync } from "node:child_process"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { loadProfile, listProfiles } from "../lib/profile-loader"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); const SKILLS_LOCK = join(homedir(), "skills-lock.json"); interface LockEntry { @@ -62,7 +61,7 @@ function cmdList(json: boolean): number { } // Local cue skills - const localSkillsRoot = join(REPO_ROOT, "resources", "skills", "skills"); + const localSkillsRoot = join(repoRoot(), "resources", "skills", "skills"); if (existsSync(localSkillsRoot)) { let count = 0; try { diff --git a/src/commands/suggest.ts b/src/commands/suggest.ts index 704919e0..7ec9d89b 100644 --- a/src/commands/suggest.ts +++ b/src/commands/suggest.ts @@ -7,16 +7,15 @@ */ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { loadProfile, } from "../lib/profile-loader"; import { listAllSkillIds } from "../lib/resolver-local"; import { resolveProfileForCwd } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); interface Suggestion { skillId: string; diff --git a/src/commands/update.ts b/src/commands/update.ts index 517e1555..d72cd6de 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -4,10 +4,9 @@ import { spawnSync } from "node:child_process"; import { existsSync, } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); interface UpdateResult { repo: string; @@ -16,7 +15,7 @@ interface UpdateResult { } function gitPull(dir: string): UpdateResult { - const name = dir === REPO_ROOT ? "cue" : dir.split("/").pop()!; + const name = dir === repoRoot() ? "cue" : dir.split("/").pop()!; if (!existsSync(join(dir, ".git"))) { return { repo: name, updated: false, changes: "not a git repo" }; @@ -51,16 +50,16 @@ export async function run(args: string[]): Promise { const repos: string[] = []; - if (!skillsOnly) repos.push(REPO_ROOT); - repos.push(join(REPO_ROOT, "resources", "skills")); - repos.push(join(REPO_ROOT, "resources", "mcps")); + if (!skillsOnly) repos.push(repoRoot()); + repos.push(join(repoRoot(), "resources", "skills")); + repos.push(join(repoRoot(), "resources", "mcps")); if (check) { // Just show what would change process.stdout.write("Checking for updates...\n\n"); for (const dir of repos) { if (!existsSync(join(dir, ".git"))) continue; - const name = dir === REPO_ROOT ? "cue" : dir.split("/").pop()!; + const name = dir === repoRoot() ? "cue" : dir.split("/").pop()!; const res = spawnSync("git", ["log", "HEAD..@{u}", "--oneline"], { cwd: dir, encoding: "utf8" }); spawnSync("git", ["fetch", "--quiet"], { cwd: dir, encoding: "utf8", timeout: 15000 }); const pending = res.stdout.trim(); @@ -91,7 +90,7 @@ export async function run(args: string[]): Promise { // Run bun install if package.json changed if (!skillsOnly && results[0]?.updated) { process.stdout.write("\n Running bun install...\n"); - const bunRes = spawnSync("bun", ["install"], { cwd: REPO_ROOT, encoding: "utf8", timeout: 30000 }); + const bunRes = spawnSync("bun", ["install"], { cwd: repoRoot(), encoding: "utf8", timeout: 30000 }); if (bunRes.status === 0) { process.stdout.write(" ✅ Dependencies updated\n"); } else { diff --git a/src/commands/upgrade.ts b/src/commands/upgrade.ts index c2e35ef4..f8773953 100644 --- a/src/commands/upgrade.ts +++ b/src/commands/upgrade.ts @@ -7,13 +7,12 @@ import { spawnSync } from "node:child_process"; import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, dirname } from "node:path"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const REGISTRY_PATH = join(REPO_ROOT, "docs", "registry", "index.json"); +const REGISTRY_PATH = join(repoRoot(), "docs", "registry", "index.json"); const REGISTRY_URL = "https://opencue.github.io/cue/registry/index.json"; -const UPGRADE_STATE = join(REPO_ROOT, "profiles", "_cache", "last-upgrade.json"); +const UPGRADE_STATE = join(repoRoot(), "profiles", "_cache", "last-upgrade.json"); interface RegistrySkill { id: string; name: string; description: string; diff --git a/src/commands/watch-live.ts b/src/commands/watch-live.ts index dec207b2..ea7119f8 100644 --- a/src/commands/watch-live.ts +++ b/src/commands/watch-live.ts @@ -6,15 +6,14 @@ */ import { watch, existsSync, } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(REPO_ROOT, "profiles"); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const PROFILES_DIR = process.env.CUE_PROFILES_DIR ?? join(repoRoot(), "profiles"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); export async function run(args: string[]): Promise { if (args.includes("-h") || args.includes("--help")) { @@ -84,7 +83,7 @@ Press Ctrl+C to stop. let debounceTimer: ReturnType | null = null; const rematerialize = async (changedPath: string) => { - const rel = changedPath.replace(REPO_ROOT + "/", ""); + const rel = changedPath.replace(repoRoot() + "/", ""); process.stdout.write(`[watch] Detected change in ${rel} → rematerializing...\n`); try { const { materializeRuntime } = await import("../lib/runtime-materializer"); diff --git a/src/commands/why.ts b/src/commands/why.ts index 3a0282e6..9a1920fc 100644 --- a/src/commands/why.ts +++ b/src/commands/why.ts @@ -3,14 +3,13 @@ */ import { readFileSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { loadProfile, listProfiles } from "../lib/profile-loader"; import { resolveActiveProfile } from "../lib/cwd-resolver"; +import { repoRoot } from "../lib/repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PROFILES_DIR = join(REPO_ROOT, "profiles"); +const PROFILES_DIR = join(repoRoot(), "profiles"); export async function run(args: string[]): Promise { const resource = args.find(a => !a.startsWith("-")); @@ -87,7 +86,7 @@ export async function run(args: string[]): Promise { // Disk path for skills if (skillMatch) { - result.diskPath = join(REPO_ROOT, "resources", "skills", "skills", skillMatch.id); + result.diskPath = join(repoRoot(), "resources", "skills", "skills", skillMatch.id); } if (json) { diff --git a/src/index.ts b/src/index.ts index be436b5f..1b3ba7d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,17 +12,15 @@ * 2 internal error (uncaught exception, missing dep) */ import { readFileSync } from "node:fs"; -import { fileURLToPath } from "node:url"; -import { dirname, resolve } from "node:path"; +import { resolve } from "node:path"; import { COMMANDS, type CommandName } from "./commands/_index"; +import { repoRoot } from "./lib/repo-root"; -const HERE = dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(HERE, ".."); function readVersion(): string { try { const pkg = JSON.parse( - readFileSync(resolve(REPO_ROOT, "package.json"), "utf8"), + readFileSync(resolve(repoRoot(), "package.json"), "utf8"), ) as { version?: string }; return pkg.version ?? "0.0.0"; } catch { diff --git a/src/lib/brand-icons.ts b/src/lib/brand-icons.ts index ad97ba2f..eb3dcfae 100644 --- a/src/lib/brand-icons.ts +++ b/src/lib/brand-icons.ts @@ -9,12 +9,11 @@ */ import { existsSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const ICONS_DIR = join(REPO_ROOT, "resources", "icons"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); +const ICONS_DIR = join(repoRoot(), "resources", "icons"); // Known brand → asset file mappings (skill slug or MCP id → relative icon path) const BRAND_ICONS: Record = { @@ -118,12 +117,12 @@ export function getSkillIcon(skillSlug: string): string | null { export function getMcpIcon(mcpId: string): string | null { // 1. Check MCP_ICONS map if (MCP_ICONS[mcpId]) { - const p = join(REPO_ROOT, MCP_ICONS[mcpId]!); + const p = join(repoRoot(), MCP_ICONS[mcpId]!); if (existsSync(p)) return p; } // 2. Check resources/mcps/mcps// for any icon - const mcpDir = join(REPO_ROOT, "resources", "mcps", "mcps", mcpId); + const mcpDir = join(repoRoot(), "resources", "mcps", "mcps", mcpId); if (existsSync(mcpDir)) { try { const { readdirSync } = require("node:fs"); diff --git a/src/lib/cli-extractor.ts b/src/lib/cli-extractor.ts index ff9d5c51..62d3220c 100644 --- a/src/lib/cli-extractor.ts +++ b/src/lib/cli-extractor.ts @@ -10,15 +10,14 @@ */ import { existsSync, readdirSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { homedir } from "node:os"; import { loadProfile } from "./profile-loader"; import { extractCLIsFromSkill } from "../commands/optimizer"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); const HOME_SKILLS = join(homedir(), ".claude", "skills"); export interface CliRequirement { diff --git a/src/lib/colony-dispatch.ts b/src/lib/colony-dispatch.ts index 6c9b8397..2e462323 100644 --- a/src/lib/colony-dispatch.ts +++ b/src/lib/colony-dispatch.ts @@ -3,11 +3,10 @@ */ import { readFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const RULES_PATH = join(REPO_ROOT, "resources", "colony-profiles.yaml"); +const RULES_PATH = join(repoRoot(), "resources", "colony-profiles.yaml"); interface Rule { match: string[]; diff --git a/src/lib/conflict-detector.ts b/src/lib/conflict-detector.ts index 73b3a5a2..4322e8c4 100644 --- a/src/lib/conflict-detector.ts +++ b/src/lib/conflict-detector.ts @@ -4,11 +4,9 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); interface Directive { skillId: string; diff --git a/src/lib/mcp-materializer.ts b/src/lib/mcp-materializer.ts index 64193547..50790c91 100644 --- a/src/lib/mcp-materializer.ts +++ b/src/lib/mcp-materializer.ts @@ -31,10 +31,10 @@ */ import { readFile } from "node:fs/promises"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { ProfileError, type ResolvedProfile } from "../../profiles/_types"; +import { repoRoot } from "./repo-root"; // --------------------------------------------------------------------------- // Errors @@ -72,13 +72,8 @@ export class UnresolvedEnvPlaceholder extends ProfileError { // --------------------------------------------------------------------------- /** Resolve repo root: env override first, else walk up from this file. */ -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve( - dirname(fileURLToPath(import.meta.url)), - "..", - "..", -); -const DEFAULT_CONFIGS_ROOT = join(REPO_ROOT, "resources", "mcps", "configs"); +const DEFAULT_CONFIGS_ROOT = join(repoRoot(), "resources", "mcps", "configs"); export interface MaterializeOptions { /** diff --git a/src/lib/pack-resolver.ts b/src/lib/pack-resolver.ts index a9949b2f..45fae9fc 100644 --- a/src/lib/pack-resolver.ts +++ b/src/lib/pack-resolver.ts @@ -3,11 +3,10 @@ */ import { readFileSync, readdirSync, existsSync } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PACKS_DIR = join(REPO_ROOT, "resources", "skill-packs"); +const PACKS_DIR = join(repoRoot(), "resources", "skill-packs"); export interface SkillPack { name: string; diff --git a/src/lib/profile-generator.ts b/src/lib/profile-generator.ts index 6b15e065..b25f4b68 100644 --- a/src/lib/profile-generator.ts +++ b/src/lib/profile-generator.ts @@ -15,21 +15,15 @@ import { } from "node:fs/promises"; import type { Dirent } from "node:fs"; import { homedir } from "node:os"; -import { - dirname, - join, - relative, - resolve, - sep, -} from "node:path"; +import { dirname, join, relative, resolve, sep } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { parse as parseYaml } from "yaml"; import type { NpxSkillRef, Profile } from "../../profiles/_types"; +import { repoRoot as cueRepoRoot } from "./repo-root"; const HERE = dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(HERE, "..", ".."); export type SkillOrigin = "local" | "npx" | "plugin"; @@ -317,11 +311,11 @@ const STOP_WORDS = new Set([ "with", ]); -export function defaultProfilesDir(repoRoot = REPO_ROOT): string { +export function defaultProfilesDir(repoRoot = cueRepoRoot()): string { return process.env.CUE_PROFILES_DIR ?? process.env.SOUL_PROFILES_DIR ?? join(repoRoot, "profiles"); } -export function defaultSkillsRoot(repoRoot = REPO_ROOT): string { +export function defaultSkillsRoot(repoRoot = cueRepoRoot()): string { return join(repoRoot, "resources", "skills", "skills"); } @@ -332,7 +326,7 @@ export function validateProfileName(name: string): boolean { export async function scanInstalledSkills( options: ScanOptions = {}, ): Promise { - const repoRoot = options.repoRoot ?? REPO_ROOT; + const repoRoot = options.repoRoot ?? cueRepoRoot(); const diagnostics: string[] = []; const skills: DiscoveredSkill[] = []; diff --git a/src/lib/profile-linter.ts b/src/lib/profile-linter.ts index a5859d1f..4f4a52e3 100644 --- a/src/lib/profile-linter.ts +++ b/src/lib/profile-linter.ts @@ -20,8 +20,7 @@ import { mkdir, mkdtemp, readdir, readFile, rm } from "node:fs/promises"; import { existsSync, type Dirent } from "node:fs"; import { tmpdir } from "node:os"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; import { parse as parseYaml } from "yaml"; @@ -45,16 +44,15 @@ import { } from "./resolver-npx"; import { PluginNotInstalled, resolvePlugins } from "./resolver-plugins"; import { parseSkillFromPath } from "./skill-router"; +import { repoRoot as cueRepoRoot } from "./repo-root"; -const HERE = dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(HERE, "..", ".."); -const DEFAULT_PROFILES_DIR = join(REPO_ROOT, "profiles"); -const DEFAULT_SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); -const DEFAULT_CONFIGS_ROOT = join(REPO_ROOT, "resources", "mcps", "configs"); -const DEFAULT_RULES_ROOT = join(REPO_ROOT, "resources", "rules"); -const DEFAULT_COMMANDS_ROOT = join(REPO_ROOT, "resources", "commands"); -const DEFAULT_HOOKS_ROOT = join(REPO_ROOT, "resources", "hooks"); -const DEFAULT_SUBAGENTS_ROOT = join(REPO_ROOT, "resources", "subagents"); +const DEFAULT_PROFILES_DIR = join(cueRepoRoot(), "profiles"); +const DEFAULT_SKILLS_ROOT = join(cueRepoRoot(), "resources", "skills", "skills"); +const DEFAULT_CONFIGS_ROOT = join(cueRepoRoot(), "resources", "mcps", "configs"); +const DEFAULT_RULES_ROOT = join(cueRepoRoot(), "resources", "rules"); +const DEFAULT_COMMANDS_ROOT = join(cueRepoRoot(), "resources", "commands"); +const DEFAULT_HOOKS_ROOT = join(cueRepoRoot(), "resources", "hooks"); +const DEFAULT_SUBAGENTS_ROOT = join(cueRepoRoot(), "resources", "subagents"); export type LintRuleId = | "W1" | "W2" | "W3" | "W4" | "W5" | "W6" | "W7" | "W8" | "W9" @@ -172,7 +170,7 @@ function profilesDir(opts: ProfileLinterOptions): string { } function repoRoot(opts: ProfileLinterOptions): string { - return opts.repoRoot ?? process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? REPO_ROOT; + return opts.repoRoot ?? process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? cueRepoRoot(); } function addIssue( diff --git a/src/lib/profile-metrics.ts b/src/lib/profile-metrics.ts index cef766df..aa5f6646 100644 --- a/src/lib/profile-metrics.ts +++ b/src/lib/profile-metrics.ts @@ -21,12 +21,11 @@ */ import { readFileSync, existsSync, } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { configDir } from "./config-paths"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -export const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +export const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); export function estimateTokens(text: string): number { return Math.ceil(text.length / 4); diff --git a/src/lib/skill-compressor.ts b/src/lib/skill-compressor.ts index a2376081..d71e0d69 100644 --- a/src/lib/skill-compressor.ts +++ b/src/lib/skill-compressor.ts @@ -4,11 +4,9 @@ import { readFileSync, existsSync } from "node:fs"; import { join } from "node:path"; -import { resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); interface SkillSummary { id: string; diff --git a/src/lib/skill-deps.ts b/src/lib/skill-deps.ts index 1256971f..0311e961 100644 --- a/src/lib/skill-deps.ts +++ b/src/lib/skill-deps.ts @@ -6,11 +6,10 @@ */ import { readFileSync, existsSync, } from "node:fs"; -import { join, resolve, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); -const SKILLS_ROOT = join(REPO_ROOT, "resources", "skills", "skills"); +const SKILLS_ROOT = join(repoRoot(), "resources", "skills", "skills"); function skillsRoot(): string { return process.env.CUE_SKILLS_ROOT ?? SKILLS_ROOT; diff --git a/src/lib/skill-quality.ts b/src/lib/skill-quality.ts index 5b49bea9..65a1c92d 100644 --- a/src/lib/skill-quality.ts +++ b/src/lib/skill-quality.ts @@ -3,19 +3,18 @@ */ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot as cueRepoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); function getSkillsRoot(): string { - const root = process.env.CUE_REPO_ROOT ?? REPO_ROOT; + const root = process.env.CUE_REPO_ROOT ?? cueRepoRoot(); return join(root, "resources", "skills", "skills"); } function getRepoRoot(): string { - return process.env.CUE_REPO_ROOT ?? REPO_ROOT; + return process.env.CUE_REPO_ROOT ?? cueRepoRoot(); } interface ScoreBreakdown { diff --git a/src/lib/skill-sandbox.ts b/src/lib/skill-sandbox.ts index 8369ac64..70fc7ef2 100644 --- a/src/lib/skill-sandbox.ts +++ b/src/lib/skill-sandbox.ts @@ -6,13 +6,12 @@ */ import { readFileSync, existsSync } from "node:fs"; -import { resolve, dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { repoRoot } from "./repo-root"; -const REPO_ROOT = process.env.CUE_REPO_ROOT ?? process.env.SOUL_REPO_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); function getSkillsRoot(): string { - const root = process.env.CUE_REPO_ROOT ?? REPO_ROOT; + const root = process.env.CUE_REPO_ROOT ?? repoRoot(); return join(root, "resources", "skills", "skills"); }