forget-connect 0.5.1 → 0.6.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.
package/src/skills.js ADDED
@@ -0,0 +1,95 @@
1
+ import { readFile, rmdir, unlink } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { atomicWrite } from "./core.js";
7
+
8
+ export const MEMORY_AGENT_SKILL = "memory-agent";
9
+ export const SKILL_MARKER = "forget-connect:skill";
10
+ const SKILL_FILES = ["SKILL.md", path.join("agents", "openai.yaml")];
11
+
12
+ function assetRoot() {
13
+ return fileURLToPath(new URL(`../assets/skills/${MEMORY_AGENT_SKILL}/`, import.meta.url));
14
+ }
15
+
16
+ function homeFor(env = process.env) {
17
+ return env.HOME ?? env.USERPROFILE ?? os.homedir();
18
+ }
19
+
20
+ export function skillDirFor(clientId, { env = process.env } = {}) {
21
+ const home = homeFor(env);
22
+ if (clientId === "codex") {
23
+ const codexHome = env.CODEX_HOME ? path.resolve(env.CODEX_HOME) : path.join(home, ".codex");
24
+ return path.join(codexHome, "skills", MEMORY_AGENT_SKILL);
25
+ }
26
+ if (clientId === "claude-code") {
27
+ return path.join(home, ".claude", "skills", MEMORY_AGENT_SKILL);
28
+ }
29
+ return "";
30
+ }
31
+
32
+ async function readOptional(filePath) {
33
+ try {
34
+ return await readFile(filePath, "utf8");
35
+ } catch (error) {
36
+ if (error?.code === "ENOENT") return null;
37
+ throw error;
38
+ }
39
+ }
40
+
41
+ async function ownedSkill(targetDir) {
42
+ const entry = await readOptional(path.join(targetDir, "SKILL.md"));
43
+ return entry === null || entry.includes(SKILL_MARKER);
44
+ }
45
+
46
+ export async function installMemoryAgentSkill(clientId, { env = process.env } = {}) {
47
+ const targetDir = skillDirFor(clientId, { env });
48
+ if (!targetDir) return { written: [], skipped: [] };
49
+ if (!(await ownedSkill(targetDir))) {
50
+ return { written: [], skipped: [targetDir] };
51
+ }
52
+ const written = [];
53
+ for (const relative of SKILL_FILES) {
54
+ const source = await readFile(path.join(assetRoot(), relative), "utf8");
55
+ const target = path.join(targetDir, relative);
56
+ const existing = await readOptional(target);
57
+ if (existing === source) continue;
58
+ await atomicWrite(target, source, 0o644);
59
+ written.push(target);
60
+ }
61
+ return { written, skipped: [] };
62
+ }
63
+
64
+ export async function removeMemoryAgentSkill(clientId, { env = process.env } = {}) {
65
+ const targetDir = skillDirFor(clientId, { env });
66
+ if (!targetDir || !(await ownedSkill(targetDir))) return { removed: [], kept: targetDir ? [targetDir] : [] };
67
+ const removed = [];
68
+ for (const relative of [...SKILL_FILES].reverse()) {
69
+ const target = path.join(targetDir, relative);
70
+ try {
71
+ await unlink(target);
72
+ removed.push(target);
73
+ } catch (error) {
74
+ if (error?.code !== "ENOENT") throw error;
75
+ }
76
+ }
77
+ await rmdir(path.join(targetDir, "agents")).catch((error) => {
78
+ if (!error || !["ENOENT", "ENOTEMPTY"].includes(error.code)) throw error;
79
+ });
80
+ await rmdir(targetDir).catch((error) => {
81
+ if (!error || !["ENOENT", "ENOTEMPTY"].includes(error.code)) throw error;
82
+ });
83
+ return { removed, kept: [] };
84
+ }
85
+
86
+ export async function inspectMemoryAgentSkill(clientId, { env = process.env } = {}) {
87
+ const targetDir = skillDirFor(clientId, { env });
88
+ if (!targetDir) return { installed: false, current: false, path: "" };
89
+ const installed = await readOptional(path.join(targetDir, "SKILL.md"));
90
+ if (installed === null || !installed.includes(SKILL_MARKER)) {
91
+ return { installed: false, current: false, path: targetDir };
92
+ }
93
+ const expected = await readFile(path.join(assetRoot(), "SKILL.md"), "utf8");
94
+ return { installed: true, current: installed === expected, path: targetDir };
95
+ }