@intent-systems/nexus 2026.1.5-5 → 2026.1.5-8

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 (245) hide show
  1. package/dist/agents/identity-state.js +45 -23
  2. package/dist/agents/skill-runner.js +12 -2
  3. package/dist/agents/skill-usage.js +4 -7
  4. package/dist/agents/skills-status.js +3 -2
  5. package/dist/agents/skills.js +20 -23
  6. package/dist/agents/workspace.js +32 -19
  7. package/dist/capabilities/detector.js +2 -2
  8. package/dist/cli/cloud-cli.js +58 -0
  9. package/dist/cli/credential-cli.js +77 -8
  10. package/dist/cli/program.js +24 -2
  11. package/dist/cli/skills-cli.js +30 -18
  12. package/dist/cli/skills-hub-cli.js +9 -7
  13. package/dist/commands/bootstrap-preset.js +16 -6
  14. package/dist/commands/config.js +85 -0
  15. package/dist/commands/cursor-hooks.js +240 -0
  16. package/dist/commands/cursor-rules.js +13 -207
  17. package/dist/commands/identity.js +3 -3
  18. package/dist/commands/init.js +293 -6
  19. package/dist/commands/onboard-eve-identity.js +1 -1
  20. package/dist/commands/skills-manifest.js +89 -29
  21. package/dist/commands/status.js +52 -50
  22. package/dist/daemon/launchd.js +14 -0
  23. package/dist/entry.js +0 -0
  24. package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud +0 -0
  25. package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud-rs +0 -0
  26. package/dist/utils.js +6 -2
  27. package/docs/AGENTS.default.md +1 -1
  28. package/docs/configuration.md +1 -1
  29. package/docs/feature-inventory/overview.md +2 -2
  30. package/docs/reference/templates/AGENTS.md +172 -109
  31. package/docs/templates/AGENTS.md +140 -199
  32. package/docs/templates/BOOTSTRAP.md +40 -20
  33. package/docs/templates/IDENTITY.md +6 -0
  34. package/docs/templates/USER.md +22 -2
  35. package/package.json +3 -1
  36. package/skills/{notion → connectors/notion}/SKILL.md +1 -1
  37. package/skills/{filesystem → guides/filesystem}/SKILL.md +1 -1
  38. package/skills/{onboarding → guides/onboarding}/SKILL.md +1 -1
  39. package/skills/{onboarding → guides/onboarding}/docs/CAPABILITY_TAXONOMY.md +5 -5
  40. package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR.md +8 -8
  41. package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_ONBOARDING.md +2 -2
  42. package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_SKILLS.md +26 -20
  43. package/skills/{onboarding → guides/onboarding}/docs/GOAL_STATE_ARCHITECTURE.md +38 -43
  44. package/skills/{onboarding → guides/onboarding}/docs/NEXUS_SYSTEM_OVERVIEW.md +4 -4
  45. package/skills/{onboarding → guides/onboarding}/docs/SKILLS_HUB_SPEC.md +1 -1
  46. package/skills/{onboarding → guides/onboarding}/docs/SKILLS_SPECIFICATION.md +8 -7
  47. package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_DESIGN.md +16 -16
  48. package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_PRD.md +10 -12
  49. package/skills/guides/onboarding/docs/canonical/00_CONFLICT_ANALYSIS.md +463 -0
  50. package/skills/guides/onboarding/docs/canonical/01_NEXUS_OVERVIEW.md +167 -0
  51. package/skills/guides/onboarding/docs/canonical/02_CLI_REFERENCE.md +404 -0
  52. package/skills/guides/onboarding/docs/canonical/03_STATE_ARCHITECTURE.md +357 -0
  53. package/skills/guides/onboarding/docs/canonical/04_SKILL_SPECIFICATION.md +393 -0
  54. package/skills/guides/onboarding/docs/canonical/05_CAPABILITY_TAXONOMY.md +298 -0
  55. package/skills/guides/onboarding/docs/canonical/06_CAPABILITIES_REFERENCE.md +207 -0
  56. package/skills/guides/onboarding/docs/canonical/07_AGENT_BINDINGS.md +85 -0
  57. package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/nexus-cloud.md +2 -2
  58. package/skills/{onboarding → guides/onboarding}/scripts/ralph/progress.txt +1 -1
  59. package/skills/{nexus-cloud → tools/nexus-cloud}/SKILL.md +2 -1
  60. package/skills/{nexus-cloud → tools/nexus-cloud}/docs/setup.md +1 -1
  61. package/docs/templates/PROFILE.md +0 -14
  62. /package/skills/{brave-search → connectors/brave-search}/SKILL.md +0 -0
  63. /package/skills/{brave-search → connectors/brave-search}/docs/setup.md +0 -0
  64. /package/skills/{brave-search → connectors/brave-search}/docs/troubleshooting.md +0 -0
  65. /package/skills/{brave-search → connectors/brave-search}/docs/usage.md +0 -0
  66. /package/skills/{brave-search → connectors/brave-search}/scripts/content.mjs +0 -0
  67. /package/skills/{brave-search → connectors/brave-search}/scripts/search.mjs +0 -0
  68. /package/skills/{discord → connectors/discord}/SKILL.md +0 -0
  69. /package/skills/{gemini → connectors/gemini}/SKILL.md +0 -0
  70. /package/skills/{github → connectors/github}/SKILL.md +0 -0
  71. /package/skills/{github → connectors/github}/docs/setup.md +0 -0
  72. /package/skills/{github → connectors/github}/docs/troubleshooting.md +0 -0
  73. /package/skills/{google-oauth → connectors/google-oauth}/SKILL.md +0 -0
  74. /package/skills/{slack → connectors/slack}/SKILL.md +0 -0
  75. /package/skills/{telegram → connectors/telegram}/SKILL.md +0 -0
  76. /package/skills/{telegram → connectors/telegram}/docs/pairing.md +0 -0
  77. /package/skills/{telegram → connectors/telegram}/docs/setup.md +0 -0
  78. /package/skills/{telegram → connectors/telegram}/docs/webhook.md +0 -0
  79. /package/skills/{wacli → connectors/wacli}/SKILL.md +0 -0
  80. /package/skills/{wacli → connectors/wacli}/docs/auth.md +0 -0
  81. /package/skills/{wacli → connectors/wacli}/docs/backup.md +0 -0
  82. /package/skills/{wacli → connectors/wacli}/docs/troubleshooting.md +0 -0
  83. /package/skills/{browser-use-agent-sdk → guides/browser-use-agent-sdk}/SKILL.md +0 -0
  84. /package/skills/{json-render → guides/json-render}/SKILL.md +0 -0
  85. /package/skills/{json-render → guides/json-render}/assets/components/README.md +0 -0
  86. /package/skills/{json-render → guides/json-render}/assets/components/catalog.ts +0 -0
  87. /package/skills/{json-render → guides/json-render}/assets/components/registry.tsx +0 -0
  88. /package/skills/{json-render → guides/json-render}/assets/demo/App.css +0 -0
  89. /package/skills/{json-render → guides/json-render}/assets/demo/App.tsx +0 -0
  90. /package/skills/{json-render → guides/json-render}/assets/demo/README.md +0 -0
  91. /package/skills/{json-render → guides/json-render}/assets/demo/catalog.ts +0 -0
  92. /package/skills/{json-render → guides/json-render}/assets/demo/data/nexus-core.json +0 -0
  93. /package/skills/{json-render → guides/json-render}/assets/demo/index.css +0 -0
  94. /package/skills/{json-render → guides/json-render}/assets/demo/registry.tsx +0 -0
  95. /package/skills/{json-render → guides/json-render}/docs/nexus-state-demo.md +0 -0
  96. /package/skills/{json-render → guides/json-render}/docs/shadcn-preset.md +0 -0
  97. /package/skills/{json-render → guides/json-render}/scripts/create-vite-demo.sh +0 -0
  98. /package/skills/{json-render → guides/json-render}/scripts/llm-server/README.md +0 -0
  99. /package/skills/{json-render → guides/json-render}/scripts/llm-server/catalog.ts +0 -0
  100. /package/skills/{json-render → guides/json-render}/scripts/llm-server/package-lock.json +0 -0
  101. /package/skills/{json-render → guides/json-render}/scripts/llm-server/package.json +0 -0
  102. /package/skills/{json-render → guides/json-render}/scripts/llm-server/server.ts +0 -0
  103. /package/skills/{onboarding → guides/onboarding}/docs/CAPABILITIES.md +0 -0
  104. /package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_CREDENTIALS.md +0 -0
  105. /package/skills/{onboarding → guides/onboarding}/docs/DOCUMENTATION_OVERVIEW.md +0 -0
  106. /package/skills/{onboarding → guides/onboarding}/docs/ENTITY_MODEL.md +0 -0
  107. /package/skills/{onboarding → guides/onboarding}/docs/SKILL_INVENTORY.md +0 -0
  108. /package/skills/{onboarding → guides/onboarding}/docs/STATE_ARCHITECTURE.md +0 -0
  109. /package/skills/{onboarding → guides/onboarding}/docs/TROUBLESHOOTING.md +0 -0
  110. /package/skills/{onboarding → guides/onboarding}/docs/USER_JOURNEY.md +0 -0
  111. /package/skills/{onboarding → guides/onboarding}/docs/WOW_MOMENTS.md +0 -0
  112. /package/skills/{onboarding → guides/onboarding}/docs/agent-apple-id.md +0 -0
  113. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/1password.md +0 -0
  114. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/TEMPLATE.md +0 -0
  115. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/aix.md +0 -0
  116. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/bird.md +0 -0
  117. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/brave-search.md +0 -0
  118. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/comms.md +0 -0
  119. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/computer-use.md +0 -0
  120. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/cron-and-heartbeat.md +0 -0
  121. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/eve.md +0 -0
  122. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/github.md +0 -0
  123. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/gog.md +0 -0
  124. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/homebrew-prereqs.md +0 -0
  125. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/qmd.md +0 -0
  126. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/telegram.md +0 -0
  127. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/wacli.md +0 -0
  128. /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/weather.md +0 -0
  129. /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prd.json +0 -0
  130. /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prompt.md +0 -0
  131. /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.log +0 -0
  132. /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.sh +0 -0
  133. /package/skills/{onboarding → guides/onboarding}/scripts/setup-cursor-skills.sh +0 -0
  134. /package/skills/{1password → tools/1password}/SKILL.md +0 -0
  135. /package/skills/{1password → tools/1password}/docs/setup.md +0 -0
  136. /package/skills/{1password → tools/1password}/docs/troubleshooting.md +0 -0
  137. /package/skills/{1password → tools/1password}/references/cli-examples.md +0 -0
  138. /package/skills/{1password → tools/1password}/references/get-started.md +0 -0
  139. /package/skills/{agent-browser → tools/agent-browser}/SKILL.md +0 -0
  140. /package/skills/{agent-browser → tools/agent-browser}/docs/browser-use-eval.md +0 -0
  141. /package/skills/{agent-browser → tools/agent-browser}/docs/first-tests.md +0 -0
  142. /package/skills/{agent-browser → tools/agent-browser}/docs/wordle-nyt-eval.js +0 -0
  143. /package/skills/{aix → tools/aix}/SKILL.md +0 -0
  144. /package/skills/{aix → tools/aix}/docs/embeddings.md +0 -0
  145. /package/skills/{aix → tools/aix}/docs/setup.md +0 -0
  146. /package/skills/{aix → tools/aix}/docs/troubleshooting.md +0 -0
  147. /package/skills/{aix → tools/aix}/references/sql.md +0 -0
  148. /package/skills/{apple-notes → tools/apple-notes}/SKILL.md +0 -0
  149. /package/skills/{apple-reminders → tools/apple-reminders}/SKILL.md +0 -0
  150. /package/skills/{bear-notes → tools/bear-notes}/SKILL.md +0 -0
  151. /package/skills/{bird → tools/bird}/SKILL.md +0 -0
  152. /package/skills/{bird → tools/bird}/docs/auth.md +0 -0
  153. /package/skills/{bird → tools/bird}/docs/troubleshooting.md +0 -0
  154. /package/skills/{blogwatcher → tools/blogwatcher}/SKILL.md +0 -0
  155. /package/skills/{blucli → tools/blucli}/SKILL.md +0 -0
  156. /package/skills/{camsnap → tools/camsnap}/SKILL.md +0 -0
  157. /package/skills/{clawdhub → tools/clawdhub}/SKILL.md +0 -0
  158. /package/skills/{coding-agent → tools/coding-agent}/SKILL.md +0 -0
  159. /package/skills/{comms → tools/comms}/SKILL.md +0 -0
  160. /package/skills/{comms → tools/comms}/docs/adapters.md +0 -0
  161. /package/skills/{comms → tools/comms}/docs/setup.md +0 -0
  162. /package/skills/{comms → tools/comms}/docs/troubleshooting.md +0 -0
  163. /package/skills/{comms → tools/comms}/references/schema.md +0 -0
  164. /package/skills/{computer-use → tools/computer-use}/SKILL.md +0 -0
  165. /package/skills/{computer-use → tools/computer-use}/docs/open-interpreter.md +0 -0
  166. /package/skills/{computer-use → tools/computer-use}/docs/peekaboo.md +0 -0
  167. /package/skills/{computer-use → tools/computer-use}/docs/setup.md +0 -0
  168. /package/skills/{computer-use → tools/computer-use}/docs/troubleshooting.md +0 -0
  169. /package/skills/{eightctl → tools/eightctl}/SKILL.md +0 -0
  170. /package/skills/{eve → tools/eve}/SKILL.md +0 -0
  171. /package/skills/{eve → tools/eve}/docs/dual-account.md +0 -0
  172. /package/skills/{eve → tools/eve}/docs/intelligence.md +0 -0
  173. /package/skills/{eve → tools/eve}/docs/setup.md +0 -0
  174. /package/skills/{eve → tools/eve}/docs/troubleshooting.md +0 -0
  175. /package/skills/{eve → tools/eve}/scripts/setup-dual-account.sh +0 -0
  176. /package/skills/{food-order → tools/food-order}/SKILL.md +0 -0
  177. /package/skills/{gh → tools/gh}/SKILL.md +0 -0
  178. /package/skills/{gh → tools/gh}/docs/usage.md +0 -0
  179. /package/skills/{gifgrep → tools/gifgrep}/SKILL.md +0 -0
  180. /package/skills/{gog → tools/gog}/SKILL.md +0 -0
  181. /package/skills/{gog → tools/gog}/docs/portability.md +0 -0
  182. /package/skills/{gog → tools/gog}/docs/setup.md +0 -0
  183. /package/skills/{gog → tools/gog}/docs/troubleshooting.md +0 -0
  184. /package/skills/{gog → tools/gog}/scripts/cdp/README.md +0 -0
  185. /package/skills/{gog → tools/gog}/scripts/cdp/add_test_users.py +0 -0
  186. /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts.py +0 -0
  187. /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts_manual.py +0 -0
  188. /package/skills/{gog → tools/gog}/scripts/cdp/create_oauth_client.py +0 -0
  189. /package/skills/{gog → tools/gog}/scripts/cdp/launch_cdp_chrome.sh +0 -0
  190. /package/skills/{goplaces → tools/goplaces}/SKILL.md +0 -0
  191. /package/skills/{imsg → tools/imsg}/SKILL.md +0 -0
  192. /package/skills/{local-places → tools/local-places}/SERVER_README.md +0 -0
  193. /package/skills/{local-places → tools/local-places}/SKILL.md +0 -0
  194. /package/skills/{local-places → tools/local-places}/pyproject.toml +0 -0
  195. /package/skills/{local-places → tools/local-places}/src/local_places/__init__.py +0 -0
  196. /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
  197. /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
  198. /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
  199. /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
  200. /package/skills/{local-places → tools/local-places}/src/local_places/google_places.py +0 -0
  201. /package/skills/{local-places → tools/local-places}/src/local_places/main.py +0 -0
  202. /package/skills/{local-places → tools/local-places}/src/local_places/schemas.py +0 -0
  203. /package/skills/{mcporter → tools/mcporter}/SKILL.md +0 -0
  204. /package/skills/{model-usage → tools/model-usage}/SKILL.md +0 -0
  205. /package/skills/{model-usage → tools/model-usage}/references/codexbar-cli.md +0 -0
  206. /package/skills/{model-usage → tools/model-usage}/scripts/model_usage.py +0 -0
  207. /package/skills/{nano-banana-pro → tools/nano-banana-pro}/SKILL.md +0 -0
  208. /package/skills/{nano-banana-pro → tools/nano-banana-pro}/scripts/generate_image.py +0 -0
  209. /package/skills/{nano-pdf → tools/nano-pdf}/SKILL.md +0 -0
  210. /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/security.md +0 -0
  211. /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/troubleshooting.md +0 -0
  212. /package/skills/{obsidian → tools/obsidian}/SKILL.md +0 -0
  213. /package/skills/{openai-image-gen → tools/openai-image-gen}/SKILL.md +0 -0
  214. /package/skills/{openai-image-gen → tools/openai-image-gen}/scripts/gen.py +0 -0
  215. /package/skills/{openai-whisper → tools/openai-whisper}/SKILL.md +0 -0
  216. /package/skills/{openai-whisper-api → tools/openai-whisper-api}/SKILL.md +0 -0
  217. /package/skills/{openai-whisper-api → tools/openai-whisper-api}/scripts/transcribe.sh +0 -0
  218. /package/skills/{openhue → tools/openhue}/SKILL.md +0 -0
  219. /package/skills/{oracle → tools/oracle}/SKILL.md +0 -0
  220. /package/skills/{ordercli → tools/ordercli}/SKILL.md +0 -0
  221. /package/skills/{peekaboo → tools/peekaboo}/SKILL.md +0 -0
  222. /package/skills/{qmd → tools/qmd}/SKILL.md +0 -0
  223. /package/skills/{qmd → tools/qmd}/docs/mcp.md +0 -0
  224. /package/skills/{qmd → tools/qmd}/docs/ollama.md +0 -0
  225. /package/skills/{qmd → tools/qmd}/docs/setup.md +0 -0
  226. /package/skills/{sag → tools/sag}/SKILL.md +0 -0
  227. /package/skills/{skill-cli-template → tools/skill-cli-template}/SKILL.md +0 -0
  228. /package/skills/{songsee → tools/songsee}/SKILL.md +0 -0
  229. /package/skills/{sonoscli → tools/sonoscli}/SKILL.md +0 -0
  230. /package/skills/{spotify-player → tools/spotify-player}/SKILL.md +0 -0
  231. /package/skills/{summarize → tools/summarize}/SKILL.md +0 -0
  232. /package/skills/{things-mac → tools/things-mac}/SKILL.md +0 -0
  233. /package/skills/{tmux → tools/tmux}/SKILL.md +0 -0
  234. /package/skills/{tmux → tools/tmux}/scripts/find-sessions.sh +0 -0
  235. /package/skills/{tmux → tools/tmux}/scripts/wait-for-text.sh +0 -0
  236. /package/skills/{trello → tools/trello}/SKILL.md +0 -0
  237. /package/skills/{upstream-sync → tools/upstream-sync}/SKILL.md +0 -0
  238. /package/skills/{upstream-sync → tools/upstream-sync}/scripts/auto-port.sh +0 -0
  239. /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-all.sh +0 -0
  240. /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-nexus.sh +0 -0
  241. /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-pi-ai.sh +0 -0
  242. /package/skills/{video-frames → tools/video-frames}/SKILL.md +0 -0
  243. /package/skills/{video-frames → tools/video-frames}/scripts/frame.sh +0 -0
  244. /package/skills/{weather → tools/weather}/SKILL.md +0 -0
  245. /package/skills/{weather → tools/weather}/docs/usage.md +0 -0
@@ -1,10 +1,7 @@
1
- import fs from "node:fs";
2
1
  import fsp from "node:fs/promises";
3
2
  import path from "node:path";
4
- import { resolveStateDir } from "../config/paths.js";
5
3
  import { defaultRuntime } from "../runtime.js";
6
- import { MANAGED_SKILLS_DIR, NEXUS_ROOT, resolveUserPath } from "../utils.js";
7
- import { flattenManifest, generateSkillManifest, readSkillManifest, } from "./skills-manifest.js";
4
+ import { NEXUS_ROOT, SKILLS_STATE_DIR, WORKSPACE_SKILLS_DIR, resolveUserPath, } from "../utils.js";
8
5
  /**
9
6
  * Generate and write `.cursor/rules` for a given workspace.
10
7
  *
@@ -12,8 +9,7 @@ import { flattenManifest, generateSkillManifest, readSkillManifest, } from "./sk
12
9
  * nexus cursor-rules [--workspace <path>]
13
10
  */
14
11
  export async function cursorRulesCommand(opts, runtime = defaultRuntime) {
15
- const workspaceDir = opts?.workspace?.trim() ||
16
- path.join(process.env.HOME || "~", "nexus", "home");
12
+ const workspaceDir = opts?.workspace?.trim() || NEXUS_ROOT;
17
13
  const resolvedWorkspace = resolveUserPath(workspaceDir);
18
14
  runtime.log(`Generating .cursor/rules for workspace: ${resolvedWorkspace}`);
19
15
  const outputPath = await writeCursorRules({
@@ -24,214 +20,24 @@ export async function cursorRulesCommand(opts, runtime = defaultRuntime) {
24
20
  });
25
21
  runtime.log(`✓ Wrote ${outputPath}`);
26
22
  }
27
- /**
28
- * Group skills by category based on their description/name
29
- */
30
- function categorizeSkills(skills) {
31
- const categories = {
32
- "Communication & Messaging": [],
33
- "Productivity & Notes": [],
34
- "Google & Cloud Services": [],
35
- "Media & Content": [],
36
- "Smart Home & Devices": [],
37
- "Development & Tools": [],
38
- Other: [],
39
- };
40
- const categoryPatterns = [
41
- [
42
- "Communication & Messaging",
43
- [/imsg|imessage|whatsapp|discord|slack|telegram|sms|message|chat/i],
44
- ],
45
- [
46
- "Productivity & Notes",
47
- [/note|reminder|bear|obsidian|notion|things|trello|task|todo/i],
48
- ],
49
- [
50
- "Google & Cloud Services",
51
- [/google|gmail|calendar|drive|sheets|docs|gemini|gog|places/i],
52
- ],
53
- [
54
- "Media & Content",
55
- [/image|video|audio|camera|gif|pdf|whisper|speech|photo|screenshot/i],
56
- ],
57
- [
58
- "Smart Home & Devices",
59
- [/hue|sonos|spotify|eight|sleep|smart|home|light|speaker/i],
60
- ],
61
- [
62
- "Development & Tools",
63
- [/github|git|code|coding|terminal|bash|cli|api|mcp|1password|search/i],
64
- ],
65
- ];
66
- for (const skill of skills) {
67
- const text = `${skill.name} ${skill.description || ""}`;
68
- let matched = false;
69
- for (const [category, patterns] of categoryPatterns) {
70
- if (patterns.some((p) => p.test(text))) {
71
- categories[category].push(skill);
72
- matched = true;
73
- break;
74
- }
75
- }
76
- if (!matched) {
77
- categories.Other.push(skill);
78
- }
79
- }
80
- return categories;
81
- }
82
- /**
83
- * Format skill entries for the rules file
84
- */
85
- function formatSkillsSection(skills) {
86
- const categories = categorizeSkills(skills);
87
- const lines = [];
88
- for (const [category, categorySkills] of Object.entries(categories)) {
89
- if (categorySkills.length === 0)
90
- continue;
91
- lines.push(`### ${category}`);
92
- for (const skill of categorySkills.sort((a, b) => a.name.localeCompare(b.name))) {
93
- const desc = skill.description
94
- ? skill.description.slice(0, 80) +
95
- (skill.description.length > 80 ? "..." : "")
96
- : "(no description)";
97
- lines.push(`- **${skill.name}**: ${desc}`);
98
- }
99
- lines.push("");
100
- }
101
- return lines.join("\n");
102
- }
103
- /**
104
- * Load AGENTS.md from the Nexus root
105
- */
106
- async function loadAgentsMd(workspaceDir) {
107
- const candidates = [
108
- path.join(NEXUS_ROOT, "AGENTS.md"),
109
- path.join(workspaceDir, "AGENTS.md"),
110
- path.join(workspaceDir, ".intent", "AGENTS.md"),
111
- ];
112
- for (const candidate of candidates) {
113
- try {
114
- if (fs.existsSync(candidate)) {
115
- return await fsp.readFile(candidate, "utf-8");
116
- }
117
- }
118
- catch {
119
- // continue
120
- }
121
- }
122
- return undefined;
123
- }
124
- /**
125
- * Load bootstrap files (IDENTITY.md, PROFILE.md, SOUL.md) if they exist
126
- */
127
- async function loadBootstrapFiles(_workspaceDir) {
128
- const files = {};
129
- const agentId = process.env.NEXUS_AGENT_ID?.trim() || "default";
130
- const stateDir = resolveStateDir();
131
- const agentIdentityDir = path.join(stateDir, "agents", agentId, "identity");
132
- const userIdentityDir = path.join(stateDir, "user", "identity");
133
- const bootstrapFiles = ["IDENTITY.md", "PROFILE.md", "SOUL.md"];
134
- for (const filename of bootstrapFiles) {
135
- const candidates = filename === "PROFILE.md"
136
- ? [path.join(userIdentityDir, filename)]
137
- : [path.join(agentIdentityDir, filename)];
138
- for (const candidate of candidates) {
139
- try {
140
- if (fs.existsSync(candidate)) {
141
- files[filename] = await fsp.readFile(candidate, "utf-8");
142
- break;
143
- }
144
- }
145
- catch {
146
- // continue
147
- }
148
- }
149
- }
150
- return files;
151
- }
152
23
  /**
153
24
  * Generate .cursor/rules content from Nexus skills and config
154
25
  */
155
26
  export async function generateCursorRules(options = {}) {
156
- const workspaceDir = options.workspaceDir || process.cwd();
157
- const skillsDir = options.skillsDir || MANAGED_SKILLS_DIR;
158
- const userSkillsDir = options.userSkillsDir || path.join(NEXUS_ROOT, "home", "skills");
159
- const managedSkillsDir = path.join(skillsDir, "managed");
160
- // Try to read existing manifest, or generate one
161
- let manifest = await readSkillManifest();
162
- if (!manifest) {
163
- manifest = await generateSkillManifest(skillsDir, userSkillsDir);
164
- }
165
- const skills = flattenManifest(manifest);
166
- const skillsSection = formatSkillsSection(skills);
167
- // Load AGENTS.md if requested
168
- let agentsMdSection = "";
169
- if (options.includeAgentsMd !== false) {
170
- const agentsMd = await loadAgentsMd(workspaceDir);
171
- if (agentsMd) {
172
- agentsMdSection = `
173
- ## Project Guidelines (from AGENTS.md)
174
-
175
- ${agentsMd}
176
- `;
177
- }
178
- }
179
- // Load bootstrap files if requested
180
- let bootstrapSection = "";
181
- if (options.includeBootstrap) {
182
- const bootstrapFiles = await loadBootstrapFiles(workspaceDir);
183
- if (Object.keys(bootstrapFiles).length > 0) {
184
- const parts = [];
185
- for (const [filename, content] of Object.entries(bootstrapFiles)) {
186
- parts.push(`### ${filename}\n\n${content}`);
187
- }
188
- bootstrapSection = `
189
- ## User Context
190
-
191
- ${parts.join("\n\n")}
192
- `;
193
- }
194
- }
195
- const rules = `# Nexus Workspace Rules
196
-
197
- This file is auto-generated by \`nexus cursor-rules\`. Regenerate with:
198
- \`\`\`bash
199
- nexus cursor-rules --output .cursor/rules
200
- \`\`\`
201
-
202
- ## Available Skills (${skills.length} total)
203
-
204
- The following skills are available in \`${skillsDir}\`. Read the full \`SKILL.md\` when you need detailed usage instructions.
205
-
206
- ${skillsSection}
207
- ## How to Use Skills
208
-
209
- 1. **When a user's request relates to a skill**, read the corresponding SKILL.md:
210
- \`\`\`bash
211
- cat ${path.join(skillsDir, "<skill>", "SKILL.md")}
212
- \`\`\`
213
-
214
- 2. **Skills provide CLI tools** - use bash to run them:
215
- \`\`\`bash
216
- eve db query --sql "SELECT * FROM messages LIMIT 5"
217
- imsg chats --limit 10 --json
218
- gog gmail search "is:unread" --max 5
219
- \`\`\`
220
-
221
- 3. **Most tools support \`--json\`** for structured output.
27
+ const skillsDir = options.skillsDir || WORKSPACE_SKILLS_DIR;
28
+ const agentsPath = path.join(NEXUS_ROOT, "AGENTS.md");
29
+ const rules = `# Nexus Workspace - Cursor Configuration
222
30
 
223
- 4. **Check if a tool is installed** before using:
224
- \`\`\`bash
225
- which eve imsg gog
226
- \`\`\`
227
- ${agentsMdSection}${bootstrapSection}
228
- ## Skill Directory Structure
31
+ This workspace uses Nexus. Follow the root \`AGENTS.md\` file for all protocols.
229
32
 
230
- - **Bundled**: \`${skillsDir}\` (${manifest.bundled.count} skills)
231
- - **Managed**: \`${managedSkillsDir}\` (${manifest.managed.count} skills)
232
- - **User**: \`${userSkillsDir}\` (${manifest.user.count} skills)
33
+ ## Cursor-Specific
233
34
 
234
- Precedence: User > Managed > Bundled
35
+ - Run \`nexus status\` first
36
+ - Cursor sessionStart hook injects identity context (see \`.cursor/hooks.json\`)
37
+ - Use the Shell tool for \`nexus\` commands
38
+ - Skill definitions live in \`${skillsDir}\`
39
+ - Skill state and usage logs live in \`${SKILLS_STATE_DIR}\`
40
+ - Read \`${agentsPath}\` for full instructions
235
41
  `;
236
42
  return `${rules.trim()}\n`;
237
43
  }
@@ -30,8 +30,8 @@ export async function identityCommand(opts, runtime = defaultRuntime) {
30
30
  },
31
31
  user: {
32
32
  name: identity.userName,
33
- profilePath: identity.userProfilePath,
34
- exists: identity.userProfileExists,
33
+ identityPath: identity.userIdentityPath,
34
+ exists: identity.userIdentityExists,
35
35
  },
36
36
  };
37
37
  if (opts.json) {
@@ -40,7 +40,7 @@ export async function identityCommand(opts, runtime = defaultRuntime) {
40
40
  }
41
41
  if (!opts.target || opts.target === "user") {
42
42
  runtime.log(`User: ${payload.user.name ?? "(unknown)"}`);
43
- runtime.log(` ${payload.user.profilePath}`);
43
+ runtime.log(` ${payload.user.identityPath}`);
44
44
  }
45
45
  if (!opts.target || opts.target === "agent") {
46
46
  runtime.log(`Agent: ${payload.agent.name ?? "(unknown)"} (${payload.agent.id})`);
@@ -3,13 +3,18 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { ensureAgentWorkspace } from "../agents/workspace.js";
6
- import { CONFIG_PATH_NEXUS, writeConfigFile } from "../config/config.js";
7
- import { resolveStateDir } from "../config/paths.js";
6
+ import { CONFIG_PATH_NEXUS, readConfigFileSnapshot, writeConfigFile, } from "../config/config.js";
7
+ import { resolveGatewayPort, resolveStateDir } from "../config/paths.js";
8
+ import { GATEWAY_LAUNCH_AGENT_LABEL } from "../daemon/constants.js";
9
+ import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
10
+ import { resolveGatewayService } from "../daemon/service.js";
8
11
  import { defaultRuntime } from "../runtime.js";
9
- import { MANAGED_SKILLS_DIR, NEXUS_ROOT } from "../utils.js";
12
+ import { NEXUS_ROOT, SKILLS_STATE_DIR, WORKSPACE_SKILLS_DIR, resolveUserPath, } from "../utils.js";
10
13
  import { scanCredentials } from "./credential.js";
14
+ import { writeCursorHooks } from "./cursor-hooks.js";
11
15
  import { writeCursorRules } from "./cursor-rules.js";
12
16
  import { copyBundledSkills, generateSkillManifest, writeSkillManifest, } from "./skills-manifest.js";
17
+ import { ensureSystemdUserLingerNonInteractive } from "./systemd-linger.js";
13
18
  const execFileAsync = promisify(execFile);
14
19
  /**
15
20
  * Checks if a directory is already a git repository.
@@ -78,9 +83,265 @@ build/
78
83
  await fs.writeFile(gitignorePath, gitignoreContent, "utf-8");
79
84
  runtime.log(`✓ Created .gitignore`);
80
85
  }
86
+ function shouldCopyIdentity(env) {
87
+ return (env.NEXUS_INIT_COPY_IDENTITY === "1" ||
88
+ env.NEXUS_TEST_MODE === "1" ||
89
+ env.VITEST === "true" ||
90
+ env.NODE_ENV === "test");
91
+ }
92
+ function resolveIdentitySourceDir(env) {
93
+ const explicit = env.NEXUS_INIT_IDENTITY_SRC?.trim() || env.NEXUS_IDENTITY_DIR?.trim();
94
+ if (explicit)
95
+ return resolveUserPath(explicit);
96
+ if (!shouldCopyIdentity(env))
97
+ return null;
98
+ return resolveUserPath("~") + "/nexus";
99
+ }
100
+ function stripQuotes(value) {
101
+ if ((value.startsWith('"') && value.endsWith('"')) ||
102
+ (value.startsWith("'") && value.endsWith("'"))) {
103
+ return value.slice(1, -1);
104
+ }
105
+ return value;
106
+ }
107
+ function readFrontmatterName(raw) {
108
+ const normalized = raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
109
+ if (!normalized.startsWith("---"))
110
+ return null;
111
+ const endIndex = normalized.indexOf("\n---", 3);
112
+ if (endIndex === -1)
113
+ return null;
114
+ const block = normalized.slice(4, endIndex);
115
+ for (const line of block.split("\n")) {
116
+ const match = line.match(/^name\s*:\s*(.+)$/i);
117
+ if (match?.[1])
118
+ return stripQuotes(match[1].trim());
119
+ }
120
+ return null;
121
+ }
122
+ function extractName(raw) {
123
+ const frontmatterName = readFrontmatterName(raw);
124
+ if (frontmatterName)
125
+ return frontmatterName;
126
+ const plainMatch = raw.match(/^[-*]?\s*Name\s*:\s*(.+)$/im);
127
+ if (plainMatch?.[1])
128
+ return plainMatch[1].trim();
129
+ const boldMatch = raw.match(/^\s*[-*]?\s*\*\*Name:\*\*\s*(.+)$/im);
130
+ if (boldMatch?.[1])
131
+ return boldMatch[1].trim();
132
+ return null;
133
+ }
134
+ function upsertFrontmatterName(raw, name) {
135
+ const normalized = raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
136
+ if (normalized.startsWith("---")) {
137
+ const endIndex = normalized.indexOf("\n---", 3);
138
+ if (endIndex !== -1) {
139
+ const block = normalized.slice(4, endIndex);
140
+ const hasName = block.split("\n").some((line) => /^name\s*:/i.test(line));
141
+ if (hasName)
142
+ return raw;
143
+ const updatedBlock = `name: ${name}\n${block}`;
144
+ return `---\n${updatedBlock}\n---${normalized.slice(endIndex + 4)}`;
145
+ }
146
+ }
147
+ return `---\nname: ${name}\n---\n\n${raw}`;
148
+ }
149
+ async function copyIdentityFile(params) {
150
+ try {
151
+ const raw = await fs.readFile(params.sourcePath, "utf-8");
152
+ let next = raw;
153
+ if (params.ensureNameLine) {
154
+ const name = extractName(raw);
155
+ if (name && !readFrontmatterName(raw)) {
156
+ next = upsertFrontmatterName(raw, name);
157
+ }
158
+ }
159
+ await fs.mkdir(path.dirname(params.destinationPath), { recursive: true });
160
+ await fs.writeFile(params.destinationPath, next, "utf-8");
161
+ return true;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ async function copyIdentityForInit(params) {
168
+ const sourceDir = resolveIdentitySourceDir(params.env);
169
+ if (!sourceDir)
170
+ return;
171
+ const resolvedSource = path.resolve(sourceDir);
172
+ const agentIdentityPath = params.workspace.identityPath;
173
+ const agentSoulPath = params.workspace.soulPath;
174
+ const agentMemoryPath = params.workspace.memoryPath;
175
+ const userIdentityPath = params.workspace.userPath;
176
+ if (!agentIdentityPath ||
177
+ !userIdentityPath ||
178
+ !agentSoulPath ||
179
+ !agentMemoryPath)
180
+ return;
181
+ const agentId = process.env.NEXUS_AGENT_ID?.trim() || "default";
182
+ const agentSources = [
183
+ path.join(resolvedSource, "IDENTITY.md"),
184
+ path.join(resolvedSource, "AGENT.md"),
185
+ path.join(resolvedSource, "agents", agentId, "IDENTITY.md"),
186
+ path.join(resolvedSource, "state", "agents", agentId, "IDENTITY.md"),
187
+ path.join(resolvedSource, "state", "identity", "AGENT.md"),
188
+ path.join(resolvedSource, "state", "identity", "IDENTITY.md"),
189
+ ];
190
+ const userSources = [
191
+ path.join(resolvedSource, "IDENTITY.md"),
192
+ path.join(resolvedSource, "PROFILE.md"),
193
+ path.join(resolvedSource, "USER.md"),
194
+ path.join(resolvedSource, "user", "IDENTITY.md"),
195
+ path.join(resolvedSource, "state", "user", "IDENTITY.md"),
196
+ path.join(resolvedSource, "state", "identity", "PROFILE.md"),
197
+ path.join(resolvedSource, "state", "identity", "USER.md"),
198
+ ];
199
+ const soulSources = [
200
+ path.join(resolvedSource, "SOUL.md"),
201
+ path.join(resolvedSource, "agents", agentId, "SOUL.md"),
202
+ path.join(resolvedSource, "state", "agents", agentId, "SOUL.md"),
203
+ path.join(resolvedSource, "state", "identity", "SOUL.md"),
204
+ ];
205
+ const memorySources = [
206
+ path.join(resolvedSource, "MEMORY.md"),
207
+ path.join(resolvedSource, "agents", agentId, "MEMORY.md"),
208
+ path.join(resolvedSource, "state", "agents", agentId, "MEMORY.md"),
209
+ path.join(resolvedSource, "state", "identity", "MEMORY.md"),
210
+ ];
211
+ const pickExisting = async (candidates) => {
212
+ for (const candidate of candidates) {
213
+ try {
214
+ await fs.access(candidate);
215
+ return candidate;
216
+ }
217
+ catch {
218
+ // continue
219
+ }
220
+ }
221
+ return null;
222
+ };
223
+ const agentSource = await pickExisting(agentSources);
224
+ const userSource = await pickExisting(userSources);
225
+ const wroteAgent = agentSource
226
+ ? await copyIdentityFile({
227
+ sourcePath: agentSource,
228
+ destinationPath: agentIdentityPath,
229
+ ensureNameLine: true,
230
+ })
231
+ : false;
232
+ const wroteUser = userSource
233
+ ? await copyIdentityFile({
234
+ sourcePath: userSource,
235
+ destinationPath: userIdentityPath,
236
+ ensureNameLine: true,
237
+ })
238
+ : false;
239
+ const soulSource = await pickExisting(soulSources);
240
+ const memorySource = await pickExisting(memorySources);
241
+ const wroteSoul = soulSource
242
+ ? await copyIdentityFile({
243
+ sourcePath: soulSource,
244
+ destinationPath: agentSoulPath,
245
+ })
246
+ : false;
247
+ const wroteMemory = memorySource
248
+ ? await copyIdentityFile({
249
+ sourcePath: memorySource,
250
+ destinationPath: agentMemoryPath,
251
+ })
252
+ : false;
253
+ if (wroteAgent || wroteUser || wroteSoul || wroteMemory) {
254
+ params.runtime.log(`✓ Copied identity files from ${resolvedSource} (bootstrap skipped)`);
255
+ }
256
+ }
257
+ function isGatewayCommandMatch(existing, expected) {
258
+ const existingArgs = existing.programArguments.map((arg) => arg.trim());
259
+ const expectedArgs = expected.programArguments.map((arg) => arg.trim());
260
+ if (existingArgs.length !== expectedArgs.length)
261
+ return false;
262
+ for (let i = 0; i < expectedArgs.length; i += 1) {
263
+ if (existingArgs[i] !== expectedArgs[i])
264
+ return false;
265
+ }
266
+ const existingDir = existing.workingDirectory?.trim() || "";
267
+ const expectedDir = expected.workingDirectory?.trim() || "";
268
+ return existingDir === expectedDir;
269
+ }
270
+ function hasGatewayEnvMismatch(existing, expected) {
271
+ const keysToCheck = Object.entries(expected).filter(([key]) => key !== "PATH");
272
+ if (!existing?.environment) {
273
+ return keysToCheck.some(([, value]) => Boolean(value?.trim()));
274
+ }
275
+ for (const [key, value] of keysToCheck) {
276
+ if (!value?.trim())
277
+ continue;
278
+ if (existing.environment[key] !== value) {
279
+ return true;
280
+ }
281
+ }
282
+ return false;
283
+ }
284
+ async function ensureGatewayDaemon(params) {
285
+ if (params.env.NEXUS_INIT_SKIP_DAEMON === "1")
286
+ return;
287
+ const runtime = params.runtime;
288
+ let nextConfig = params.configSnapshot.config ?? {};
289
+ const gateway = { ...nextConfig.gateway };
290
+ let updated = false;
291
+ if (!gateway.mode) {
292
+ gateway.mode = "local";
293
+ updated = true;
294
+ }
295
+ if (!gateway.bind) {
296
+ gateway.bind = "loopback";
297
+ updated = true;
298
+ }
299
+ const tailscaleMode = gateway.tailscale?.mode;
300
+ if (!tailscaleMode) {
301
+ gateway.tailscale = { ...gateway.tailscale, mode: "off" };
302
+ updated = true;
303
+ }
304
+ if (updated) {
305
+ nextConfig = { ...nextConfig, gateway };
306
+ await writeConfigFile(nextConfig);
307
+ runtime.log(`✓ Updated config file: ${CONFIG_PATH_NEXUS}`);
308
+ }
309
+ await ensureSystemdUserLingerNonInteractive({ runtime, env: params.env });
310
+ const service = resolveGatewayService();
311
+ const loaded = await service.isLoaded({ env: params.env });
312
+ const port = resolveGatewayPort(nextConfig, params.env);
313
+ const devMode = process.argv[1]?.includes("/src/") && process.argv[1]?.endsWith(".ts");
314
+ const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({ port, dev: devMode });
315
+ const environment = {
316
+ PATH: params.env.PATH,
317
+ NEXUS_ROOT: params.env.NEXUS_ROOT,
318
+ NEXUS_STATE_DIR: params.env.NEXUS_STATE_DIR,
319
+ NEXUS_AGENT_ID: params.env.NEXUS_AGENT_ID,
320
+ NEXUS_PROFILE: params.env.NEXUS_PROFILE,
321
+ NEXUS_LAUNCHD_LABEL: process.platform === "darwin" ? GATEWAY_LAUNCH_AGENT_LABEL : undefined,
322
+ };
323
+ const existing = loaded ? await service.readCommand(params.env) : null;
324
+ const shouldReinstall = !existing ||
325
+ !isGatewayCommandMatch(existing, { programArguments, workingDirectory }) ||
326
+ hasGatewayEnvMismatch(existing, environment);
327
+ if (!loaded || shouldReinstall) {
328
+ await service.install({
329
+ env: params.env,
330
+ stdout: process.stdout,
331
+ programArguments,
332
+ workingDirectory,
333
+ environment,
334
+ });
335
+ runtime.log(`✓ Installed ${service.label} gateway daemon`);
336
+ }
337
+ else {
338
+ await service.restart({ stdout: process.stdout });
339
+ runtime.log(`✓ Restarted ${service.label} gateway daemon`);
340
+ }
341
+ }
81
342
  /**
82
343
  * Creates the Nexus directory structure:
83
- * - ~/nexus/ (root with AGENTS.md and .cursor/rules)
344
+ * - ~/nexus/ (root with AGENTS.md, .cursor/rules, .cursor/hooks.json)
84
345
  * - ~/nexus/home/ (user workspace)
85
346
  * - ~/nexus/state/ (state directory)
86
347
  */
@@ -102,6 +363,7 @@ export async function initCommand(opts, runtime = defaultRuntime) {
102
363
  runtime.log(` - ${path.basename(workspace.toolsPath)}`);
103
364
  if (workspace.bootstrapPath)
104
365
  runtime.log(` - ${path.basename(workspace.bootstrapPath)}`);
366
+ await copyIdentityForInit({ env: process.env, runtime, workspace });
105
367
  // 2. Resolve user skills directory (may be created later by user)
106
368
  const userSkillsDir = path.join(workspace.dir, "skills");
107
369
  // 4. Create state directory (~/nexus/state)
@@ -127,13 +389,21 @@ export async function initCommand(opts, runtime = defaultRuntime) {
127
389
  };
128
390
  await fs.writeFile(workspaceMetaPath, JSON.stringify(workspaceMeta, null, 2), "utf-8");
129
391
  runtime.log(`✓ Created workspace metadata: ${workspaceMetaPath}`);
392
+ const configSnapshot = await readConfigFileSnapshot();
130
393
  // 5b. Ensure credential index
131
394
  await scanCredentials();
132
395
  runtime.log(`✓ Created credential index: ${path.join(stateDir, "credentials", "index.json")}`);
133
- // 6. Create main skills directory (state/skills) and copy bundled skills
134
- const skillsDir = MANAGED_SKILLS_DIR;
396
+ // 6. Create main skills directory (~/nexus/skills) and copy bundled skills
397
+ const skillsDir = WORKSPACE_SKILLS_DIR;
135
398
  await fs.mkdir(skillsDir, { recursive: true });
399
+ await fs.mkdir(path.join(skillsDir, "tools"), { recursive: true });
400
+ await fs.mkdir(path.join(skillsDir, "connectors"), { recursive: true });
401
+ await fs.mkdir(path.join(skillsDir, "guides"), { recursive: true });
402
+ await fs.mkdir(path.join(skillsDir, "managed"), { recursive: true });
136
403
  runtime.log(`✓ Created skills directory: ${skillsDir}`);
404
+ // 6b. Create skills state directory (state/skills)
405
+ await fs.mkdir(SKILLS_STATE_DIR, { recursive: true });
406
+ runtime.log(`✓ Created skills state directory: ${SKILLS_STATE_DIR}`);
137
407
  // Copy bundled skills from installed package to ~/nexus/skills/
138
408
  const copyResult = await copyBundledSkills(skillsDir);
139
409
  if (copyResult.copied.length > 0) {
@@ -159,6 +429,23 @@ export async function initCommand(opts, runtime = defaultRuntime) {
159
429
  includeBootstrap: false,
160
430
  });
161
431
  runtime.log(`✓ Created Cursor rules: ${cursorRulesPath}`);
432
+ // 8b. Write Cursor hooks (sessionStart bootstrap)
433
+ const cursorHooks = await writeCursorHooks({ workspaceDir: NEXUS_ROOT });
434
+ if (cursorHooks.hooksCreated || cursorHooks.scriptCreated) {
435
+ runtime.log(`✓ Created Cursor hooks: ${cursorHooks.hooksPath}`);
436
+ if (cursorHooks.scriptCreated) {
437
+ runtime.log(` - ${cursorHooks.scriptPath}`);
438
+ }
439
+ }
440
+ else {
441
+ runtime.log(`✓ Cursor hooks already present: ${cursorHooks.hooksPath}`);
442
+ }
443
+ // 9. Install and start gateway daemon
444
+ await ensureGatewayDaemon({
445
+ runtime,
446
+ env: process.env,
447
+ configSnapshot,
448
+ });
162
449
  runtime.log("\nNexus initialization complete!");
163
450
  runtime.log(`\nNext steps:\n - Run 'nexus status' to orient\n - Open ${NEXUS_ROOT} in your editor`);
164
451
  }
@@ -91,7 +91,7 @@ export async function maybeSetupIdentityFromEve(params) {
91
91
  whoami.emails?.length ? `Emails: ${whoami.emails.join(", ")}` : undefined,
92
92
  ].filter(Boolean);
93
93
  await prompter.note(summaryLines.join("\n"), "Detected identity (Eve)");
94
- // NOTE: We don't directly edit IDENTITY.md / PROFILE.md here yet; the bootstrap ritual
94
+ // NOTE: We don't directly edit agent/user IDENTITY.md here yet; the bootstrap ritual
95
95
  // should incorporate these values when it runs. Next step: write this into config
96
96
  // and/or workspace files once we agree on the exact schema + wording.
97
97
  }