@kkmila/cpc 1.0.0

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.
@@ -0,0 +1,36 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import yaml from "js-yaml";
4
+ import { expandHome } from "./fs.mjs";
5
+
6
+ /**
7
+ * Load and parse registry.yaml.
8
+ */
9
+ export function loadRegistry(repoRoot) {
10
+ const raw = readFileSync(resolve(repoRoot, "registry.yaml"), "utf8");
11
+ return yaml.load(raw);
12
+ }
13
+
14
+ /**
15
+ * Get all agent definitions from registry.
16
+ */
17
+ export function getAgents(registry) {
18
+ return registry.agents || {};
19
+ }
20
+
21
+ /**
22
+ * Get a single agent definition.
23
+ */
24
+ export function getAgent(registry, agentId) {
25
+ return registry.agents?.[agentId] || null;
26
+ }
27
+
28
+ /**
29
+ * Resolve ~ paths in agent definition.
30
+ */
31
+ export function resolveAgentPaths(agent) {
32
+ return {
33
+ ...agent,
34
+ home: expandHome(agent.home),
35
+ };
36
+ }
package/src/lib/fs.mjs ADDED
@@ -0,0 +1,75 @@
1
+ import { homedir } from "node:os";
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readlinkSync,
6
+ lstatSync,
7
+ unlinkSync,
8
+ symlinkSync,
9
+ writeFileSync,
10
+ readFileSync,
11
+ readdirSync,
12
+ statSync,
13
+ } from "node:fs";
14
+ import { join } from "node:path";
15
+
16
+ /**
17
+ * Expand ~ to the user's home directory.
18
+ */
19
+ export function expandHome(p) {
20
+ if (!p) return p;
21
+ return p.replace(/^~/, homedir());
22
+ }
23
+
24
+ /**
25
+ * Ensure a directory exists (mkdir -p).
26
+ */
27
+ export function ensureDir(dir) {
28
+ if (!existsSync(dir)) {
29
+ mkdirSync(dir, { recursive: true });
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Check if a path exists as a symlink (even if target is broken).
35
+ * Unlike existsSync, this does NOT follow the symlink.
36
+ */
37
+ function symlinkExists(p) {
38
+ try {
39
+ return lstatSync(p).isSymbolicLink();
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Create a symlink. Returns: 'already-linked' | 'replaced' | 'conflict' | 'created'
47
+ */
48
+ export function ensureSymlink(src, dst) {
49
+ if (symlinkExists(dst)) {
50
+ const current = readlinkSync(dst);
51
+ if (current === src) return "already-linked";
52
+ // Stale or different target — replace
53
+ unlinkSync(dst);
54
+ symlinkSync(src, dst);
55
+ return "replaced";
56
+ }
57
+ if (existsSync(dst)) {
58
+ // Real file/dir exists — refuse to overwrite
59
+ return "conflict";
60
+ }
61
+ symlinkSync(src, dst);
62
+ return "created";
63
+ }
64
+
65
+ /**
66
+ * Remove a symlink. Returns: 'removed' | 'not-found' | 'not-symlink'
67
+ */
68
+ export function removeSymlink(dst) {
69
+ if (!symlinkExists(dst)) {
70
+ if (existsSync(dst)) return "not-symlink";
71
+ return "not-found";
72
+ }
73
+ unlinkSync(dst);
74
+ return "removed";
75
+ }
@@ -0,0 +1,13 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ /**
6
+ * Resolve the custom-provider-cli repo root from the CLI script location.
7
+ * Handles symlinked installations (npm link, /usr/local/bin, ~/.local/bin).
8
+ */
9
+ export function resolveRepoRoot() {
10
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
11
+ // cli/src/lib/ → repo root is 3 levels up (lib → src → cli → root)
12
+ return resolve(scriptDir, "..", "..", "..");
13
+ }