@imunitic/synapse 0.0.1-test.1 → 0.1.1

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.
@@ -80,10 +80,43 @@ function readObsidianPluginData() {
80
80
  return data;
81
81
  }
82
82
 
83
+ // The manifest recording exactly which names this tool wrote into a given
84
+ // destination on the prior run -- diffed against the current run's names
85
+ // so a renamed or removed skill/command is deleted instead of left behind
86
+ // as an orphan forever. Scoped strictly to names a previous manifest
87
+ // actually listed: anything else living in the same directory (a skill
88
+ // from a different source, or the user's own) was never in that manifest
89
+ // and is never touched. Lives inside `destRoot` itself, as a dotfile --
90
+ // no `.md` extension and not a `{name}/SKILL.md` subdirectory, so no
91
+ // harness's own skill/command discovery mistakes it for one of ours.
92
+ const MANAGED_MANIFEST = ".synapse-managed.json";
93
+
94
+ function pruneStale(destRoot, currentNames) {
95
+ const manifestPath = path.join(destRoot, MANAGED_MANIFEST);
96
+ let previous = [];
97
+ if (fs.existsSync(manifestPath)) {
98
+ try {
99
+ previous = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
100
+ } catch {
101
+ previous = [];
102
+ }
103
+ }
104
+ const current = new Set(currentNames);
105
+ for (const name of previous) {
106
+ if (current.has(name)) continue;
107
+ fs.rmSync(path.join(destRoot, name), { recursive: true, force: true });
108
+ }
109
+ fs.mkdirSync(destRoot, { recursive: true });
110
+ fs.writeFileSync(manifestPath, JSON.stringify(currentNames, null, 2) + "\n");
111
+ }
112
+
83
113
  // Copies every shared SKILL.md into `destRoot/{name}/SKILL.md`, verbatim
84
114
  // unless `transform` is given. Each skill gets its own uniquely-named
85
115
  // subdirectory, so overwriting ours on every run never touches anything a
86
116
  // harness's own skill discovery also finds there under a different name.
117
+ // Returns the names written -- pruning stale ones is the caller's job,
118
+ // since a destRoot shared with another source (Codex's own skills, on top
119
+ // of these) needs the combined set before it can prune safely.
87
120
  function copySkills(destRoot, transform) {
88
121
  const src = path.join(PKG_ROOT, "skills");
89
122
  const names = fs
@@ -96,7 +129,7 @@ function copySkills(destRoot, transform) {
96
129
  fs.mkdirSync(destDir, { recursive: true });
97
130
  writeMaybeTransformed(path.join(src, name, "SKILL.md"), path.join(destDir, "SKILL.md"), transform);
98
131
  }
99
- return names.length;
132
+ return names;
100
133
  }
101
134
 
102
135
  function copyCommands(destRoot, transform) {
@@ -106,7 +139,7 @@ function copyCommands(destRoot, transform) {
106
139
  for (const name of names) {
107
140
  writeMaybeTransformed(path.join(src, name), path.join(destRoot, name), transform);
108
141
  }
109
- return names.length;
142
+ return names;
110
143
  }
111
144
 
112
145
  function writeMaybeTransformed(srcPath, destPath, transform) {
@@ -177,9 +210,13 @@ function mergeHooksInto(existingHooks, rendered, hookBin) {
177
210
  function configureClaude() {
178
211
  const hookBin = resolveHookBin();
179
212
 
180
- const skillCount = copySkills(path.join(os.homedir(), ".claude", "skills"));
181
- const commandCount = copyCommands(path.join(os.homedir(), ".claude", "commands"));
182
- console.log(`installed ${skillCount} skills, ${commandCount} commands to ~/.claude`);
213
+ const skillsDest = path.join(os.homedir(), ".claude", "skills");
214
+ const skillNames = copySkills(skillsDest);
215
+ pruneStale(skillsDest, skillNames);
216
+ const commandsDest = path.join(os.homedir(), ".claude", "commands");
217
+ const commandNames = copyCommands(commandsDest);
218
+ pruneStale(commandsDest, commandNames);
219
+ console.log(`installed ${skillNames.length} skills, ${commandNames.length} commands to ~/.claude`);
183
220
 
184
221
  const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
185
222
  let settings = {};
@@ -283,7 +320,7 @@ function configureCodex() {
283
320
  console.log(`configured ${hooksPath}`);
284
321
 
285
322
  const skillsDestRoot = path.join(os.homedir(), ".codex", "skills");
286
- const sharedCount = copySkills(skillsDestRoot);
323
+ const sharedNames = copySkills(skillsDestRoot);
287
324
  const codexSkillsSrc = path.join(PKG_ROOT, "harness", "codex", "skills");
288
325
  const codexNames = fs
289
326
  .readdirSync(codexSkillsSrc, { withFileTypes: true })
@@ -293,7 +330,8 @@ function configureCodex() {
293
330
  for (const name of codexNames) {
294
331
  fs.cpSync(path.join(codexSkillsSrc, name), path.join(skillsDestRoot, name), { recursive: true, force: true });
295
332
  }
296
- console.log(`installed ${sharedCount + codexNames.length} skills to ${skillsDestRoot}`);
333
+ pruneStale(skillsDestRoot, [...sharedNames, ...codexNames]);
334
+ console.log(`installed ${sharedNames.length + codexNames.length} skills to ${skillsDestRoot}`);
297
335
 
298
336
  const pluginData = readObsidianPluginData();
299
337
  if (!pluginData.enableInsecureServer || !pluginData.insecurePort) {
@@ -365,12 +403,13 @@ function configureOpencode() {
365
403
  fs.writeFileSync(pluginDest, pluginSource);
366
404
  console.log(`installed ${pluginDest}`);
367
405
 
368
- const skillCount = copySkills(path.join(os.homedir(), ".config", "opencode", "skill"), opencodeToolNameTransform);
369
- const commandCount = copyCommands(
370
- path.join(os.homedir(), ".config", "opencode", "command"),
371
- opencodeToolNameTransform
372
- );
373
- console.log(`installed ${skillCount} skills, ${commandCount} commands to ~/.config/opencode`);
406
+ const skillsDest = path.join(os.homedir(), ".config", "opencode", "skill");
407
+ const skillNames = copySkills(skillsDest, opencodeToolNameTransform);
408
+ pruneStale(skillsDest, skillNames);
409
+ const commandsDest = path.join(os.homedir(), ".config", "opencode", "command");
410
+ const commandNames = copyCommands(commandsDest, opencodeToolNameTransform);
411
+ pruneStale(commandsDest, commandNames);
412
+ console.log(`installed ${skillNames.length} skills, ${commandNames.length} commands to ~/.config/opencode`);
374
413
 
375
414
  const pluginData = readObsidianPluginData();
376
415
  if (!pluginData.enableInsecureServer || !pluginData.insecurePort) {
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@imunitic/synapse",
3
- "version": "0.0.1-test.1",
3
+ "version": "0.1.1",
4
4
  "description": "Memory for Claude Code, Codex CLI, and OpenCode: a durable Obsidian vault plus a per-repo code graph.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/imunitic/synapse"
8
+ },
5
9
  "bin": {
6
10
  "synapse-setup": "bin/synapse-setup.cjs",
7
11
  "synapse": "bin/synapse.cjs",
@@ -18,9 +22,9 @@
18
22
  "*.conf.template"
19
23
  ],
20
24
  "optionalDependencies": {
21
- "@imunitic/synapse-darwin-arm64": "0.0.1-test.0",
22
- "@imunitic/synapse-linux-x64": "0.0.1-test.0",
23
- "@imunitic/synapse-linux-arm64": "0.0.1-test.0"
25
+ "@imunitic/synapse-darwin-arm64": "0.1.1",
26
+ "@imunitic/synapse-linux-x64": "0.1.1",
27
+ "@imunitic/synapse-linux-arm64": "0.1.1"
24
28
  },
25
29
  "license": "SEE LICENSE IN LICENSE"
26
30
  }