@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.
- package/dist/agents/identity-state.js +45 -23
- package/dist/agents/skill-runner.js +12 -2
- package/dist/agents/skill-usage.js +4 -7
- package/dist/agents/skills-status.js +3 -2
- package/dist/agents/skills.js +20 -23
- package/dist/agents/workspace.js +32 -19
- package/dist/capabilities/detector.js +2 -2
- package/dist/cli/cloud-cli.js +58 -0
- package/dist/cli/credential-cli.js +77 -8
- package/dist/cli/program.js +24 -2
- package/dist/cli/skills-cli.js +30 -18
- package/dist/cli/skills-hub-cli.js +9 -7
- package/dist/commands/bootstrap-preset.js +16 -6
- package/dist/commands/config.js +85 -0
- package/dist/commands/cursor-hooks.js +240 -0
- package/dist/commands/cursor-rules.js +13 -207
- package/dist/commands/identity.js +3 -3
- package/dist/commands/init.js +293 -6
- package/dist/commands/onboard-eve-identity.js +1 -1
- package/dist/commands/skills-manifest.js +89 -29
- package/dist/commands/status.js +52 -50
- package/dist/daemon/launchd.js +14 -0
- package/dist/entry.js +0 -0
- package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud +0 -0
- package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud-rs +0 -0
- package/dist/utils.js +6 -2
- package/docs/AGENTS.default.md +1 -1
- package/docs/configuration.md +1 -1
- package/docs/feature-inventory/overview.md +2 -2
- package/docs/reference/templates/AGENTS.md +172 -109
- package/docs/templates/AGENTS.md +140 -199
- package/docs/templates/BOOTSTRAP.md +40 -20
- package/docs/templates/IDENTITY.md +6 -0
- package/docs/templates/USER.md +22 -2
- package/package.json +3 -1
- package/skills/{notion → connectors/notion}/SKILL.md +1 -1
- package/skills/{filesystem → guides/filesystem}/SKILL.md +1 -1
- package/skills/{onboarding → guides/onboarding}/SKILL.md +1 -1
- package/skills/{onboarding → guides/onboarding}/docs/CAPABILITY_TAXONOMY.md +5 -5
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR.md +8 -8
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_ONBOARDING.md +2 -2
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_SKILLS.md +26 -20
- package/skills/{onboarding → guides/onboarding}/docs/GOAL_STATE_ARCHITECTURE.md +38 -43
- package/skills/{onboarding → guides/onboarding}/docs/NEXUS_SYSTEM_OVERVIEW.md +4 -4
- package/skills/{onboarding → guides/onboarding}/docs/SKILLS_HUB_SPEC.md +1 -1
- package/skills/{onboarding → guides/onboarding}/docs/SKILLS_SPECIFICATION.md +8 -7
- package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_DESIGN.md +16 -16
- package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_PRD.md +10 -12
- package/skills/guides/onboarding/docs/canonical/00_CONFLICT_ANALYSIS.md +463 -0
- package/skills/guides/onboarding/docs/canonical/01_NEXUS_OVERVIEW.md +167 -0
- package/skills/guides/onboarding/docs/canonical/02_CLI_REFERENCE.md +404 -0
- package/skills/guides/onboarding/docs/canonical/03_STATE_ARCHITECTURE.md +357 -0
- package/skills/guides/onboarding/docs/canonical/04_SKILL_SPECIFICATION.md +393 -0
- package/skills/guides/onboarding/docs/canonical/05_CAPABILITY_TAXONOMY.md +298 -0
- package/skills/guides/onboarding/docs/canonical/06_CAPABILITIES_REFERENCE.md +207 -0
- package/skills/guides/onboarding/docs/canonical/07_AGENT_BINDINGS.md +85 -0
- package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/nexus-cloud.md +2 -2
- package/skills/{onboarding → guides/onboarding}/scripts/ralph/progress.txt +1 -1
- package/skills/{nexus-cloud → tools/nexus-cloud}/SKILL.md +2 -1
- package/skills/{nexus-cloud → tools/nexus-cloud}/docs/setup.md +1 -1
- package/docs/templates/PROFILE.md +0 -14
- /package/skills/{brave-search → connectors/brave-search}/SKILL.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/setup.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/troubleshooting.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/usage.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/scripts/content.mjs +0 -0
- /package/skills/{brave-search → connectors/brave-search}/scripts/search.mjs +0 -0
- /package/skills/{discord → connectors/discord}/SKILL.md +0 -0
- /package/skills/{gemini → connectors/gemini}/SKILL.md +0 -0
- /package/skills/{github → connectors/github}/SKILL.md +0 -0
- /package/skills/{github → connectors/github}/docs/setup.md +0 -0
- /package/skills/{github → connectors/github}/docs/troubleshooting.md +0 -0
- /package/skills/{google-oauth → connectors/google-oauth}/SKILL.md +0 -0
- /package/skills/{slack → connectors/slack}/SKILL.md +0 -0
- /package/skills/{telegram → connectors/telegram}/SKILL.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/pairing.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/setup.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/webhook.md +0 -0
- /package/skills/{wacli → connectors/wacli}/SKILL.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/auth.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/backup.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/troubleshooting.md +0 -0
- /package/skills/{browser-use-agent-sdk → guides/browser-use-agent-sdk}/SKILL.md +0 -0
- /package/skills/{json-render → guides/json-render}/SKILL.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/registry.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/App.css +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/App.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/data/nexus-core.json +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/index.css +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/registry.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/docs/nexus-state-demo.md +0 -0
- /package/skills/{json-render → guides/json-render}/docs/shadcn-preset.md +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/create-vite-demo.sh +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/package-lock.json +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/package.json +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/server.ts +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/CAPABILITIES.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_CREDENTIALS.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/DOCUMENTATION_OVERVIEW.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/ENTITY_MODEL.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/SKILL_INVENTORY.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/STATE_ARCHITECTURE.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/TROUBLESHOOTING.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/USER_JOURNEY.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/WOW_MOMENTS.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/agent-apple-id.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/1password.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/TEMPLATE.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/aix.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/bird.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/brave-search.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/comms.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/computer-use.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/cron-and-heartbeat.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/eve.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/github.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/gog.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/homebrew-prereqs.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/qmd.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/telegram.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/wacli.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/weather.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prd.json +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prompt.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.log +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.sh +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/setup-cursor-skills.sh +0 -0
- /package/skills/{1password → tools/1password}/SKILL.md +0 -0
- /package/skills/{1password → tools/1password}/docs/setup.md +0 -0
- /package/skills/{1password → tools/1password}/docs/troubleshooting.md +0 -0
- /package/skills/{1password → tools/1password}/references/cli-examples.md +0 -0
- /package/skills/{1password → tools/1password}/references/get-started.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/SKILL.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/browser-use-eval.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/first-tests.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/wordle-nyt-eval.js +0 -0
- /package/skills/{aix → tools/aix}/SKILL.md +0 -0
- /package/skills/{aix → tools/aix}/docs/embeddings.md +0 -0
- /package/skills/{aix → tools/aix}/docs/setup.md +0 -0
- /package/skills/{aix → tools/aix}/docs/troubleshooting.md +0 -0
- /package/skills/{aix → tools/aix}/references/sql.md +0 -0
- /package/skills/{apple-notes → tools/apple-notes}/SKILL.md +0 -0
- /package/skills/{apple-reminders → tools/apple-reminders}/SKILL.md +0 -0
- /package/skills/{bear-notes → tools/bear-notes}/SKILL.md +0 -0
- /package/skills/{bird → tools/bird}/SKILL.md +0 -0
- /package/skills/{bird → tools/bird}/docs/auth.md +0 -0
- /package/skills/{bird → tools/bird}/docs/troubleshooting.md +0 -0
- /package/skills/{blogwatcher → tools/blogwatcher}/SKILL.md +0 -0
- /package/skills/{blucli → tools/blucli}/SKILL.md +0 -0
- /package/skills/{camsnap → tools/camsnap}/SKILL.md +0 -0
- /package/skills/{clawdhub → tools/clawdhub}/SKILL.md +0 -0
- /package/skills/{coding-agent → tools/coding-agent}/SKILL.md +0 -0
- /package/skills/{comms → tools/comms}/SKILL.md +0 -0
- /package/skills/{comms → tools/comms}/docs/adapters.md +0 -0
- /package/skills/{comms → tools/comms}/docs/setup.md +0 -0
- /package/skills/{comms → tools/comms}/docs/troubleshooting.md +0 -0
- /package/skills/{comms → tools/comms}/references/schema.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/SKILL.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/open-interpreter.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/peekaboo.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/setup.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/troubleshooting.md +0 -0
- /package/skills/{eightctl → tools/eightctl}/SKILL.md +0 -0
- /package/skills/{eve → tools/eve}/SKILL.md +0 -0
- /package/skills/{eve → tools/eve}/docs/dual-account.md +0 -0
- /package/skills/{eve → tools/eve}/docs/intelligence.md +0 -0
- /package/skills/{eve → tools/eve}/docs/setup.md +0 -0
- /package/skills/{eve → tools/eve}/docs/troubleshooting.md +0 -0
- /package/skills/{eve → tools/eve}/scripts/setup-dual-account.sh +0 -0
- /package/skills/{food-order → tools/food-order}/SKILL.md +0 -0
- /package/skills/{gh → tools/gh}/SKILL.md +0 -0
- /package/skills/{gh → tools/gh}/docs/usage.md +0 -0
- /package/skills/{gifgrep → tools/gifgrep}/SKILL.md +0 -0
- /package/skills/{gog → tools/gog}/SKILL.md +0 -0
- /package/skills/{gog → tools/gog}/docs/portability.md +0 -0
- /package/skills/{gog → tools/gog}/docs/setup.md +0 -0
- /package/skills/{gog → tools/gog}/docs/troubleshooting.md +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/README.md +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/add_test_users.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts_manual.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/create_oauth_client.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/launch_cdp_chrome.sh +0 -0
- /package/skills/{goplaces → tools/goplaces}/SKILL.md +0 -0
- /package/skills/{imsg → tools/imsg}/SKILL.md +0 -0
- /package/skills/{local-places → tools/local-places}/SERVER_README.md +0 -0
- /package/skills/{local-places → tools/local-places}/SKILL.md +0 -0
- /package/skills/{local-places → tools/local-places}/pyproject.toml +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__init__.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/google_places.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/main.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/schemas.py +0 -0
- /package/skills/{mcporter → tools/mcporter}/SKILL.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/SKILL.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/references/codexbar-cli.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/scripts/model_usage.py +0 -0
- /package/skills/{nano-banana-pro → tools/nano-banana-pro}/SKILL.md +0 -0
- /package/skills/{nano-banana-pro → tools/nano-banana-pro}/scripts/generate_image.py +0 -0
- /package/skills/{nano-pdf → tools/nano-pdf}/SKILL.md +0 -0
- /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/security.md +0 -0
- /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/troubleshooting.md +0 -0
- /package/skills/{obsidian → tools/obsidian}/SKILL.md +0 -0
- /package/skills/{openai-image-gen → tools/openai-image-gen}/SKILL.md +0 -0
- /package/skills/{openai-image-gen → tools/openai-image-gen}/scripts/gen.py +0 -0
- /package/skills/{openai-whisper → tools/openai-whisper}/SKILL.md +0 -0
- /package/skills/{openai-whisper-api → tools/openai-whisper-api}/SKILL.md +0 -0
- /package/skills/{openai-whisper-api → tools/openai-whisper-api}/scripts/transcribe.sh +0 -0
- /package/skills/{openhue → tools/openhue}/SKILL.md +0 -0
- /package/skills/{oracle → tools/oracle}/SKILL.md +0 -0
- /package/skills/{ordercli → tools/ordercli}/SKILL.md +0 -0
- /package/skills/{peekaboo → tools/peekaboo}/SKILL.md +0 -0
- /package/skills/{qmd → tools/qmd}/SKILL.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/mcp.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/ollama.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/setup.md +0 -0
- /package/skills/{sag → tools/sag}/SKILL.md +0 -0
- /package/skills/{skill-cli-template → tools/skill-cli-template}/SKILL.md +0 -0
- /package/skills/{songsee → tools/songsee}/SKILL.md +0 -0
- /package/skills/{sonoscli → tools/sonoscli}/SKILL.md +0 -0
- /package/skills/{spotify-player → tools/spotify-player}/SKILL.md +0 -0
- /package/skills/{summarize → tools/summarize}/SKILL.md +0 -0
- /package/skills/{things-mac → tools/things-mac}/SKILL.md +0 -0
- /package/skills/{tmux → tools/tmux}/SKILL.md +0 -0
- /package/skills/{tmux → tools/tmux}/scripts/find-sessions.sh +0 -0
- /package/skills/{tmux → tools/tmux}/scripts/wait-for-text.sh +0 -0
- /package/skills/{trello → tools/trello}/SKILL.md +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/SKILL.md +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/auto-port.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-all.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-nexus.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-pi-ai.sh +0 -0
- /package/skills/{video-frames → tools/video-frames}/SKILL.md +0 -0
- /package/skills/{video-frames → tools/video-frames}/scripts/frame.sh +0 -0
- /package/skills/{weather → tools/weather}/SKILL.md +0 -0
- /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 {
|
|
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
|
|
157
|
-
const
|
|
158
|
-
const
|
|
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
|
-
|
|
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
|
-
-
|
|
231
|
-
- **Managed**: \`${managedSkillsDir}\` (${manifest.managed.count} skills)
|
|
232
|
-
- **User**: \`${userSkillsDir}\` (${manifest.user.count} skills)
|
|
33
|
+
## Cursor-Specific
|
|
233
34
|
|
|
234
|
-
|
|
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
|
-
|
|
34
|
-
exists: identity.
|
|
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.
|
|
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})`);
|
package/dist/commands/init.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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 (
|
|
134
|
-
const skillsDir =
|
|
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
|
|
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
|
}
|