@intent-systems/nexus 2026.1.5-5 → 2026.1.5-9
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 +11 -4
- package/dist/agents/skill-usage.js +4 -7
- package/dist/agents/skills-status.js +0 -3
- package/dist/agents/skills.js +18 -25
- 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 +27 -18
- package/dist/cli/skills-hub-cli.js +76 -45
- 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 +292 -6
- package/dist/commands/onboard-eve-identity.js +1 -1
- package/dist/commands/skills-manifest.js +88 -48
- 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 +171 -109
- package/docs/templates/AGENTS.md +139 -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
package/dist/cli/skills-cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { getSkillInfo,
|
|
3
|
+
import { getSkillInfo, verifySkill } from "../agents/skill-runner.js";
|
|
4
4
|
import { getAggregateStats, getSkillStats, hasSkillUsage, recordSkillUsage, } from "../agents/skill-usage.js";
|
|
5
5
|
import { getSkillMetadata, loadWorkspaceSkillEntries } from "../agents/skills.js";
|
|
6
6
|
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
|
7
7
|
import { DEFAULT_AGENT_WORKSPACE_DIR } from "../agents/workspace.js";
|
|
8
8
|
import { loadConfig } from "../config/config.js";
|
|
9
|
-
import {
|
|
9
|
+
import { NEXUS_ROOT, SKILLS_STATE_DIR, resolveUserPath } from "../utils.js";
|
|
10
10
|
const STATUS_ICON = {
|
|
11
11
|
active: "✅",
|
|
12
12
|
ready: "⭐",
|
|
@@ -47,6 +47,7 @@ export function registerSkillsCommand(program) {
|
|
|
47
47
|
.description("List all available skills")
|
|
48
48
|
.option("--json", "Output as JSON")
|
|
49
49
|
.option("--type <type>", "Filter by type (guide|tool|connector)")
|
|
50
|
+
.option("--status <status>", "Filter by status (active|ready|needs-setup|needs-install|unavailable)")
|
|
50
51
|
.option("--active", "Only show active skills")
|
|
51
52
|
.option("--ready", "Only show ready skills")
|
|
52
53
|
.option("--needs-setup", "Only show skills needing setup")
|
|
@@ -58,11 +59,9 @@ export function registerSkillsCommand(program) {
|
|
|
58
59
|
const workspaceDir = resolveUserPath(config.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
59
60
|
const entries = loadWorkspaceSkillEntries(workspaceDir, {
|
|
60
61
|
config,
|
|
61
|
-
managedSkillsDir: MANAGED_SKILLS_DIR,
|
|
62
62
|
});
|
|
63
63
|
const status = buildWorkspaceSkillStatus(workspaceDir, {
|
|
64
64
|
config,
|
|
65
|
-
managedSkillsDir: MANAGED_SKILLS_DIR,
|
|
66
65
|
entries,
|
|
67
66
|
});
|
|
68
67
|
const statusMap = new Map(status.skills.map((item) => [item.name.toLowerCase(), item]));
|
|
@@ -84,6 +83,25 @@ export function registerSkillsCommand(program) {
|
|
|
84
83
|
? String(opts.type).toLowerCase()
|
|
85
84
|
: undefined;
|
|
86
85
|
const statusFilters = new Set();
|
|
86
|
+
if (opts.status) {
|
|
87
|
+
const raw = String(opts.status)
|
|
88
|
+
.split(",")
|
|
89
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
for (const entry of raw) {
|
|
92
|
+
const normalized = entry.replace(/_/g, "-");
|
|
93
|
+
if (normalized === "needs-setup")
|
|
94
|
+
statusFilters.add("needs_setup");
|
|
95
|
+
else if (normalized === "needs-install")
|
|
96
|
+
statusFilters.add("needs_install");
|
|
97
|
+
else if (normalized === "active")
|
|
98
|
+
statusFilters.add("active");
|
|
99
|
+
else if (normalized === "ready")
|
|
100
|
+
statusFilters.add("ready");
|
|
101
|
+
else if (normalized === "unavailable")
|
|
102
|
+
statusFilters.add("unavailable");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
87
105
|
if (opts.active)
|
|
88
106
|
statusFilters.add("active");
|
|
89
107
|
if (opts.ready)
|
|
@@ -125,8 +143,11 @@ export function registerSkillsCommand(program) {
|
|
|
125
143
|
const icon = STATUS_ICON[item.status] ?? "•";
|
|
126
144
|
console.log(` ${icon} ${item.name}${desc}`);
|
|
127
145
|
}
|
|
128
|
-
|
|
129
|
-
console.log(
|
|
146
|
+
const skillsRoot = path.join(NEXUS_ROOT, "skills");
|
|
147
|
+
console.log(`\nSkills directory: ${skillsRoot}`);
|
|
148
|
+
console.log(`Skill types: ${path.join(skillsRoot, "tools")}, ${path.join(skillsRoot, "connectors")}, ${path.join(skillsRoot, "guides")}`);
|
|
149
|
+
console.log(`Skill state: ${SKILLS_STATE_DIR}`);
|
|
150
|
+
console.log(`User skills: ${path.join(workspaceDir, "skills")}\n`);
|
|
130
151
|
});
|
|
131
152
|
skill
|
|
132
153
|
.command("info <name>")
|
|
@@ -190,18 +211,6 @@ export function registerSkillsCommand(program) {
|
|
|
190
211
|
console.log(info.prompt.trimEnd());
|
|
191
212
|
recordSkillUsage(name, { ts: Date.now(), event: "use", ok: true });
|
|
192
213
|
});
|
|
193
|
-
skill
|
|
194
|
-
.command("run <name>")
|
|
195
|
-
.description("Run a skill command with arguments")
|
|
196
|
-
.allowUnknownOption()
|
|
197
|
-
.action(async (name, _opts, command) => {
|
|
198
|
-
const args = command.args.slice(1);
|
|
199
|
-
const result = await runSkill(name, args);
|
|
200
|
-
if (!result.ok) {
|
|
201
|
-
console.error(result.error);
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
214
|
skill
|
|
206
215
|
.command("verify <name>")
|
|
207
216
|
.description("Verify skill setup and dependencies")
|
|
@@ -12,7 +12,7 @@ import { generateSkillManifest, writeSkillManifest, } from "../commands/skills-m
|
|
|
12
12
|
import { loadConfig } from "../config/config.js";
|
|
13
13
|
import { listCredentialEntries, resolveCredentialValue, } from "../credentials/store.js";
|
|
14
14
|
import { runCommandWithTimeout } from "../process/exec.js";
|
|
15
|
-
import {
|
|
15
|
+
import { NEXUS_ROOT, resolveUserPath } from "../utils.js";
|
|
16
16
|
function getHubBaseUrl() {
|
|
17
17
|
return (process.env.NEXUS_HUB_URL ||
|
|
18
18
|
process.env.NEXUS_WEBSITE_URL ||
|
|
@@ -284,23 +284,27 @@ function dirNameToSlug(dirName) {
|
|
|
284
284
|
async function ensureDir(dir) {
|
|
285
285
|
await fs.mkdir(dir, { recursive: true });
|
|
286
286
|
}
|
|
287
|
-
async function
|
|
287
|
+
async function loadInstalledSkillSlugs(skillsRoot) {
|
|
288
288
|
const slugs = new Set();
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
for (const entry of entries) {
|
|
298
|
-
if (!entry.isDirectory())
|
|
299
|
-
continue;
|
|
300
|
-
const slug = dirNameToSlug(entry.name);
|
|
301
|
-
if (!slug)
|
|
289
|
+
const roots = ["tools", "connectors", "guides"].map((segment) => path.join(skillsRoot, segment));
|
|
290
|
+
for (const root of roots) {
|
|
291
|
+
let entries = [];
|
|
292
|
+
try {
|
|
293
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
302
296
|
continue;
|
|
303
|
-
|
|
297
|
+
}
|
|
298
|
+
for (const entry of entries) {
|
|
299
|
+
if (!entry.isDirectory())
|
|
300
|
+
continue;
|
|
301
|
+
if (entry.name.startsWith("."))
|
|
302
|
+
continue;
|
|
303
|
+
const slug = dirNameToSlug(entry.name);
|
|
304
|
+
if (!slug)
|
|
305
|
+
continue;
|
|
306
|
+
slugs.add(normalizeSkillKey(slug));
|
|
307
|
+
}
|
|
304
308
|
}
|
|
305
309
|
return slugs;
|
|
306
310
|
}
|
|
@@ -383,6 +387,32 @@ async function normalizeSkillRoot(destDir) {
|
|
|
383
387
|
}
|
|
384
388
|
await fs.rm(nested, { recursive: true, force: true });
|
|
385
389
|
}
|
|
390
|
+
async function resolveInstalledSkillType(skillDir) {
|
|
391
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
392
|
+
let content = "";
|
|
393
|
+
try {
|
|
394
|
+
content = await fs.readFile(skillFile, "utf-8");
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return "guide";
|
|
398
|
+
}
|
|
399
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
400
|
+
const rawType = typeof frontmatter.type === "string" ? frontmatter.type.trim().toLowerCase() : "";
|
|
401
|
+
if (rawType === "tool" || rawType === "connector" || rawType === "guide") {
|
|
402
|
+
return rawType;
|
|
403
|
+
}
|
|
404
|
+
const metadata = normalizeMetadata(frontmatter.metadata);
|
|
405
|
+
const metaType = metadata.nexus && typeof metadata.nexus === "object"
|
|
406
|
+
? metadata.nexus.type
|
|
407
|
+
: undefined;
|
|
408
|
+
if (typeof metaType === "string") {
|
|
409
|
+
const normalized = metaType.trim().toLowerCase();
|
|
410
|
+
if (normalized === "tool" || normalized === "connector" || normalized === "guide") {
|
|
411
|
+
return normalized;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return "guide";
|
|
415
|
+
}
|
|
386
416
|
async function resolveHubDependencyPlan(slug, installed, getDetails, onMissing) {
|
|
387
417
|
const plan = [];
|
|
388
418
|
const visiting = new Set();
|
|
@@ -562,28 +592,13 @@ function buildManifestPayload(input) {
|
|
|
562
592
|
}
|
|
563
593
|
async function installHubSkill(params) {
|
|
564
594
|
const dirName = slugToDirName(params.slug);
|
|
565
|
-
const destDir = path.join(params.managedDir, dirName);
|
|
566
|
-
try {
|
|
567
|
-
if (!params.force) {
|
|
568
|
-
await fs.access(destDir);
|
|
569
|
-
if (params.allowSkip) {
|
|
570
|
-
return { installed: false, destDir };
|
|
571
|
-
}
|
|
572
|
-
throw new Error(`Skill already installed at ${destDir}. Use --force to overwrite.`);
|
|
573
|
-
}
|
|
574
|
-
await fs.rm(destDir, { recursive: true, force: true });
|
|
575
|
-
}
|
|
576
|
-
catch (err) {
|
|
577
|
-
if (err.code !== "ENOENT") {
|
|
578
|
-
throw err;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
595
|
const download = await downloadSkillArtifact(params.slug);
|
|
582
596
|
if (!download.downloadUrl || !download.artifactSha256) {
|
|
583
597
|
throw new Error("Download URL missing from hub response");
|
|
584
598
|
}
|
|
585
599
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "nexus-skill-"));
|
|
586
600
|
const artifactPath = path.join(tmpDir, "artifact.bin");
|
|
601
|
+
const extractDir = path.join(tmpDir, "skill");
|
|
587
602
|
const downloadHeaders = {};
|
|
588
603
|
if (download.cloudToken) {
|
|
589
604
|
downloadHeaders.authorization = `Bearer ${download.cloudToken}`;
|
|
@@ -598,9 +613,29 @@ async function installHubSkill(params) {
|
|
|
598
613
|
if (hash !== download.artifactSha256) {
|
|
599
614
|
throw new Error(`Checksum mismatch (expected ${download.artifactSha256}, got ${hash})`);
|
|
600
615
|
}
|
|
601
|
-
await extractArchive(artifactPath,
|
|
602
|
-
await normalizeSkillRoot(
|
|
603
|
-
const
|
|
616
|
+
await extractArchive(artifactPath, extractDir);
|
|
617
|
+
await normalizeSkillRoot(extractDir);
|
|
618
|
+
const skillType = await resolveInstalledSkillType(extractDir);
|
|
619
|
+
const targetBase = path.join(params.skillsRoot, `${skillType}s`);
|
|
620
|
+
const destDir = path.join(targetBase, dirName);
|
|
621
|
+
try {
|
|
622
|
+
if (!params.force) {
|
|
623
|
+
await fs.access(destDir);
|
|
624
|
+
if (params.allowSkip) {
|
|
625
|
+
return { installed: false, destDir };
|
|
626
|
+
}
|
|
627
|
+
throw new Error(`Skill already installed at ${destDir}. Use --force to overwrite.`);
|
|
628
|
+
}
|
|
629
|
+
await fs.rm(destDir, { recursive: true, force: true });
|
|
630
|
+
}
|
|
631
|
+
catch (err) {
|
|
632
|
+
if (err.code !== "ENOENT") {
|
|
633
|
+
throw err;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
await ensureDir(targetBase);
|
|
637
|
+
await fs.rename(extractDir, destDir);
|
|
638
|
+
const manifest = await generateSkillManifest(params.skillsRoot, params.userSkillsDir);
|
|
604
639
|
await writeSkillManifest(manifest);
|
|
605
640
|
return { installed: true, destDir };
|
|
606
641
|
}
|
|
@@ -628,21 +663,19 @@ export function registerSkillsHubCommand(program) {
|
|
|
628
663
|
: 10;
|
|
629
664
|
const cfg = loadConfig();
|
|
630
665
|
const workspaceDir = resolveUserPath(cfg.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
631
|
-
const
|
|
666
|
+
const skillsRoot = path.join(NEXUS_ROOT, "skills");
|
|
632
667
|
const status = buildWorkspaceSkillStatus(workspaceDir, {
|
|
633
668
|
config: cfg,
|
|
634
|
-
managedSkillsDir,
|
|
635
669
|
});
|
|
636
670
|
const metadataEntries = loadWorkspaceSkillEntries(workspaceDir, {
|
|
637
671
|
config: cfg,
|
|
638
|
-
managedSkillsDir,
|
|
639
672
|
});
|
|
640
673
|
const metadataByName = new Map(metadataEntries.map((entry) => [
|
|
641
674
|
entry.skill.name,
|
|
642
675
|
getSkillMetadata(entry),
|
|
643
676
|
]));
|
|
644
677
|
const installedNames = new Set(metadataEntries.map((entry) => normalizeSkillKey(entry.skill.name)));
|
|
645
|
-
const installedSlugs = await
|
|
678
|
+
const installedSlugs = await loadInstalledSkillSlugs(skillsRoot);
|
|
646
679
|
const installedIndex = {
|
|
647
680
|
names: installedNames,
|
|
648
681
|
slugs: installedSlugs,
|
|
@@ -1049,14 +1082,12 @@ export function registerSkillsHubCommand(program) {
|
|
|
1049
1082
|
const cfg = loadConfig();
|
|
1050
1083
|
const workspaceDir = resolveUserPath(cfg.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
1051
1084
|
const userSkillsDir = path.join(workspaceDir, "skills");
|
|
1052
|
-
const
|
|
1053
|
-
const managedDir = path.join(managedSkillsDir, "managed");
|
|
1085
|
+
const skillsRoot = path.join(NEXUS_ROOT, "skills");
|
|
1054
1086
|
const metadataEntries = loadWorkspaceSkillEntries(workspaceDir, {
|
|
1055
1087
|
config: cfg,
|
|
1056
|
-
managedSkillsDir,
|
|
1057
1088
|
});
|
|
1058
1089
|
const installedNames = new Set(metadataEntries.map((entry) => normalizeSkillKey(entry.skill.name)));
|
|
1059
|
-
const installedSlugs = await
|
|
1090
|
+
const installedSlugs = await loadInstalledSkillSlugs(skillsRoot);
|
|
1060
1091
|
const installedIndex = {
|
|
1061
1092
|
names: installedNames,
|
|
1062
1093
|
slugs: installedSlugs,
|
|
@@ -1082,7 +1113,7 @@ export function registerSkillsHubCommand(program) {
|
|
|
1082
1113
|
for (const dep of plan) {
|
|
1083
1114
|
const result = await installHubSkill({
|
|
1084
1115
|
slug: dep,
|
|
1085
|
-
|
|
1116
|
+
skillsRoot,
|
|
1086
1117
|
userSkillsDir,
|
|
1087
1118
|
allowSkip: true,
|
|
1088
1119
|
});
|
|
@@ -1107,7 +1138,7 @@ export function registerSkillsHubCommand(program) {
|
|
|
1107
1138
|
}
|
|
1108
1139
|
const result = await installHubSkill({
|
|
1109
1140
|
slug,
|
|
1110
|
-
|
|
1141
|
+
skillsRoot,
|
|
1111
1142
|
userSkillsDir,
|
|
1112
1143
|
force: Boolean(opts.force),
|
|
1113
1144
|
});
|
|
@@ -107,15 +107,15 @@ export async function applyBootstrapPreset(params) {
|
|
|
107
107
|
const { workspaceDir, runtime } = params;
|
|
108
108
|
const stateDir = resolveStateDir();
|
|
109
109
|
const agentId = process.env.NEXUS_AGENT_ID?.trim() || "default";
|
|
110
|
-
const agentIdentityDir = path.join(stateDir, "agents", agentId
|
|
111
|
-
const userIdentityDir = path.join(stateDir, "user"
|
|
110
|
+
const agentIdentityDir = path.join(stateDir, "agents", agentId);
|
|
111
|
+
const userIdentityDir = path.join(stateDir, "user");
|
|
112
112
|
const bootstrapPath = resolveBootstrapPath(undefined, stateDir);
|
|
113
113
|
if (!(await fileExists(bootstrapPath)))
|
|
114
114
|
return;
|
|
115
115
|
await fs.mkdir(agentIdentityDir, { recursive: true });
|
|
116
116
|
await fs.mkdir(userIdentityDir, { recursive: true });
|
|
117
117
|
const identityPath = path.join(agentIdentityDir, "IDENTITY.md");
|
|
118
|
-
const userPath = path.join(userIdentityDir, "
|
|
118
|
+
const userPath = path.join(userIdentityDir, "IDENTITY.md");
|
|
119
119
|
const soulPath = path.join(agentIdentityDir, "SOUL.md");
|
|
120
120
|
const agentsPath = path.join(NEXUS_ROOT, "AGENTS.md");
|
|
121
121
|
const preset = resolvePreset(params.preset);
|
|
@@ -173,17 +173,27 @@ export async function applyBootstrapPreset(params) {
|
|
|
173
173
|
userName = "Test User";
|
|
174
174
|
if (!preferredAddress)
|
|
175
175
|
preferredAddress = firstName(userName) || "friend";
|
|
176
|
-
const identityMd =
|
|
176
|
+
const identityMd = `---
|
|
177
|
+
name: ${agentName}
|
|
178
|
+
emoji: ${preset.emoji}
|
|
179
|
+
creature: ${preset.creature}
|
|
180
|
+
vibe: ${preset.vibe}
|
|
181
|
+
---
|
|
182
|
+
# IDENTITY.md - Agent Identity
|
|
177
183
|
|
|
178
184
|
- Name: ${agentName}
|
|
179
185
|
- Creature: ${preset.creature}
|
|
180
186
|
- Vibe: ${preset.vibe}
|
|
181
187
|
- Emoji: ${preset.emoji}
|
|
182
188
|
`;
|
|
183
|
-
const userMd =
|
|
189
|
+
const userMd = `---
|
|
190
|
+
name: ${userName}
|
|
191
|
+
call: ${preferredAddress}
|
|
192
|
+
---
|
|
193
|
+
# IDENTITY.md - User Identity
|
|
184
194
|
|
|
185
195
|
- Name: ${userName}
|
|
186
|
-
-
|
|
196
|
+
- What to call them: ${preferredAddress}
|
|
187
197
|
- Pronouns (optional):
|
|
188
198
|
- Timezone (optional):
|
|
189
199
|
- Notes:
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { loadConfig, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
|
2
|
+
import { defaultRuntime } from "../runtime.js";
|
|
3
|
+
function getByPath(obj, key) {
|
|
4
|
+
const parts = key.split(".").filter(Boolean);
|
|
5
|
+
let current = obj;
|
|
6
|
+
for (const part of parts) {
|
|
7
|
+
if (!current || typeof current !== "object")
|
|
8
|
+
return undefined;
|
|
9
|
+
current = current[part];
|
|
10
|
+
}
|
|
11
|
+
return current;
|
|
12
|
+
}
|
|
13
|
+
function setByPath(obj, key, value) {
|
|
14
|
+
const parts = key.split(".").filter(Boolean);
|
|
15
|
+
let current = obj;
|
|
16
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
17
|
+
const part = parts[i];
|
|
18
|
+
if (i === parts.length - 1) {
|
|
19
|
+
current[part] = value;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
const next = current[part];
|
|
23
|
+
if (!next || typeof next !== "object") {
|
|
24
|
+
current[part] = {};
|
|
25
|
+
}
|
|
26
|
+
current = current[part];
|
|
27
|
+
}
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
function parseValue(raw) {
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
if (!trimmed)
|
|
33
|
+
return "";
|
|
34
|
+
if (trimmed === "true")
|
|
35
|
+
return true;
|
|
36
|
+
if (trimmed === "false")
|
|
37
|
+
return false;
|
|
38
|
+
if (!Number.isNaN(Number(trimmed)))
|
|
39
|
+
return Number(trimmed);
|
|
40
|
+
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
|
|
41
|
+
(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(trimmed);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return trimmed;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return trimmed;
|
|
50
|
+
}
|
|
51
|
+
export async function configListCommand(opts, runtime = defaultRuntime) {
|
|
52
|
+
const snapshot = await readConfigFileSnapshot();
|
|
53
|
+
const payload = snapshot.valid ? snapshot.parsed : {};
|
|
54
|
+
if (opts?.json) {
|
|
55
|
+
runtime.log(JSON.stringify(payload, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
runtime.log(JSON.stringify(payload, null, 2));
|
|
59
|
+
}
|
|
60
|
+
export async function configGetCommand(opts, runtime = defaultRuntime) {
|
|
61
|
+
const config = loadConfig();
|
|
62
|
+
const value = getByPath(config, opts.key);
|
|
63
|
+
if (opts.json) {
|
|
64
|
+
runtime.log(JSON.stringify({ key: opts.key, value }, null, 2));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
runtime.log(value);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
runtime.log(JSON.stringify(value ?? null, null, 2));
|
|
72
|
+
}
|
|
73
|
+
export async function configSetCommand(opts, runtime = defaultRuntime) {
|
|
74
|
+
const snapshot = await readConfigFileSnapshot();
|
|
75
|
+
const base = snapshot.valid && snapshot.parsed && typeof snapshot.parsed === "object"
|
|
76
|
+
? snapshot.parsed
|
|
77
|
+
: {};
|
|
78
|
+
const next = setByPath({ ...base }, opts.key, parseValue(opts.value));
|
|
79
|
+
await writeConfigFile(next);
|
|
80
|
+
if (opts.json) {
|
|
81
|
+
runtime.log(JSON.stringify({ key: opts.key, value: getByPath(next, opts.key) }, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
runtime.log(`Updated ${opts.key}`);
|
|
85
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveUserPath } from "../utils.js";
|
|
4
|
+
const DEFAULT_SCRIPT_NAME = "nexus-session-start.js";
|
|
5
|
+
const HOOK_SCRIPT = `#!/usr/bin/env node
|
|
6
|
+
const { execFile } = require("child_process");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const fsp = require("fs/promises");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
|
|
12
|
+
const MAX_IDENTITY_CHARS = 120000;
|
|
13
|
+
const MAX_MEMORY_CHARS = 120000;
|
|
14
|
+
const MAX_DAILY_CHARS = 40000;
|
|
15
|
+
const MAX_BOOTSTRAP_CHARS = 80000;
|
|
16
|
+
|
|
17
|
+
function readStdin() {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
let data = "";
|
|
20
|
+
process.stdin.setEncoding("utf8");
|
|
21
|
+
process.stdin.on("data", (chunk) => {
|
|
22
|
+
data += chunk;
|
|
23
|
+
});
|
|
24
|
+
process.stdin.on("end", () => resolve(data));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function safeJsonParse(raw) {
|
|
29
|
+
if (!raw || !raw.trim()) return null;
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(raw);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatDate(date) {
|
|
38
|
+
const year = date.getFullYear();
|
|
39
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
40
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
41
|
+
return \`\${year}-\${month}-\${day}\`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveWorkspaceRoot(payload) {
|
|
45
|
+
const roots = payload?.workspace_roots;
|
|
46
|
+
if (Array.isArray(roots) && roots.length > 0 && roots[0]) {
|
|
47
|
+
return roots[0];
|
|
48
|
+
}
|
|
49
|
+
if (process.env.NEXUS_ROOT?.trim()) return process.env.NEXUS_ROOT.trim();
|
|
50
|
+
const home = os.homedir();
|
|
51
|
+
if (home) return path.join(home, "nexus");
|
|
52
|
+
return process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function resolveStateDir(workspaceRoot) {
|
|
56
|
+
if (process.env.NEXUS_STATE_DIR?.trim()) {
|
|
57
|
+
return process.env.NEXUS_STATE_DIR.trim();
|
|
58
|
+
}
|
|
59
|
+
return path.join(workspaceRoot, "state");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function execFileAsync(command, args, options) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
execFile(command, args, options, (error, stdout, stderr) => {
|
|
65
|
+
resolve({ error, stdout, stderr });
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function runStatus(env, cwd) {
|
|
71
|
+
const result = await execFileAsync("nexus", ["status", "--json"], {
|
|
72
|
+
env,
|
|
73
|
+
cwd,
|
|
74
|
+
});
|
|
75
|
+
const parsed = safeJsonParse(result.stdout);
|
|
76
|
+
if (parsed) return parsed;
|
|
77
|
+
if (result.error && result.error.stdout) {
|
|
78
|
+
const fallback = safeJsonParse(result.error.stdout.toString());
|
|
79
|
+
if (fallback) return fallback;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function readFileSnippet(filePath, limit) {
|
|
85
|
+
if (!filePath) return null;
|
|
86
|
+
try {
|
|
87
|
+
const content = await fsp.readFile(filePath, "utf8");
|
|
88
|
+
if (!content.trim()) return null;
|
|
89
|
+
if (content.length <= limit) return content.trim();
|
|
90
|
+
const slice = content.slice(-limit).trimStart();
|
|
91
|
+
return \`\${slice}\\n\\n[truncated]\`;
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function addSection(sections, title, body) {
|
|
98
|
+
if (!body) return;
|
|
99
|
+
sections.push(\`## \${title}\\n\${body}\`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function main() {
|
|
103
|
+
const input = await readStdin();
|
|
104
|
+
const payload = safeJsonParse(input) || {};
|
|
105
|
+
const workspaceRoot = resolveWorkspaceRoot(payload);
|
|
106
|
+
const stateDir = resolveStateDir(workspaceRoot);
|
|
107
|
+
const env = {
|
|
108
|
+
...process.env,
|
|
109
|
+
NEXUS_ROOT: workspaceRoot,
|
|
110
|
+
NEXUS_STATE_DIR: stateDir,
|
|
111
|
+
};
|
|
112
|
+
const status = await runStatus(env, workspaceRoot);
|
|
113
|
+
const identity = status?.identity || null;
|
|
114
|
+
const agentId = identity?.agentId || process.env.NEXUS_AGENT_ID || "default";
|
|
115
|
+
const agentIdentityPath =
|
|
116
|
+
identity?.agentIdentityPath ||
|
|
117
|
+
path.join(stateDir, "agents", agentId, "IDENTITY.md");
|
|
118
|
+
const agentSoulPath =
|
|
119
|
+
identity?.agentSoulPath || path.join(stateDir, "agents", agentId, "SOUL.md");
|
|
120
|
+
const agentMemoryPath =
|
|
121
|
+
identity?.agentMemoryPath ||
|
|
122
|
+
path.join(stateDir, "agents", agentId, "MEMORY.md");
|
|
123
|
+
const userIdentityPath =
|
|
124
|
+
identity?.userIdentityPath || path.join(stateDir, "user", "IDENTITY.md");
|
|
125
|
+
const userProfilePath = path.join(stateDir, "user", "PROFILE.md");
|
|
126
|
+
const resolvedUserPath = fs.existsSync(userIdentityPath)
|
|
127
|
+
? userIdentityPath
|
|
128
|
+
: fs.existsSync(userProfilePath)
|
|
129
|
+
? userProfilePath
|
|
130
|
+
: userIdentityPath;
|
|
131
|
+
|
|
132
|
+
const sections = ["# Nexus Session Bootstrap"];
|
|
133
|
+
if (identity) {
|
|
134
|
+
const summary = [
|
|
135
|
+
\`Agent: \${identity.agentName || "(unknown)"} (\${identity.agentId})\`,
|
|
136
|
+
\`User: \${identity.userName || "(unknown)"}\`,
|
|
137
|
+
\`Agent ID: \${identity.agentId}\`,
|
|
138
|
+
];
|
|
139
|
+
addSection(sections, "Status", summary.join("\\n"));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const bootstrapPath =
|
|
143
|
+
status?.identity?.bootstrapPath ||
|
|
144
|
+
path.join(stateDir, "agents", "BOOTSTRAP.md");
|
|
145
|
+
const hasIdentity =
|
|
146
|
+
typeof identity?.hasIdentity === "boolean"
|
|
147
|
+
? identity.hasIdentity
|
|
148
|
+
: fs.existsSync(agentIdentityPath) && fs.existsSync(resolvedUserPath);
|
|
149
|
+
let bootstrapPrompt = status?.bootstrap?.prompt || null;
|
|
150
|
+
if (!bootstrapPrompt && !hasIdentity) {
|
|
151
|
+
bootstrapPrompt = await readFileSnippet(
|
|
152
|
+
bootstrapPath,
|
|
153
|
+
MAX_BOOTSTRAP_CHARS,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (bootstrapPrompt) {
|
|
157
|
+
addSection(sections, "Bootstrap Prompt", bootstrapPrompt.trim());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const agentIdentity = await readFileSnippet(
|
|
161
|
+
agentIdentityPath,
|
|
162
|
+
MAX_IDENTITY_CHARS,
|
|
163
|
+
);
|
|
164
|
+
const agentSoul = await readFileSnippet(agentSoulPath, MAX_IDENTITY_CHARS);
|
|
165
|
+
const agentMemory = await readFileSnippet(agentMemoryPath, MAX_MEMORY_CHARS);
|
|
166
|
+
const userIdentity = await readFileSnippet(
|
|
167
|
+
resolvedUserPath,
|
|
168
|
+
MAX_IDENTITY_CHARS,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
addSection(sections, "Agent Identity", agentIdentity);
|
|
172
|
+
addSection(sections, "Agent Soul", agentSoul);
|
|
173
|
+
addSection(sections, "Agent Memory", agentMemory);
|
|
174
|
+
addSection(sections, "User Identity", userIdentity);
|
|
175
|
+
|
|
176
|
+
const memoryDir = path.join(workspaceRoot, "home", "memory");
|
|
177
|
+
const today = formatDate(new Date());
|
|
178
|
+
const yesterday = formatDate(new Date(Date.now() - 86400000));
|
|
179
|
+
const todayLog = await readFileSnippet(
|
|
180
|
+
path.join(memoryDir, \`\${today}.md\`),
|
|
181
|
+
MAX_DAILY_CHARS,
|
|
182
|
+
);
|
|
183
|
+
const yesterdayLog = await readFileSnippet(
|
|
184
|
+
path.join(memoryDir, \`\${yesterday}.md\`),
|
|
185
|
+
MAX_DAILY_CHARS,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
addSection(sections, \`Daily Memory (\${today})\`, todayLog);
|
|
189
|
+
addSection(sections, \`Daily Memory (\${yesterday})\`, yesterdayLog);
|
|
190
|
+
|
|
191
|
+
const additional = sections.join("\\n\\n").trim();
|
|
192
|
+
const output = { continue: true, additional_context: additional };
|
|
193
|
+
const envOut = {};
|
|
194
|
+
if (!process.env.NEXUS_ROOT?.trim() && workspaceRoot) {
|
|
195
|
+
envOut.NEXUS_ROOT = workspaceRoot;
|
|
196
|
+
}
|
|
197
|
+
if (!process.env.NEXUS_STATE_DIR?.trim() && stateDir) {
|
|
198
|
+
envOut.NEXUS_STATE_DIR = stateDir;
|
|
199
|
+
}
|
|
200
|
+
if (Object.keys(envOut).length > 0) {
|
|
201
|
+
output.env = envOut;
|
|
202
|
+
}
|
|
203
|
+
process.stdout.write(JSON.stringify(output) + "\\n");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
main().catch(() => {
|
|
207
|
+
process.stdout.write(JSON.stringify({ continue: true }) + "\\n");
|
|
208
|
+
});
|
|
209
|
+
`;
|
|
210
|
+
async function writeFileIfMissing(filePath, content) {
|
|
211
|
+
try {
|
|
212
|
+
await fsp.writeFile(filePath, content, { encoding: "utf-8", flag: "wx" });
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
const anyErr = err;
|
|
217
|
+
if (anyErr.code === "EEXIST")
|
|
218
|
+
return false;
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export async function writeCursorHooks(options = {}) {
|
|
223
|
+
const workspaceDir = resolveUserPath(options.workspaceDir ?? process.cwd());
|
|
224
|
+
const hooksPath = options.hooksPath ?? path.join(workspaceDir, ".cursor", "hooks.json");
|
|
225
|
+
const scriptPath = options.scriptPath ??
|
|
226
|
+
path.join(workspaceDir, ".cursor", "hooks", DEFAULT_SCRIPT_NAME);
|
|
227
|
+
await fsp.mkdir(path.dirname(hooksPath), { recursive: true });
|
|
228
|
+
await fsp.mkdir(path.dirname(scriptPath), { recursive: true });
|
|
229
|
+
const hooksConfig = JSON.stringify({
|
|
230
|
+
version: 1,
|
|
231
|
+
hooks: {
|
|
232
|
+
sessionStart: [
|
|
233
|
+
{ command: `node .cursor/hooks/${DEFAULT_SCRIPT_NAME}` },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
}, null, 2);
|
|
237
|
+
const hooksCreated = await writeFileIfMissing(hooksPath, `${hooksConfig}\n`);
|
|
238
|
+
const scriptCreated = await writeFileIfMissing(scriptPath, HOOK_SCRIPT);
|
|
239
|
+
return { hooksPath, scriptPath, hooksCreated, scriptCreated };
|
|
240
|
+
}
|