volute 0.31.0 → 0.33.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.
Files changed (195) hide show
  1. package/README.md +31 -22
  2. package/dist/{accept-GAKQ3MEH.js → accept-D5VBM7JW.js} +5 -4
  3. package/dist/{activity-events-T5ZRCVAL.js → activity-events-XJO3P4RR.js} +3 -2
  4. package/dist/{ai-service-UWUPM4T6.js → ai-service-SBY2WG7O.js} +18 -5
  5. package/dist/api.d.ts +703 -1068
  6. package/dist/{archive-YBNSJYZZ.js → archive-INXYFVCW.js} +3 -2
  7. package/dist/{auth-T5AW2USD.js → auth-GKCDSO4T.js} +4 -3
  8. package/dist/{bridge-4AJ3EY26.js → bridge-TXWWPPOJ.js} +5 -4
  9. package/dist/{chat-7YLT7FI3.js → chat-U5ZOME3O.js} +8 -8
  10. package/dist/{chunk-NV3TYNWX.js → chunk-2NGTS5UU.js} +1 -1
  11. package/dist/{chunk-BWKIHH7B.js → chunk-3Z2DPESO.js} +662 -508
  12. package/dist/chunk-6LXAAQ43.js +22 -0
  13. package/dist/chunk-7J3HEVR7.js +220 -0
  14. package/dist/{chunk-NOWVQ7AL.js → chunk-A2A4KLFE.js} +351 -301
  15. package/dist/{chunk-LX6T3GKQ.js → chunk-ALEF47VT.js} +1 -1
  16. package/dist/{chunk-S2TZLSDH.js → chunk-C7I35G4R.js} +163 -15
  17. package/dist/{chunk-VGWJSNHS.js → chunk-G53F3JA4.js} +1 -35
  18. package/dist/{chunk-A6TUJJ3L.js → chunk-G6BSYHPK.js} +2 -2
  19. package/dist/{chunk-DAXJKPHZ.js → chunk-GY5HBI7A.js} +2 -2
  20. package/dist/{chunk-BC3P3QCK.js → chunk-I5KY25PQ.js} +1 -9
  21. package/dist/{chunk-BNC43CSY.js → chunk-JUKK7FPS.js} +2 -2
  22. package/dist/{chunk-R5QJBZZG.js → chunk-JYVGHWEJ.js} +21 -11
  23. package/dist/chunk-KIEPMIM5.js +59 -0
  24. package/dist/{chunk-EKDWA7E4.js → chunk-KVK2DLWI.js} +5 -2
  25. package/dist/{chunk-AAO77TZX.js → chunk-LOEJ4HPQ.js} +1 -1
  26. package/dist/chunk-LRCG2JLP.js +251 -0
  27. package/dist/{chunk-EMPFLFTG.js → chunk-M7UL5S3Q.js} +1 -1
  28. package/dist/{chunk-6QIUN46C.js → chunk-N432I7QH.js} +20 -3
  29. package/dist/{chunk-SNVPRRT7.js → chunk-NNB4WIG7.js} +2 -2
  30. package/dist/{chunk-HDKY4TWU.js → chunk-NPKSDYA2.js} +3 -3
  31. package/dist/chunk-OYAKCAVY.js +29 -0
  32. package/dist/chunk-PB65JZK2.js +85 -0
  33. package/dist/chunk-PVY5W6QN.js +41 -0
  34. package/dist/{chunk-PNQCXLSV.js → chunk-QTUVYI7W.js} +58 -1
  35. package/dist/{chunk-X62AXPR7.js → chunk-RPZZSXV3.js} +8 -196
  36. package/dist/{chunk-WRS3B556.js → chunk-RSX4OPZY.js} +5 -5
  37. package/dist/{chunk-FAHDKPEH.js → chunk-RVGLDGMI.js} +5 -3
  38. package/dist/chunk-SKLSMHXO.js +208 -0
  39. package/dist/{chunk-4OUOFS23.js → chunk-UKVWJRKN.js} +1 -1
  40. package/dist/{chunk-57OKQMP3.js → chunk-VH33ZWMW.js} +5 -55
  41. package/dist/cli.js +49 -23
  42. package/dist/{clock-LJCG426D.js → clock-BVH3V6E3.js} +7 -6
  43. package/dist/{cloud-sync-O3LXIRN6.js → cloud-sync-4NWLMFVH.js} +20 -14
  44. package/dist/config-H2H4UIF7.js +72 -0
  45. package/dist/connectors/discord-bridge.js +1 -1
  46. package/dist/connectors/slack-bridge.js +1 -1
  47. package/dist/connectors/telegram-bridge.js +1 -1
  48. package/dist/{conversations-RKKGP5IA.js → conversations-AWI5SZW2.js} +4 -3
  49. package/dist/{create-TL623TFC.js → create-2FK7Z46Y.js} +6 -2
  50. package/dist/{create-WUTIIRI2.js → create-YWD2TIP4.js} +6 -5
  51. package/dist/{daemon-client-CVGM25DM.js → daemon-client-6QXHZ7US.js} +3 -2
  52. package/dist/{daemon-restart-EZP7XH3V.js → daemon-restart-GOBUKLX7.js} +8 -6
  53. package/dist/daemon.js +1918 -1472
  54. package/dist/{db-SW5PL6QA.js → db-F34YLV7D.js} +2 -1
  55. package/dist/db-RA45JBFG.js +16 -0
  56. package/dist/{delete-Z6HAG35F.js → delete-QTGWEDBI.js} +1 -1
  57. package/dist/delivery-manager-PFAKEJTC.js +32 -0
  58. package/dist/delivery-router-FL45JL7N.js +21 -0
  59. package/dist/down-FWWTEKXM.js +15 -0
  60. package/dist/{env-NHESNNSP.js → env-JCOF2222.js} +5 -4
  61. package/dist/{export-EVMP7GWY.js → export-SUYRLI5Q.js} +4 -3
  62. package/dist/{extension-LR7EW3JF.js → extension-OBTGKQQD.js} +5 -3
  63. package/dist/{extensions-NGEJI7JH.js → extensions-KYNTVTMO.js} +10 -7
  64. package/dist/{files-3SM7V33S.js → files-65PMW5IK.js} +6 -5
  65. package/dist/{history-PQD3LXEP.js → history-DKCDI3JO.js} +9 -4
  66. package/dist/{import-PR2OCGQJ.js → import-DDUFE7AY.js} +4 -3
  67. package/dist/isolation-LLAYQYDY.js +22 -0
  68. package/dist/{join-R4EN5CWQ.js → join-I5QEE3LG.js} +1 -1
  69. package/dist/{list-B4XNUOFO.js → list-JQ463EDA.js} +5 -4
  70. package/dist/{login-62JVY6A2.js → login-D7ETSU4R.js} +5 -4
  71. package/dist/{login-URWP6S2N.js → login-RIJF2F4G.js} +3 -2
  72. package/dist/{logout-NXJQJDLI.js → logout-5MLHZALK.js} +3 -2
  73. package/dist/{logout-ZK2N62T3.js → logout-UZJRGY4Z.js} +3 -2
  74. package/dist/message-delivery-DFF5SJRM.js +42 -0
  75. package/dist/{mind-E2ZV2WRX.js → mind-IOJFLEM5.js} +25 -19
  76. package/dist/{mind-activity-tracker-ASNZBMLC.js → mind-activity-tracker-F6O4Q2SL.js} +4 -3
  77. package/dist/{mind-list-BEI7E5WY.js → mind-list-WUPMQDYQ.js} +3 -2
  78. package/dist/mind-manager-NBJF5D26.js +32 -0
  79. package/dist/mind-profile-P67FEHOY.js +47 -0
  80. package/dist/mind-service-2MQ6UK5N.js +38 -0
  81. package/dist/{mind-sleep-CANABWJI.js → mind-sleep-WW2IX7JT.js} +5 -4
  82. package/dist/{mind-status-6WKZVUOP.js → mind-status-L3EFFRPR.js} +3 -2
  83. package/dist/{mind-wake-RZKLH2IN.js → mind-wake-VSSGW465.js} +5 -4
  84. package/dist/{package-NU4CA7OU.js → package-U3VFO273.js} +2 -1
  85. package/dist/{read-THL362EI.js → read-EBY56C33.js} +5 -4
  86. package/dist/read-stdin-HQJ7774D.js +8 -0
  87. package/dist/{register-QAQELAS6.js → register-HD74C4TT.js} +5 -4
  88. package/dist/{registry-ASXCQCNH.js → registry-PJ4S5PHQ.js} +8 -1
  89. package/dist/{reject-AYPBNPNL.js → reject-UJKFBHRO.js} +5 -4
  90. package/dist/{restart-6SKPV3T2.js → restart-3UCMRUVC.js} +3 -2
  91. package/dist/{sandbox-6ZEWQDVU.js → sandbox-GJOK4QLQ.js} +4 -3
  92. package/dist/scheduler-ZZ7XGQG6.js +32 -0
  93. package/dist/schema-PA3M5ZKH.js +32 -0
  94. package/dist/seed-QDYVLG74.js +11 -0
  95. package/dist/seed-check-S2IX25RL.js +32 -0
  96. package/dist/seed-cmd-DKOUFEAU.js +36 -0
  97. package/dist/{seed-OWX2AW75.js → seed-create-4XBBOLRH.js} +27 -10
  98. package/dist/{sprout-FDVI2CGN.js → seed-sprout-GQEIIQRT.js} +24 -9
  99. package/dist/{send-ZO4BTWXK.js → send-QIV2INHB.js} +92 -101
  100. package/dist/{setup-7CFITEQN.js → setup-TISPCO22.js} +7 -2
  101. package/dist/{setup-ZXBXG7E4.js → setup-XMCBE3LF.js} +11 -7
  102. package/dist/{skill-YFXP67A2.js → skill-PSQGRRJX.js} +5 -4
  103. package/dist/skills/dreaming/SKILL.md +6 -4
  104. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  105. package/dist/skills/dreaming/scripts/dream.ts +2 -2
  106. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +1 -1
  107. package/dist/skills/imagegen/SKILL.md +16 -11
  108. package/dist/skills/imagegen/references/INSTALL.md +1 -1
  109. package/dist/skills/imagegen/scripts/imagegen.ts +146 -25
  110. package/dist/skills/orientation/SKILL.md +9 -2
  111. package/dist/skills/resonance/SKILL.md +4 -1
  112. package/dist/skills/resonance/references/INSTALL.md +2 -2
  113. package/dist/skills/resonance/scripts/resonance-hook.sh +2 -0
  114. package/dist/skills/resonance/scripts/resonance.ts +35 -5
  115. package/dist/skills/seed-nurture/SKILL.md +42 -0
  116. package/dist/skills/volute-admin/SKILL.md +83 -0
  117. package/dist/skills/volute-mind/SKILL.md +15 -11
  118. package/dist/skills-7FV7EJTE.js +62 -0
  119. package/dist/sleep-manager-JTXSN7NV.js +36 -0
  120. package/dist/spirit-VRONKFMF.js +23 -0
  121. package/dist/{split-MI62KJUU.js → split-STOROBYJ.js} +1 -1
  122. package/dist/sprout-WKLZXUIQ.js +11 -0
  123. package/dist/{start-D64BRKPH.js → start-K2NCUUCG.js} +3 -2
  124. package/dist/{status-ZZWBYFGE.js → status-3JBTFSMI.js} +6 -4
  125. package/dist/{stop-OP2CTXCO.js → stop-H26JZDXF.js} +3 -2
  126. package/dist/system-chat-JAPOJ3KE.js +36 -0
  127. package/dist/{systems-EQPPT4B7.js → systems-XRI52VCH.js} +6 -5
  128. package/dist/{tailscale-6DJKUMNF.js → tailscale-XHQBZROW.js} +2 -1
  129. package/dist/{template-hash-3HOR4UAJ.js → template-hash-A6VVKOXJ.js} +2 -1
  130. package/dist/up-M5AS6SBV.js +18 -0
  131. package/dist/{update-KUJXATRS.js → update-UD543CXX.js} +6 -4
  132. package/dist/{update-check-5WVSU37T.js → update-check-ZD6OOIYQ.js} +3 -2
  133. package/dist/{upgrade-KBHCWX6T.js → upgrade-O4Q7WJM3.js} +12 -14
  134. package/dist/{version-notify-75ELVKPV.js → version-notify-NBI2MTJO.js} +22 -16
  135. package/dist/volute-config-HD7WWUQC.js +10 -0
  136. package/dist/web-assets/assets/index-CWJrVveV.css +1 -0
  137. package/dist/web-assets/assets/index-DJt14FRI.js +75 -0
  138. package/dist/web-assets/ext-theme.css +93 -0
  139. package/dist/web-assets/index.html +2 -2
  140. package/drizzle/0004_spirits.sql +5 -0
  141. package/drizzle/meta/0004_snapshot.json +7 -0
  142. package/drizzle/meta/_journal.json +7 -0
  143. package/package.json +2 -1
  144. package/packages/extensions/notes/dist/ui/assets/index-8jWEv9SA.js +61 -0
  145. package/packages/extensions/notes/dist/ui/assets/index-DkaB7Ytd.css +1 -0
  146. package/packages/extensions/notes/dist/ui/index.html +2 -2
  147. package/packages/extensions/pages/skills/pages/SKILL.md +16 -46
  148. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +40 -0
  149. package/templates/_base/.init/{.config → .local}/bin/volute +1 -1
  150. package/templates/_base/.init/.local/hooks/pre-prompt/session-activity.ts +40 -0
  151. package/templates/_base/.init/.local/hooks/startup-context.ts +58 -0
  152. package/templates/_base/home/.config/routes.json +1 -1
  153. package/templates/_base/src/lib/daemon-client.ts +21 -13
  154. package/templates/_base/src/lib/format-prefix.ts +1 -0
  155. package/templates/_base/src/lib/hook-loader.ts +155 -0
  156. package/templates/_base/src/lib/startup.ts +11 -4
  157. package/templates/_base/src/lib/transparency.ts +2 -2
  158. package/templates/claude/.init/.claude/settings.json +1 -1
  159. package/templates/claude/.init/.config/routes.json +2 -2
  160. package/templates/claude/src/agent.ts +95 -13
  161. package/templates/claude/src/lib/message-channel.ts +7 -2
  162. package/templates/claude/src/lib/stream-consumer.ts +38 -0
  163. package/templates/codex/.init/.config/routes.json +11 -0
  164. package/templates/codex/.init/AGENTS.md +29 -0
  165. package/templates/codex/home/.config/config.json.tmpl +7 -0
  166. package/templates/codex/package.json.tmpl +20 -0
  167. package/templates/codex/src/agent.ts +554 -0
  168. package/templates/codex/src/lib/content.ts +16 -0
  169. package/templates/codex/src/lib/session-store.ts +56 -0
  170. package/templates/codex/src/server.ts +59 -0
  171. package/templates/codex/volute-template.json +8 -0
  172. package/templates/pi/.init/.config/routes.json +2 -2
  173. package/templates/pi/src/agent.ts +62 -8
  174. package/templates/pi/src/lib/event-handler.ts +1 -1
  175. package/templates/pi/src/lib/reply-instructions-extension.ts +32 -11
  176. package/dist/chunk-HR5JKIDG.js +0 -222
  177. package/dist/down-TS4XQBA4.js +0 -13
  178. package/dist/message-delivery-UJHCLVU4.js +0 -30
  179. package/dist/mind-manager-IPA6DZXD.js +0 -26
  180. package/dist/pages-watcher-72OVPRMH.js +0 -22
  181. package/dist/skills/sessions/SKILL.md +0 -49
  182. package/dist/sleep-manager-TPS6OGCA.js +0 -30
  183. package/dist/system-chat-B43GIXQU.js +0 -30
  184. package/dist/up-TDXEP3VA.js +0 -16
  185. package/dist/web-assets/assets/index-BM1cTzBg.js +0 -72
  186. package/dist/web-assets/assets/index-BfJkKTPF.css +0 -1
  187. package/packages/extensions/notes/dist/ui/assets/index-B8GdTnXs.css +0 -1
  188. package/packages/extensions/notes/dist/ui/assets/index-CDpGTCWb.js +0 -2
  189. package/packages/extensions/pages/skills/pages/scripts/pages.mjs +0 -58
  190. package/templates/_base/.init/.config/hooks/startup-context.sh +0 -46
  191. package/templates/_base/.init/.config/scripts/session-reader.ts +0 -59
  192. package/templates/_base/src/lib/session-monitor.ts +0 -400
  193. package/templates/claude/src/lib/hooks/session-context.ts +0 -32
  194. package/templates/pi/src/lib/session-context-extension.ts +0 -35
  195. /package/templates/_base/.init/{.config → .local}/hooks/wake-context.sh +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  stateDir
4
- } from "./chunk-X62AXPR7.js";
4
+ } from "./chunk-LRCG2JLP.js";
5
5
 
6
6
  // src/lib/file-sharing.ts
7
7
  import { randomBytes } from "crypto";
@@ -5,16 +5,18 @@ import {
5
5
  import {
6
6
  exec,
7
7
  gitExec
8
- } from "./chunk-57OKQMP3.js";
8
+ } from "./chunk-KIEPMIM5.js";
9
9
  import {
10
10
  readGlobalConfig,
11
11
  writeGlobalConfig
12
- } from "./chunk-6QIUN46C.js";
12
+ } from "./chunk-N432I7QH.js";
13
13
  import {
14
14
  getDb,
15
- sharedSkills,
16
15
  voluteHome
17
- } from "./chunk-X62AXPR7.js";
16
+ } from "./chunk-LRCG2JLP.js";
17
+ import {
18
+ sharedSkills
19
+ } from "./chunk-RPZZSXV3.js";
18
20
 
19
21
  // src/lib/skills.ts
20
22
  import { createHash } from "crypto";
@@ -32,7 +34,7 @@ import { basename, dirname, join, resolve } from "path";
32
34
  import { eq, sql } from "drizzle-orm";
33
35
  var VALID_SKILL_ID = /^[a-zA-Z0-9_-]+$/;
34
36
  var SEED_SKILLS = ["orientation", "memory"];
35
- var STANDARD_SKILLS = ["volute-mind", "memory", "sessions", "dreaming", "shared-files"];
37
+ var STANDARD_SKILLS = ["volute-mind", "memory", "dreaming", "shared-files"];
36
38
  function getStandardSkillsWithExtensions() {
37
39
  const config = readGlobalConfig();
38
40
  if (config.defaultSkills) return [...config.defaultSkills];
@@ -42,7 +44,7 @@ async function initDefaultSkills() {
42
44
  const config = readGlobalConfig();
43
45
  let extensionSkills = [];
44
46
  try {
45
- const { getExtensionStandardSkills } = await import("./extensions-NGEJI7JH.js");
47
+ const { getExtensionStandardSkills } = await import("./extensions-KYNTVTMO.js");
46
48
  extensionSkills = getExtensionStandardSkills();
47
49
  } catch (err) {
48
50
  logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
@@ -66,15 +68,43 @@ function sharedSkillsDir() {
66
68
  }
67
69
  function parseSkillMd(content) {
68
70
  const match = content.match(/^---\n([\s\S]*?)\n---/);
69
- if (!match) return { name: "", description: "", npmDependencies: [] };
71
+ if (!match) return { name: "", description: "", npmDependencies: [], hooks: {}, bin: null };
70
72
  const frontmatter = match[1];
71
73
  const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
72
74
  const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
73
75
  const depsMatch = frontmatter.match(/^\s*npm-dependencies:\s*(.+)$/m);
76
+ const binMatch = frontmatter.match(/^\s*bin:\s*(.+)$/m);
77
+ const hooks = {};
78
+ const lines = frontmatter.split("\n");
79
+ let inHooks = false;
80
+ let hooksIndent = -1;
81
+ for (const line of lines) {
82
+ if (!line.trim()) continue;
83
+ const hooksStart = line.match(/^(\s+)hooks:\s*$/);
84
+ if (hooksStart) {
85
+ inHooks = true;
86
+ hooksIndent = hooksStart[1].length;
87
+ continue;
88
+ }
89
+ if (inHooks) {
90
+ const lineIndentMatch = line.match(/^(\s*)/);
91
+ const lineIndent = lineIndentMatch ? lineIndentMatch[1].length : 0;
92
+ if (lineIndent <= hooksIndent) {
93
+ inHooks = false;
94
+ continue;
95
+ }
96
+ const hookMatch = line.match(/^\s+([a-z0-9-]+):\s*(.+)$/);
97
+ if (hookMatch) {
98
+ hooks[hookMatch[1]] = hookMatch[2].trim();
99
+ }
100
+ }
101
+ }
74
102
  return {
75
103
  name: nameMatch?.[1].trim() ?? "",
76
104
  description: descMatch?.[1].trim() ?? "",
77
- npmDependencies: depsMatch ? depsMatch[1].trim().split(/[\s,]+/).filter(Boolean) : []
105
+ npmDependencies: depsMatch ? depsMatch[1].trim().split(/[\s,]+/).filter(Boolean) : [],
106
+ hooks,
107
+ bin: binMatch?.[1].trim() ?? null
78
108
  };
79
109
  }
80
110
  async function listSharedSkills() {
@@ -128,8 +158,24 @@ async function removeSharedSkill(id) {
128
158
  const dir = join(sharedSkillsDir(), id);
129
159
  if (existsSync(dir)) rmSync(dir, { recursive: true });
130
160
  }
161
+ var TEMPLATE_SKILLS_DIR = {
162
+ claude: ".claude/skills",
163
+ pi: ".pi/skills",
164
+ codex: ".agents/skills"
165
+ };
131
166
  function mindSkillsDir(dir) {
132
- return resolve(dir, "home", ".claude", "skills");
167
+ const home = resolve(dir, "home");
168
+ let subdir = TEMPLATE_SKILLS_DIR.claude;
169
+ if (existsSync(join(home, "AGENTS.md"))) {
170
+ subdir = TEMPLATE_SKILLS_DIR.codex;
171
+ } else if (existsSync(join(home, "MINDS.md"))) {
172
+ subdir = TEMPLATE_SKILLS_DIR.pi;
173
+ }
174
+ return resolve(home, subdir);
175
+ }
176
+ function relSkillsPath(dir) {
177
+ const home = resolve(dir, "home");
178
+ return join("home", mindSkillsDir(dir).slice(home.length + 1));
133
179
  }
134
180
  function readUpstream(skillDir) {
135
181
  const upstreamPath = join(skillDir, ".upstream.json");
@@ -172,12 +218,21 @@ async function installSkill(_mindName, dir, skillId) {
172
218
  }
173
219
  }
174
220
  }
221
+ if (existsSync(skillMdPath)) {
222
+ const { hooks, bin } = parseSkillMd(readFileSync(skillMdPath, "utf-8"));
223
+ installHookShims(dir, skillId, hooks);
224
+ if (bin) installBinShim(dir, skillId, bin);
225
+ }
175
226
  let installNotes = null;
176
227
  const installMdPath = join(destDir, "references", "INSTALL.md");
177
228
  if (existsSync(installMdPath)) {
178
229
  installNotes = readFileSync(installMdPath, "utf-8");
179
230
  }
180
- await gitExec(["add", join("home", ".claude", "skills", skillId)], { cwd: dir });
231
+ await gitExec(["add", join(relSkillsPath(dir), skillId)], { cwd: dir });
232
+ await gitExec(["add", join("home", ".local", "hooks")], { cwd: dir }).catch(() => {
233
+ });
234
+ await gitExec(["add", join("home", ".local", "bin")], { cwd: dir }).catch(() => {
235
+ });
181
236
  if (npmInstalled.length > 0) {
182
237
  await gitExec(["add", "package.json", "package-lock.json"], { cwd: dir });
183
238
  }
@@ -190,7 +245,7 @@ async function installSkill(_mindName, dir, skillId) {
190
245
  };
191
246
  writeFileSync(join(destDir, ".upstream.json"), `${JSON.stringify(upstream, null, 2)}
192
247
  `);
193
- await gitExec(["add", join("home", ".claude", "skills", skillId, ".upstream.json")], {
248
+ await gitExec(["add", join(relSkillsPath(dir), skillId, ".upstream.json")], {
194
249
  cwd: dir
195
250
  });
196
251
  await gitExec(["commit", "--amend", "--no-edit"], { cwd: dir });
@@ -200,8 +255,18 @@ async function uninstallSkill(_mindName, dir, skillId) {
200
255
  validateSkillId(skillId);
201
256
  const skillDir = join(mindSkillsDir(dir), skillId);
202
257
  if (!existsSync(skillDir)) throw new Error(`Skill not installed: ${skillId}`);
258
+ removeHookShims(dir, skillId);
259
+ const skillMdPath = join(skillDir, "SKILL.md");
260
+ if (existsSync(skillMdPath)) {
261
+ const { bin } = parseSkillMd(readFileSync(skillMdPath, "utf-8"));
262
+ if (bin) removeBinShim(dir, bin);
263
+ }
203
264
  rmSync(skillDir, { recursive: true });
204
- await gitExec(["add", join("home", ".claude", "skills", skillId)], { cwd: dir });
265
+ await gitExec(["add", join(relSkillsPath(dir), skillId)], { cwd: dir });
266
+ await gitExec(["add", join("home", ".local", "hooks")], { cwd: dir }).catch(() => {
267
+ });
268
+ await gitExec(["add", join("home", ".local", "bin")], { cwd: dir }).catch(() => {
269
+ });
205
270
  await gitExec(["commit", "-m", `Uninstall skill: ${skillId}`], { cwd: dir });
206
271
  }
207
272
  async function updateSkill(_mindName, dir, skillId) {
@@ -217,7 +282,7 @@ async function updateSkill(_mindName, dir, skillId) {
217
282
  }
218
283
  const sourceDir = join(sharedSkillsDir(), upstream.source);
219
284
  if (!existsSync(sourceDir)) throw new Error(`Shared skill files missing: ${upstream.source}`);
220
- const relSkillPath = join("home", ".claude", "skills", skillId);
285
+ const relSkillPath = join(relSkillsPath(dir), skillId);
221
286
  const currentFiles = listFilesRecursive(skillDir).filter((f) => f !== ".upstream.json");
222
287
  const newFiles = listFilesRecursive(sourceDir).filter((f) => f !== ".upstream.json");
223
288
  const allFiles = [.../* @__PURE__ */ new Set([...currentFiles, ...newFiles])];
@@ -241,7 +306,9 @@ async function updateSkill(_mindName, dir, skillId) {
241
306
  try {
242
307
  baseContent2 = await gitExec(
243
308
  ["show", `${upstream.baseCommit}:${join(relSkillPath, file)}`],
244
- { cwd: dir }
309
+ {
310
+ cwd: dir
311
+ }
245
312
  );
246
313
  } catch {
247
314
  continue;
@@ -256,7 +323,9 @@ async function updateSkill(_mindName, dir, skillId) {
256
323
  try {
257
324
  baseContent = await gitExec(
258
325
  ["show", `${upstream.baseCommit}:${join(relSkillPath, file)}`],
259
- { cwd: dir }
326
+ {
327
+ cwd: dir
328
+ }
260
329
  );
261
330
  } catch {
262
331
  baseContent = "";
@@ -352,6 +421,77 @@ async function publishSkill(mindName, dir, skillId) {
352
421
  if (!existsSync(skillMdPath)) throw new Error(`SKILL.md not found in ${skillId}`);
353
422
  return importSkillFromDir(skillDir, mindName);
354
423
  }
424
+ function shimContent(skillId, scriptPath, skillsSubdir) {
425
+ const ext = scriptPath.split(".").pop() ?? "sh";
426
+ const skillScriptPath = `${skillsSubdir}/${skillId}/${scriptPath}`;
427
+ if (ext === "ts") {
428
+ return `#!/bin/bash
429
+ exec node --import tsx ${skillScriptPath} "$@"
430
+ `;
431
+ }
432
+ if (ext === "js") {
433
+ return `#!/bin/bash
434
+ exec node ${skillScriptPath} "$@"
435
+ `;
436
+ }
437
+ return `#!/bin/bash
438
+ exec bash ${skillScriptPath} "$@"
439
+ `;
440
+ }
441
+ function installHookShims(dir, skillId, hooks) {
442
+ const home = resolve(dir, "home");
443
+ const skillsSubdir = mindSkillsDir(dir).slice(home.length + 1);
444
+ for (const [event, scriptPath] of Object.entries(hooks)) {
445
+ const eventDir = join(dir, "home", ".local", "hooks", event);
446
+ mkdirSync(eventDir, { recursive: true });
447
+ const shimPath = join(eventDir, `50-${skillId}.sh`);
448
+ const content = shimContent(skillId, scriptPath, skillsSubdir);
449
+ writeFileSync(shimPath, content, { mode: 493 });
450
+ }
451
+ }
452
+ function removeHookShims(dir, skillId) {
453
+ const hooksBase = join(dir, "home", ".local", "hooks");
454
+ if (!existsSync(hooksBase)) return;
455
+ for (const eventDir of readdirSync(hooksBase, { withFileTypes: true })) {
456
+ if (!eventDir.isDirectory()) continue;
457
+ const shimPath = join(hooksBase, eventDir.name, `50-${skillId}.sh`);
458
+ if (existsSync(shimPath)) rmSync(shimPath);
459
+ }
460
+ }
461
+ function binShimContent(skillId, scriptPath, skillsSubdir) {
462
+ const skillScriptPath = `${skillsSubdir}/${skillId}/${scriptPath}`;
463
+ const ext = scriptPath.split(".").pop() ?? "sh";
464
+ if (ext === "ts") {
465
+ return `#!/bin/bash
466
+ exec node --import tsx ${skillScriptPath} "$@"
467
+ `;
468
+ }
469
+ if (ext === "js") {
470
+ return `#!/bin/bash
471
+ exec node ${skillScriptPath} "$@"
472
+ `;
473
+ }
474
+ return `#!/bin/bash
475
+ exec bash ${skillScriptPath} "$@"
476
+ `;
477
+ }
478
+ function binCommandName(scriptPath) {
479
+ return basename(scriptPath).replace(/\.[^.]+$/, "");
480
+ }
481
+ function installBinShim(dir, skillId, scriptPath) {
482
+ const home = resolve(dir, "home");
483
+ const skillsSubdir = mindSkillsDir(dir).slice(home.length + 1);
484
+ const binDir = join(dir, "home", ".local", "bin");
485
+ mkdirSync(binDir, { recursive: true });
486
+ const cmdName = binCommandName(scriptPath);
487
+ const shimPath = join(binDir, cmdName);
488
+ writeFileSync(shimPath, binShimContent(skillId, scriptPath, skillsSubdir), { mode: 493 });
489
+ }
490
+ function removeBinShim(dir, scriptPath) {
491
+ const cmdName = binCommandName(scriptPath);
492
+ const shimPath = join(dir, "home", ".local", "bin", cmdName);
493
+ if (existsSync(shimPath)) rmSync(shimPath);
494
+ }
355
495
  function listFilesRecursive(dir, prefix = "") {
356
496
  const results = [];
357
497
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
@@ -420,16 +560,24 @@ export {
420
560
  getStandardSkillsWithExtensions,
421
561
  initDefaultSkills,
422
562
  sharedSkillsDir,
563
+ parseSkillMd,
423
564
  listSharedSkills,
424
565
  getSharedSkill,
425
566
  importSkillFromDir,
426
567
  removeSharedSkill,
568
+ mindSkillsDir,
569
+ readUpstream,
427
570
  installSkill,
428
571
  uninstallSkill,
429
572
  updateSkill,
430
573
  listMindSkills,
431
574
  publishSkill,
575
+ installHookShims,
576
+ removeHookShims,
577
+ installBinShim,
578
+ removeBinShim,
432
579
  listFilesRecursive,
580
+ findSkillsRoot,
433
581
  hashSkillDir,
434
582
  syncBuiltinSkills
435
583
  };
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/lib/template-hash.ts
4
- import { createHash } from "crypto";
5
- import { existsSync as existsSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "fs";
6
- import { resolve as resolve2 } from "path";
7
-
8
3
  // src/lib/template.ts
9
4
  import {
10
5
  cpSync,
@@ -110,39 +105,10 @@ function listFiles(dir) {
110
105
  return results;
111
106
  }
112
107
 
113
- // src/lib/template-hash.ts
114
- var hashCache = /* @__PURE__ */ new Map();
115
- function computeTemplateHash(templateName) {
116
- const cached = hashCache.get(templateName);
117
- if (cached) return cached;
118
- const templatesRoot = findTemplatesRoot();
119
- const baseDir = resolve2(templatesRoot, "_base");
120
- const templateDir = resolve2(templatesRoot, templateName);
121
- if (!existsSync2(baseDir)) throw new Error(`Base template not found: ${baseDir}`);
122
- if (!existsSync2(templateDir)) throw new Error(`Template not found: ${templateName}`);
123
- const { composedDir } = composeTemplate(templatesRoot, templateName);
124
- try {
125
- const files = listFiles(composedDir).filter((f) => !f.startsWith(".init/") && !f.startsWith(".init\\")).sort();
126
- const hash = createHash("sha256");
127
- for (const file of files) {
128
- const content = readFileSync2(resolve2(composedDir, file));
129
- hash.update(file);
130
- hash.update("\0");
131
- hash.update(content);
132
- }
133
- const result = hash.digest("hex");
134
- hashCache.set(templateName, result);
135
- return result;
136
- } finally {
137
- rmSync2(composedDir, { recursive: true, force: true });
138
- }
139
- }
140
-
141
108
  export {
142
109
  findTemplatesRoot,
143
110
  composeTemplate,
144
111
  copyTemplateToDir,
145
112
  applyInitFiles,
146
- listFiles,
147
- computeTemplateHash
113
+ listFiles
148
114
  };
@@ -6,11 +6,11 @@ function slugify(text) {
6
6
  }
7
7
  function buildVoluteSlug(opts) {
8
8
  if (opts.convType === "channel" && opts.convName) {
9
- return `volute:#${opts.convName}`;
9
+ return `#${opts.convName}`;
10
10
  }
11
11
  const other = opts.participants.find((p) => p.username !== opts.mindUsername);
12
12
  const otherSlug = other ? slugify(other.username) : "";
13
- return otherSlug ? `volute:@${otherSlug}` : `volute:${opts.conversationId}`;
13
+ return otherSlug ? `@${otherSlug}` : opts.conversationId;
14
14
  }
15
15
 
16
16
  export {
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
6
  readGlobalConfig
7
- } from "./chunk-6QIUN46C.js";
7
+ } from "./chunk-N432I7QH.js";
8
8
  import {
9
9
  getBaseName,
10
10
  readRegistry,
11
11
  voluteHome,
12
12
  voluteSystemDir,
13
13
  voluteUserHome
14
- } from "./chunk-X62AXPR7.js";
14
+ } from "./chunk-LRCG2JLP.js";
15
15
 
16
16
  // src/lib/sandbox.ts
17
17
  import { resolve } from "path";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  mindDir,
4
4
  stateDir
5
- } from "./chunk-X62AXPR7.js";
5
+ } from "./chunk-LRCG2JLP.js";
6
6
 
7
7
  // src/lib/archive.ts
8
8
  import { execFileSync } from "child_process";
@@ -91,12 +91,6 @@ function createExportArchive(options) {
91
91
  }
92
92
  }
93
93
  }
94
- if (existsSync(state)) {
95
- const channelsPath = resolve(state, "channels.json");
96
- if (existsSync(channelsPath)) {
97
- zip.addFile("state/channels.json", readFileSync(channelsPath));
98
- }
99
- }
100
94
  if (includeEnv && existsSync(state)) {
101
95
  const envPath = resolve(state, "env.json");
102
96
  if (existsSync(envPath)) {
@@ -184,14 +178,12 @@ function extractArchive(archivePath, destDir) {
184
178
  mkdirSync(resolve(destPath, ".."), { recursive: true });
185
179
  writeFileSync(destPath, entry.getData());
186
180
  }
187
- const channelsJson = resolve(extractedStateDir, "channels.json");
188
181
  const envJson = resolve(extractedStateDir, "env.json");
189
182
  const historyJsonl = resolve(normalizedDestDir, "history.jsonl");
190
183
  const sessionsDir = resolve(normalizedDestDir, "sessions");
191
184
  return {
192
185
  manifest,
193
186
  mindDir: extractedMindDir,
194
- channelsJson: existsSync(channelsJson) ? channelsJson : null,
195
187
  envJson: existsSync(envJson) ? envJson : null,
196
188
  historyJsonl: existsSync(historyJsonl) ? historyJsonl : null,
197
189
  sessionsDir: existsSync(sessionsDir) ? sessionsDir : null
@@ -5,10 +5,10 @@ import {
5
5
  pollHealthDown,
6
6
  readDaemonConfig,
7
7
  stopService
8
- } from "./chunk-SNVPRRT7.js";
8
+ } from "./chunk-NNB4WIG7.js";
9
9
  import {
10
10
  voluteSystemDir
11
- } from "./chunk-X62AXPR7.js";
11
+ } from "./chunk-LRCG2JLP.js";
12
12
 
13
13
  // src/commands/down.ts
14
14
  import { existsSync, readFileSync, unlinkSync } from "fs";
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  broadcast
4
- } from "./chunk-EKDWA7E4.js";
4
+ } from "./chunk-KVK2DLWI.js";
5
+ import {
6
+ getDb
7
+ } from "./chunk-LRCG2JLP.js";
5
8
  import {
6
- getDb,
7
9
  users
8
- } from "./chunk-X62AXPR7.js";
10
+ } from "./chunk-RPZZSXV3.js";
9
11
 
10
12
  // src/lib/auth.ts
11
13
  import { compareSync, hashSync } from "bcryptjs";
12
- import { and, count, eq } from "drizzle-orm";
14
+ import { and, count, eq, or } from "drizzle-orm";
13
15
  var userSelectFields = {
14
16
  id: users.id,
15
17
  username: users.username,
@@ -61,7 +63,12 @@ async function listUsersByType(userType) {
61
63
  }
62
64
  async function getOrCreateMindUser(mindName) {
63
65
  const db = await getDb();
64
- const existing = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
66
+ const existing = await db.select(userSelectFields).from(users).where(
67
+ and(
68
+ eq(users.username, mindName),
69
+ or(eq(users.user_type, "mind"), eq(users.user_type, "system"))
70
+ )
71
+ ).get();
65
72
  if (existing) return existing;
66
73
  try {
67
74
  const [result] = await db.insert(users).values({
@@ -72,10 +79,13 @@ async function getOrCreateMindUser(mindName) {
72
79
  }).returning(userSelectFields);
73
80
  return result;
74
81
  } catch (err) {
75
- if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
76
- const retried = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
77
- if (retried) return retried;
78
- }
82
+ const retried = await db.select(userSelectFields).from(users).where(
83
+ and(
84
+ eq(users.username, mindName),
85
+ or(eq(users.user_type, "mind"), eq(users.user_type, "system"))
86
+ )
87
+ ).get();
88
+ if (retried) return retried;
79
89
  throw err;
80
90
  }
81
91
  }
@@ -87,9 +97,9 @@ async function getOrCreateSystemUser() {
87
97
  const [result] = await db.insert(users).values({
88
98
  username: "volute",
89
99
  password_hash: "!system",
90
- role: "user",
100
+ role: "system",
91
101
  user_type: "system",
92
- display_name: "Volute"
102
+ display_name: "volute"
93
103
  }).returning(userSelectFields);
94
104
  return result;
95
105
  } catch (err) {
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ wrapForIsolation
4
+ } from "./chunk-VH33ZWMW.js";
5
+
6
+ // src/lib/exec.ts
7
+ import { execFile as execFileCb, execFileSync, spawn } from "child_process";
8
+ async function exec(cmd, args, options) {
9
+ const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
10
+ return new Promise((resolve, reject) => {
11
+ execFileCb(
12
+ wrappedCmd,
13
+ wrappedArgs,
14
+ { cwd: options?.cwd, env: options?.env },
15
+ (err, stdout, stderr) => {
16
+ if (err) {
17
+ err.stderr = stderr;
18
+ err.stdout = stdout;
19
+ reject(err);
20
+ } else {
21
+ resolve(stdout);
22
+ }
23
+ }
24
+ );
25
+ });
26
+ }
27
+ function gitExec(args, options) {
28
+ const fullArgs = process.env.VOLUTE_ISOLATION === "user" ? ["-c", "safe.directory=*", ...args] : args;
29
+ return exec("git", fullArgs, options);
30
+ }
31
+ function resolveVoluteBin() {
32
+ try {
33
+ return execFileSync("which", ["volute"], { encoding: "utf-8" }).trim();
34
+ } catch {
35
+ throw new Error("Could not find volute binary on PATH");
36
+ }
37
+ }
38
+ async function execInherit(cmd, args, options) {
39
+ const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
40
+ return new Promise((resolve, reject) => {
41
+ const child = spawn(wrappedCmd, wrappedArgs, {
42
+ cwd: options?.cwd,
43
+ env: options?.env,
44
+ stdio: "inherit"
45
+ });
46
+ child.on("error", reject);
47
+ child.on("close", (code) => {
48
+ if (code === 0) resolve();
49
+ else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
50
+ });
51
+ });
52
+ }
53
+
54
+ export {
55
+ exec,
56
+ gitExec,
57
+ resolveVoluteBin,
58
+ execInherit
59
+ };
@@ -3,9 +3,11 @@ import {
3
3
  logger_default
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
- activity,
7
6
  getDb
8
- } from "./chunk-X62AXPR7.js";
7
+ } from "./chunk-LRCG2JLP.js";
8
+ import {
9
+ activity
10
+ } from "./chunk-RPZZSXV3.js";
9
11
 
10
12
  // src/lib/events/activity-events.ts
11
13
  var subscribers = /* @__PURE__ */ new Set();
@@ -35,6 +37,7 @@ async function publish(event) {
35
37
  }
36
38
  const full = { ...event, id, created_at };
37
39
  notify(full);
40
+ return id;
38
41
  }
39
42
  function broadcast(event) {
40
43
  const created_at = event.created_at ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  publish
4
- } from "./chunk-EKDWA7E4.js";
4
+ } from "./chunk-KVK2DLWI.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-YUIHSKR6.js";