@intent-systems/nexus 2026.1.5-4 → 2026.1.5-5
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/agent-id.js +41 -0
- package/dist/agents/auth-profiles.js +114 -25
- package/dist/agents/identity-state.js +79 -0
- package/dist/agents/model-auth.js +1 -0
- package/dist/agents/model-fallback.js +15 -9
- package/dist/agents/model-selection.js +1 -1
- package/dist/agents/models-config.js +17 -11
- package/dist/agents/pi-embedded-runner.js +101 -9
- package/dist/agents/sandbox.js +12 -3
- package/dist/agents/skill-runner.js +29 -4
- package/dist/agents/skill-usage.js +114 -11
- package/dist/agents/skills-status.js +4 -4
- package/dist/agents/skills.js +18 -7
- package/dist/agents/subagent-registry.js +25 -11
- package/dist/agents/system-prompt.js +16 -0
- package/dist/agents/tool-policy.js +19 -3
- package/dist/agents/tools/browser-tool.js +5 -2
- package/dist/agents/tools/image-tool.js +93 -8
- package/dist/agents/tools/sessions-announce-target.js +5 -1
- package/dist/agents/workspace.js +55 -46
- package/dist/auto-reply/command-detection.js +2 -1
- package/dist/auto-reply/reply/directive-handling.js +153 -28
- package/dist/auto-reply/reply/directives.js +17 -2
- package/dist/auto-reply/reply/model-selection.js +8 -3
- package/dist/auto-reply/reply/queue.js +2 -2
- package/dist/auto-reply/reply.js +1 -1
- package/dist/auto-reply/thinking.js +15 -0
- package/dist/browser/chrome.js +1 -1
- package/dist/browser/client.js +2 -0
- package/dist/browser/config.js +6 -2
- package/dist/browser/pw-tools-core.js +3 -0
- package/dist/browser/routes/agent.js +14 -0
- package/dist/canvas-host/server.js +1 -1
- package/dist/capabilities/detector.js +46 -15
- package/dist/capabilities/registry.js +2 -1
- package/dist/cli/cloud-cli.js +12 -7
- package/dist/cli/credential-cli.js +139 -17
- package/dist/cli/gateway-cli.js +1 -1
- package/dist/cli/log-cli.js +25 -0
- package/dist/cli/pairing-cli.js +1 -1
- package/dist/cli/program.js +58 -6
- package/dist/cli/run-main.js +1 -1
- package/dist/cli/skills-cli.js +144 -21
- package/dist/cli/skills-hub-cli.js +59 -29
- package/dist/cli/tool-connector-cli.js +99 -24
- package/dist/cli/upstream-sync-cli.js +253 -96
- package/dist/cli/usage-cli.js +14 -0
- package/dist/commands/auth-choice-options.js +6 -1
- package/dist/commands/auth-choice.js +157 -5
- package/dist/commands/bootstrap-preset.js +10 -6
- package/dist/commands/capabilities.js +33 -6
- package/dist/commands/claude-md.js +3 -2
- package/dist/commands/config-view.js +1 -1
- package/dist/commands/configure.js +4 -4
- package/dist/commands/credential.js +497 -36
- package/dist/commands/cursor-rules.js +39 -19
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/identity.js +28 -31
- package/dist/commands/init.js +15 -18
- package/dist/commands/log.js +134 -0
- package/dist/commands/models/fallbacks.js +1 -1
- package/dist/commands/models/image-fallbacks.js +1 -1
- package/dist/commands/models/list.js +1 -1
- package/dist/commands/models/scan.js +1 -1
- package/dist/commands/onboard-auth.js +27 -2
- package/dist/commands/onboard-eve-identity.js +7 -8
- package/dist/commands/onboard-non-interactive.js +4 -2
- package/dist/commands/onboard-quickstart.js +18 -11
- package/dist/commands/quest-state.js +271 -0
- package/dist/commands/quest.js +53 -13
- package/dist/commands/reset.js +1 -1
- package/dist/commands/sessions-ingest.js +5 -4
- package/dist/commands/setup.js +4 -2
- package/dist/commands/skills-manifest.js +2 -2
- package/dist/commands/status.js +179 -61
- package/dist/commands/suggestions.js +1 -1
- package/dist/commands/usage-tracking.js +32 -0
- package/dist/commands/usage-upload.js +6 -1
- package/dist/config/defaults.js +1 -3
- package/dist/config/includes.js +5 -7
- package/dist/config/io.js +88 -16
- package/dist/config/legacy.js +4 -2
- package/dist/config/paths.js +16 -0
- package/dist/config/sessions.js +9 -5
- package/dist/config/zod-schema.js +4 -3
- package/dist/control-plane/broker/broker.js +131 -78
- package/dist/control-plane/compaction.js +3 -5
- package/dist/control-plane/factory.js +2 -2
- package/dist/control-plane/index.js +2 -2
- package/dist/control-plane/odu/agents.js +28 -23
- package/dist/control-plane/odu/interaction-tools.js +62 -50
- package/dist/control-plane/odu/prompt-loader.js +8 -8
- package/dist/control-plane/odu/runtime.js +87 -75
- package/dist/control-plane/odu-control-plane.js +14 -12
- package/dist/control-plane/single-agent.js +13 -13
- package/dist/credentials/store.js +133 -7
- package/dist/gateway/server-browser.js +5 -4
- package/dist/gateway/server-methods/cron.js +11 -1
- package/dist/gateway/server.js +14 -7
- package/dist/infra/bonjour.js +1 -1
- package/dist/infra/event-log.js +8 -2
- package/dist/infra/path-env.js +1 -2
- package/dist/infra/provider-usage.auth.js +5 -3
- package/dist/infra/provider-usage.fetch.claude.js +16 -6
- package/dist/infra/provider-usage.fetch.minimax.js +8 -3
- package/dist/infra/provider-usage.js +9 -5
- package/dist/infra/restart.js +2 -2
- package/dist/infra/usage-settings.js +78 -0
- package/dist/infra/usage-suggestions.js +17 -5
- package/dist/infra/usage-upload.js +38 -1
- package/dist/infra/voicewake.js +2 -2
- package/dist/media/image-ops.js +3 -1
- package/dist/memory/index.js +2 -381
- package/dist/pairing/pairing-store.js +24 -0
- package/dist/providers/github-copilot-auth.js +1 -1
- package/dist/routing/resolve-route.js +6 -6
- package/dist/routing/session-key.js +3 -1
- package/dist/sessions/send-policy.js +5 -5
- package/dist/slack/monitor.js +22 -1
- package/dist/telegram/reaction-level.js +2 -1
- package/dist/utils.js +4 -3
- package/dist/wizard/onboarding.js +29 -7
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { logCommand } from "../commands/log.js";
|
|
2
|
+
import { defaultRuntime } from "../runtime.js";
|
|
3
|
+
export function registerLogCli(program) {
|
|
4
|
+
program
|
|
5
|
+
.command("log")
|
|
6
|
+
.description("Show Nexus event or skill usage logs")
|
|
7
|
+
.option("--json", "Output as JSON")
|
|
8
|
+
.option("--errors", "Only include error events")
|
|
9
|
+
.option("--since <time>", "Only include events after time (e.g. 24h)")
|
|
10
|
+
.option("--limit <n>", "Limit entries", (value) => Number.parseInt(value, 10))
|
|
11
|
+
.option("--skill <name>", "Show usage log for a skill")
|
|
12
|
+
.option("--source <source>", "Filter by event source")
|
|
13
|
+
.option("--command <path>", "Filter by command path")
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
await logCommand({
|
|
16
|
+
json: Boolean(opts.json),
|
|
17
|
+
errors: Boolean(opts.errors),
|
|
18
|
+
since: typeof opts.since === "string" ? opts.since : undefined,
|
|
19
|
+
limit: Number.isFinite(opts.limit) ? opts.limit : undefined,
|
|
20
|
+
skill: typeof opts.skill === "string" ? opts.skill : undefined,
|
|
21
|
+
source: typeof opts.source === "string" ? opts.source : undefined,
|
|
22
|
+
command: typeof opts.command === "string" ? opts.command : undefined,
|
|
23
|
+
}, defaultRuntime);
|
|
24
|
+
});
|
|
25
|
+
}
|
package/dist/cli/pairing-cli.js
CHANGED
|
@@ -15,7 +15,7 @@ const PROVIDERS = [
|
|
|
15
15
|
"whatsapp",
|
|
16
16
|
];
|
|
17
17
|
function parseProvider(raw) {
|
|
18
|
-
const value = String(raw
|
|
18
|
+
const value = (typeof raw === "string" || typeof raw === "number" ? String(raw) : "")
|
|
19
19
|
.trim()
|
|
20
20
|
.toLowerCase();
|
|
21
21
|
if (PROVIDERS.includes(value))
|
package/dist/cli/program.js
CHANGED
|
@@ -4,22 +4,24 @@ import { configViewCommand } from "../commands/config-view.js";
|
|
|
4
4
|
import { identityCommand } from "../commands/identity.js";
|
|
5
5
|
import { initCommand } from "../commands/init.js";
|
|
6
6
|
import { questCommand } from "../commands/quest.js";
|
|
7
|
-
import { suggestionsCommand } from "../commands/suggestions.js";
|
|
8
7
|
import { statusCommand } from "../commands/status.js";
|
|
8
|
+
import { suggestionsCommand } from "../commands/suggestions.js";
|
|
9
9
|
import { updateCommand } from "../commands/update.js";
|
|
10
10
|
import { isNixMode, migrateLegacyConfig, readConfigFileSnapshot, writeConfigFile, } from "../config/config.js";
|
|
11
11
|
import { danger } from "../globals.js";
|
|
12
|
+
import { recordCliCommandFinish, recordCliCommandStart, } from "../infra/event-log.js";
|
|
12
13
|
import { defaultRuntime } from "../runtime.js";
|
|
13
14
|
import { VERSION } from "../version.js";
|
|
14
|
-
import { recordCliCommandFinish, recordCliCommandStart } from "../infra/event-log.js";
|
|
15
15
|
import { registerCloudCommand } from "./cloud-cli.js";
|
|
16
16
|
import { registerCredentialCli } from "./credential-cli.js";
|
|
17
17
|
import { registerDnsCli } from "./dns-cli.js";
|
|
18
18
|
import { registerGatewayCli } from "./gateway-cli.js";
|
|
19
|
+
import { registerLogCli } from "./log-cli.js";
|
|
19
20
|
import { registerSkillsCommand } from "./skills-cli.js";
|
|
20
21
|
import { registerSkillsHubCommand } from "./skills-hub-cli.js";
|
|
21
22
|
import { registerToolConnectorCli } from "./tool-connector-cli.js";
|
|
22
23
|
import { registerUsageCli } from "./usage-cli.js";
|
|
24
|
+
import { resolveIdentitySnapshot } from "../agents/identity-state.js";
|
|
23
25
|
function buildCommandPath(cmd) {
|
|
24
26
|
const parts = [];
|
|
25
27
|
let current = cmd;
|
|
@@ -51,6 +53,19 @@ export function buildProgram() {
|
|
|
51
53
|
});
|
|
52
54
|
if (actionCommand.name() === "init")
|
|
53
55
|
return;
|
|
56
|
+
const skipIdentityCheck = process.env.NODE_ENV === "test" || Boolean(process.env.VITEST);
|
|
57
|
+
if (!skipIdentityCheck && actionCommand.name() !== "status") {
|
|
58
|
+
const identity = resolveIdentitySnapshot();
|
|
59
|
+
if (!identity.ok) {
|
|
60
|
+
defaultRuntime.error("Multiple agents detected in state/agents.");
|
|
61
|
+
defaultRuntime.error(`Set NEXUS_AGENT_ID to one of: ${identity.agentOptions.join(", ")}`);
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
if (!identity.snapshot.hasIdentity) {
|
|
65
|
+
await statusCommand({ brief: true }, defaultRuntime);
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
54
69
|
const snapshot = await readConfigFileSnapshot();
|
|
55
70
|
if (snapshot.legacyIssues.length === 0)
|
|
56
71
|
return;
|
|
@@ -79,41 +94,77 @@ export function buildProgram() {
|
|
|
79
94
|
.option("--brief", "Compact output")
|
|
80
95
|
.option("--capabilities", "Focus on capabilities")
|
|
81
96
|
.option("--credentials", "Focus on credentials")
|
|
97
|
+
.option("--usage", "Focus on usage statistics")
|
|
98
|
+
.option("--quiet", "Minimal output, exit codes only")
|
|
82
99
|
.action(async (opts) => {
|
|
83
100
|
await statusCommand({
|
|
84
101
|
json: Boolean(opts.json),
|
|
85
102
|
brief: Boolean(opts.brief),
|
|
86
103
|
capabilities: Boolean(opts.capabilities),
|
|
87
104
|
credentials: Boolean(opts.credentials),
|
|
105
|
+
usage: Boolean(opts.usage),
|
|
106
|
+
quiet: Boolean(opts.quiet),
|
|
88
107
|
}, defaultRuntime);
|
|
89
108
|
});
|
|
90
109
|
program
|
|
91
110
|
.command("capabilities")
|
|
92
111
|
.description("Show full capabilities map")
|
|
93
112
|
.option("--json", "Output as JSON")
|
|
113
|
+
.option("--category <name>", "Filter to a category")
|
|
114
|
+
.option("--status <status>", "Filter by status")
|
|
115
|
+
.option("--compact", "Compact output")
|
|
94
116
|
.action(async (opts) => {
|
|
95
|
-
await capabilitiesCommand({
|
|
117
|
+
await capabilitiesCommand({
|
|
118
|
+
json: Boolean(opts.json),
|
|
119
|
+
category: opts.category ? String(opts.category) : undefined,
|
|
120
|
+
status: opts.status ? String(opts.status) : undefined,
|
|
121
|
+
compact: Boolean(opts.compact),
|
|
122
|
+
}, defaultRuntime);
|
|
96
123
|
});
|
|
97
124
|
program
|
|
98
125
|
.command("map")
|
|
99
126
|
.description("Alias for capabilities")
|
|
100
127
|
.option("--json", "Output as JSON")
|
|
128
|
+
.option("--category <name>", "Filter to a category")
|
|
129
|
+
.option("--status <status>", "Filter by status")
|
|
130
|
+
.option("--compact", "Compact output")
|
|
101
131
|
.action(async (opts) => {
|
|
102
|
-
await capabilitiesCommand({
|
|
132
|
+
await capabilitiesCommand({
|
|
133
|
+
json: Boolean(opts.json),
|
|
134
|
+
category: opts.category ? String(opts.category) : undefined,
|
|
135
|
+
status: opts.status ? String(opts.status) : undefined,
|
|
136
|
+
compact: Boolean(opts.compact),
|
|
137
|
+
}, defaultRuntime);
|
|
103
138
|
});
|
|
104
139
|
program
|
|
105
140
|
.command("quest")
|
|
106
141
|
.description("Show onboarding quests")
|
|
107
142
|
.option("--json", "Output as JSON")
|
|
143
|
+
.option("--list", "List all quests")
|
|
144
|
+
.option("--progress", "Show quest progress summary")
|
|
145
|
+
.option("--start <quest>", "Mark a quest as started")
|
|
146
|
+
.option("--secrets", "Include secret quests")
|
|
147
|
+
.option("--power-path", "Only show power-path quests")
|
|
148
|
+
.option("--quick-wins", "Only show quick wins")
|
|
108
149
|
.action(async (opts) => {
|
|
109
|
-
await questCommand({
|
|
150
|
+
await questCommand({
|
|
151
|
+
json: Boolean(opts.json),
|
|
152
|
+
list: Boolean(opts.list),
|
|
153
|
+
progress: Boolean(opts.progress),
|
|
154
|
+
start: opts.start ? String(opts.start) : undefined,
|
|
155
|
+
secrets: Boolean(opts.secrets),
|
|
156
|
+
powerPath: Boolean(opts.powerPath),
|
|
157
|
+
quickWins: Boolean(opts.quickWins),
|
|
158
|
+
}, defaultRuntime);
|
|
110
159
|
});
|
|
111
160
|
program
|
|
112
161
|
.command("identity [target]")
|
|
113
162
|
.description("Show identity files")
|
|
114
163
|
.option("--json", "Output as JSON")
|
|
115
164
|
.action(async (target, opts) => {
|
|
116
|
-
const normalized = target && (target === "user" || target === "agent")
|
|
165
|
+
const normalized = target && (target === "user" || target === "agent")
|
|
166
|
+
? target
|
|
167
|
+
: undefined;
|
|
117
168
|
await identityCommand({ target: normalized, json: Boolean(opts.json) }, defaultRuntime);
|
|
118
169
|
});
|
|
119
170
|
program
|
|
@@ -156,6 +207,7 @@ export function buildProgram() {
|
|
|
156
207
|
registerCredentialCli(program);
|
|
157
208
|
registerDnsCli(program);
|
|
158
209
|
registerGatewayCli(program);
|
|
210
|
+
registerLogCli(program);
|
|
159
211
|
registerToolConnectorCli(program);
|
|
160
212
|
registerUsageCli(program);
|
|
161
213
|
return program;
|
package/dist/cli/run-main.js
CHANGED
|
@@ -2,10 +2,10 @@ import process from "node:process";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { loadDotEnv } from "../infra/dotenv.js";
|
|
4
4
|
import { normalizeEnv } from "../infra/env.js";
|
|
5
|
+
import { initCliEventLogSession, recordCliCommandFailure, recordCliSessionEnd, registerAgentEventLogListener, } from "../infra/event-log.js";
|
|
5
6
|
import { isMainModule } from "../infra/is-main.js";
|
|
6
7
|
import { ensureNexusCliOnPath } from "../infra/path-env.js";
|
|
7
8
|
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
|
|
8
|
-
import { initCliEventLogSession, recordCliCommandFailure, recordCliSessionEnd, registerAgentEventLogListener, } from "../infra/event-log.js";
|
|
9
9
|
import { enableConsoleCapture } from "../logging.js";
|
|
10
10
|
export async function runCli(argv = process.argv) {
|
|
11
11
|
loadDotEnv({ quiet: true });
|
package/dist/cli/skills-cli.js
CHANGED
|
@@ -1,36 +1,129 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { getSkillInfo, runSkill, verifySkill } from "../agents/skill-runner.js";
|
|
4
|
-
import { getAggregateStats, getSkillStats } from "../agents/skill-usage.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { getAggregateStats, getSkillStats, hasSkillUsage, recordSkillUsage, } from "../agents/skill-usage.js";
|
|
5
|
+
import { getSkillMetadata, loadWorkspaceSkillEntries } from "../agents/skills.js";
|
|
6
|
+
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
|
7
|
+
import { DEFAULT_AGENT_WORKSPACE_DIR } from "../agents/workspace.js";
|
|
8
|
+
import { loadConfig } from "../config/config.js";
|
|
9
|
+
import { MANAGED_SKILLS_DIR, NEXUS_ROOT, resolveUserPath } from "../utils.js";
|
|
10
|
+
const STATUS_ICON = {
|
|
11
|
+
active: "✅",
|
|
12
|
+
ready: "⭐",
|
|
13
|
+
needs_setup: "🔧",
|
|
14
|
+
needs_install: "📥",
|
|
15
|
+
unavailable: "⛔",
|
|
16
|
+
};
|
|
17
|
+
function resolveSkillType(entry) {
|
|
18
|
+
const metadata = getSkillMetadata(entry);
|
|
19
|
+
if (metadata?.type)
|
|
20
|
+
return metadata.type;
|
|
21
|
+
const requires = metadata?.requires;
|
|
22
|
+
if (requires?.env?.length || requires?.config?.length)
|
|
23
|
+
return "connector";
|
|
24
|
+
if (requires?.bins?.length || requires?.anyBins?.length)
|
|
25
|
+
return "tool";
|
|
26
|
+
return "guide";
|
|
27
|
+
}
|
|
28
|
+
function resolveSkillStatus(statusEntry) {
|
|
29
|
+
if (statusEntry.disabled || statusEntry.blockedByAllowlist) {
|
|
30
|
+
return "unavailable";
|
|
31
|
+
}
|
|
32
|
+
if (statusEntry.missing.os.length > 0)
|
|
33
|
+
return "unavailable";
|
|
34
|
+
if (statusEntry.missing.bins.length > 0)
|
|
35
|
+
return "needs_install";
|
|
36
|
+
if (statusEntry.missing.env.length > 0 || statusEntry.missing.config.length > 0) {
|
|
37
|
+
return "needs_setup";
|
|
38
|
+
}
|
|
39
|
+
if (hasSkillUsage(statusEntry.name))
|
|
40
|
+
return "active";
|
|
41
|
+
return "ready";
|
|
42
|
+
}
|
|
7
43
|
export function registerSkillsCommand(program) {
|
|
8
44
|
const skill = program.command("skill").description("Use and inspect skills");
|
|
9
45
|
skill
|
|
10
46
|
.command("list")
|
|
11
47
|
.description("List all available skills")
|
|
12
48
|
.option("--json", "Output as JSON")
|
|
49
|
+
.option("--type <type>", "Filter by type (guide|tool|connector)")
|
|
50
|
+
.option("--active", "Only show active skills")
|
|
51
|
+
.option("--ready", "Only show ready skills")
|
|
52
|
+
.option("--needs-setup", "Only show skills needing setup")
|
|
53
|
+
.option("--needs-install", "Only show skills needing install")
|
|
54
|
+
.option("--unavailable", "Only show unavailable skills")
|
|
55
|
+
.option("--all", "Include unavailable skills")
|
|
13
56
|
.action(async (opts) => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
57
|
+
const config = loadConfig();
|
|
58
|
+
const workspaceDir = resolveUserPath(config.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
59
|
+
const entries = loadWorkspaceSkillEntries(workspaceDir, {
|
|
60
|
+
config,
|
|
61
|
+
managedSkillsDir: MANAGED_SKILLS_DIR,
|
|
62
|
+
});
|
|
63
|
+
const status = buildWorkspaceSkillStatus(workspaceDir, {
|
|
64
|
+
config,
|
|
65
|
+
managedSkillsDir: MANAGED_SKILLS_DIR,
|
|
66
|
+
entries,
|
|
67
|
+
});
|
|
68
|
+
const statusMap = new Map(status.skills.map((item) => [item.name.toLowerCase(), item]));
|
|
69
|
+
const items = entries.map((entry) => {
|
|
70
|
+
const statusEntry = statusMap.get(entry.skill.name.toLowerCase());
|
|
71
|
+
const statusValue = statusEntry
|
|
72
|
+
? resolveSkillStatus(statusEntry)
|
|
73
|
+
: "unavailable";
|
|
74
|
+
return {
|
|
75
|
+
name: entry.skill.name,
|
|
76
|
+
description: entry.skill.description,
|
|
77
|
+
type: resolveSkillType(entry),
|
|
78
|
+
status: statusValue,
|
|
79
|
+
source: entry.skill.source,
|
|
80
|
+
filePath: entry.skill.filePath,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
const typeFilter = opts.type
|
|
84
|
+
? String(opts.type).toLowerCase()
|
|
85
|
+
: undefined;
|
|
86
|
+
const statusFilters = new Set();
|
|
87
|
+
if (opts.active)
|
|
88
|
+
statusFilters.add("active");
|
|
89
|
+
if (opts.ready)
|
|
90
|
+
statusFilters.add("ready");
|
|
91
|
+
if (opts.needsSetup)
|
|
92
|
+
statusFilters.add("needs_setup");
|
|
93
|
+
if (opts.needsInstall)
|
|
94
|
+
statusFilters.add("needs_install");
|
|
95
|
+
if (opts.unavailable)
|
|
96
|
+
statusFilters.add("unavailable");
|
|
97
|
+
const hasStatusFilter = statusFilters.size > 0;
|
|
98
|
+
const filtered = items.filter((item) => {
|
|
99
|
+
if (typeFilter && item.type !== typeFilter)
|
|
100
|
+
return false;
|
|
101
|
+
if (hasStatusFilter && !statusFilters.has(item.status))
|
|
102
|
+
return false;
|
|
103
|
+
if (!hasStatusFilter && !opts.all && item.status === "unavailable")
|
|
104
|
+
return false;
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
19
107
|
if (opts.json) {
|
|
20
|
-
console.log(JSON.stringify(
|
|
108
|
+
console.log(JSON.stringify({
|
|
109
|
+
workspace: workspaceDir,
|
|
110
|
+
total: items.length,
|
|
111
|
+
filtered: filtered.length,
|
|
112
|
+
skills: filtered,
|
|
113
|
+
}, null, 2));
|
|
21
114
|
return;
|
|
22
115
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.log("No skills installed.");
|
|
116
|
+
if (filtered.length === 0) {
|
|
117
|
+
console.log("No skills matched.");
|
|
26
118
|
return;
|
|
27
119
|
}
|
|
28
|
-
console.log(`\n📚 Nexus Skills (${
|
|
29
|
-
for (const
|
|
30
|
-
const desc =
|
|
31
|
-
? ` - ${
|
|
120
|
+
console.log(`\n📚 Nexus Skills (${filtered.length} shown)\n`);
|
|
121
|
+
for (const item of filtered) {
|
|
122
|
+
const desc = item.description
|
|
123
|
+
? ` - ${item.description.slice(0, 60)}${item.description.length > 60 ? "..." : ""}`
|
|
32
124
|
: "";
|
|
33
|
-
|
|
125
|
+
const icon = STATUS_ICON[item.status] ?? "•";
|
|
126
|
+
console.log(` ${icon} ${item.name}${desc}`);
|
|
34
127
|
}
|
|
35
128
|
console.log(`\nSkills directory: ${MANAGED_SKILLS_DIR}`);
|
|
36
129
|
console.log(`User skills: ${path.join(NEXUS_ROOT, "home", "skills")}\n`);
|
|
@@ -42,6 +135,7 @@ export function registerSkillsCommand(program) {
|
|
|
42
135
|
.option("--prompt", "Include the full SKILL.md prompt")
|
|
43
136
|
.action(async (name, opts) => {
|
|
44
137
|
const info = getSkillInfo(name);
|
|
138
|
+
const stats = await getSkillStats(name);
|
|
45
139
|
if (!info.exists) {
|
|
46
140
|
console.error(`Skill not found: ${name}`);
|
|
47
141
|
console.error(`\nRun 'nexus skill list' to see available skills.`);
|
|
@@ -49,7 +143,8 @@ export function registerSkillsCommand(program) {
|
|
|
49
143
|
}
|
|
50
144
|
if (opts.json) {
|
|
51
145
|
const output = opts.prompt ? info : { ...info, prompt: undefined };
|
|
52
|
-
|
|
146
|
+
const payload = stats ? { ...output, usage: stats } : output;
|
|
147
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
53
148
|
return;
|
|
54
149
|
}
|
|
55
150
|
console.log(`\n${chalk.bold.cyan("Skill:")} ${info.name}`);
|
|
@@ -63,12 +158,16 @@ export function registerSkillsCommand(program) {
|
|
|
63
158
|
const statusColor = info.configured ? chalk.green : chalk.yellow;
|
|
64
159
|
const statusIcon = info.configured ? "✓" : "○";
|
|
65
160
|
console.log(`${chalk.bold("Status:")} ${statusColor(`${statusIcon} ${info.configured ? "Configured" : "Not configured"}`)}`);
|
|
66
|
-
if (info.lastUsed) {
|
|
67
|
-
console.log(`${chalk.bold("Last used:")} ${info.lastUsed}`);
|
|
161
|
+
if (stats?.lastUsed ?? info.lastUsed) {
|
|
162
|
+
console.log(`${chalk.bold("Last used:")} ${stats?.lastUsed ?? info.lastUsed}`);
|
|
68
163
|
}
|
|
69
164
|
if (info.lastError) {
|
|
70
165
|
console.log(`${chalk.bold("Last error:")} ${chalk.red(info.lastError)}`);
|
|
71
166
|
}
|
|
167
|
+
if (stats) {
|
|
168
|
+
console.log(`${chalk.bold("Runs:")} ${stats.runs}`);
|
|
169
|
+
console.log(`${chalk.bold("Errors:")} ${stats.errors}`);
|
|
170
|
+
}
|
|
72
171
|
if (opts.prompt && info.prompt) {
|
|
73
172
|
console.log(`\n${chalk.bold.cyan("--- SKILL.md ---")}\n`);
|
|
74
173
|
console.log(info.prompt);
|
|
@@ -76,6 +175,23 @@ export function registerSkillsCommand(program) {
|
|
|
76
175
|
});
|
|
77
176
|
skill
|
|
78
177
|
.command("use <name>")
|
|
178
|
+
.description("Show the SKILL.md guide for a skill")
|
|
179
|
+
.action(async (name) => {
|
|
180
|
+
const info = getSkillInfo(name);
|
|
181
|
+
if (!info.exists) {
|
|
182
|
+
console.error(`Skill not found: ${name}`);
|
|
183
|
+
console.error(`\nRun 'nexus skill list' to see available skills.`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
if (!info.prompt) {
|
|
187
|
+
console.error(`No SKILL.md found for ${name}.`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
console.log(info.prompt.trimEnd());
|
|
191
|
+
recordSkillUsage(name, { ts: Date.now(), event: "use", ok: true });
|
|
192
|
+
});
|
|
193
|
+
skill
|
|
194
|
+
.command("run <name>")
|
|
79
195
|
.description("Run a skill command with arguments")
|
|
80
196
|
.allowUnknownOption()
|
|
81
197
|
.action(async (name, _opts, command) => {
|
|
@@ -126,7 +242,14 @@ export function registerSkillsCommand(program) {
|
|
|
126
242
|
}
|
|
127
243
|
console.log(`Total skill runs: ${stats.totalRuns}`);
|
|
128
244
|
console.log(`Active skills: ${stats.activeSkills}`);
|
|
129
|
-
|
|
245
|
+
if (stats.topUsed && stats.topUsed.length > 0) {
|
|
246
|
+
console.log(`Most used: ${stats.topUsed
|
|
247
|
+
.map((item) => `${item.name} (${item.runs})`)
|
|
248
|
+
.join(", ")}`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
console.log("Most used: none");
|
|
252
|
+
}
|
|
130
253
|
console.log(`Ready but unused: ${stats.readyButUnused?.join(", ") || "none"}`);
|
|
131
254
|
});
|
|
132
255
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
-
import
|
|
4
|
+
import path from "node:path";
|
|
5
5
|
import { Readable } from "node:stream";
|
|
6
6
|
import { pipeline } from "node:stream/promises";
|
|
7
7
|
import { fileTypeFromFile } from "file-type";
|
|
8
|
-
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
|
9
8
|
import { getSkillMetadata, hasBinary, loadWorkspaceSkillEntries, } from "../agents/skills.js";
|
|
9
|
+
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
|
10
10
|
import { DEFAULT_AGENT_WORKSPACE_DIR } from "../agents/workspace.js";
|
|
11
|
-
import { generateSkillManifest, writeSkillManifest } from "../commands/skills-manifest.js";
|
|
11
|
+
import { generateSkillManifest, writeSkillManifest, } from "../commands/skills-manifest.js";
|
|
12
12
|
import { loadConfig } from "../config/config.js";
|
|
13
|
-
import { runCommandWithTimeout } from "../process/exec.js";
|
|
14
13
|
import { listCredentialEntries, resolveCredentialValue, } from "../credentials/store.js";
|
|
14
|
+
import { runCommandWithTimeout } from "../process/exec.js";
|
|
15
15
|
import { MANAGED_SKILLS_DIR, resolveUserPath } from "../utils.js";
|
|
16
16
|
function getHubBaseUrl() {
|
|
17
17
|
return (process.env.NEXUS_HUB_URL ||
|
|
@@ -41,7 +41,9 @@ function normalizeStringList(value) {
|
|
|
41
41
|
return [];
|
|
42
42
|
}
|
|
43
43
|
function normalizeHubDependencies(value) {
|
|
44
|
-
const raw = value && typeof value === "object"
|
|
44
|
+
const raw = value && typeof value === "object"
|
|
45
|
+
? value
|
|
46
|
+
: {};
|
|
45
47
|
return {
|
|
46
48
|
dependencies: normalizeStringList(raw.dependencies),
|
|
47
49
|
tools: normalizeStringList(raw.tools),
|
|
@@ -142,16 +144,16 @@ function normalizeMetadata(value) {
|
|
|
142
144
|
return {};
|
|
143
145
|
}
|
|
144
146
|
function resolveCapabilities(entry) {
|
|
147
|
+
const metadata = entry.metadata;
|
|
148
|
+
const metadataNexus = metadata && typeof metadata.nexus === "object"
|
|
149
|
+
? metadata.nexus
|
|
150
|
+
: undefined;
|
|
145
151
|
const candidates = [
|
|
146
152
|
entry.capabilities,
|
|
147
153
|
entry.provides,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
entry.metadata?.nexus
|
|
152
|
-
? entry.metadata.nexus.capabilities
|
|
153
|
-
: undefined,
|
|
154
|
-
entry.metadata?.capabilities,
|
|
154
|
+
metadataNexus?.provides,
|
|
155
|
+
metadataNexus?.capabilities,
|
|
156
|
+
metadata?.capabilities,
|
|
155
157
|
];
|
|
156
158
|
for (const candidate of candidates) {
|
|
157
159
|
const parsed = normalizeStringList(candidate);
|
|
@@ -456,7 +458,7 @@ async function computeArtifactInfo(filePath) {
|
|
|
456
458
|
return { sha256: hash, bytes: stats.size };
|
|
457
459
|
}
|
|
458
460
|
async function resolveHubToken(input) {
|
|
459
|
-
if (input
|
|
461
|
+
if (input?.trim())
|
|
460
462
|
return input.trim();
|
|
461
463
|
const envToken = process.env.NEXUS_HUB_TOKEN ||
|
|
462
464
|
process.env.NEXUS_WEBSITE_TOKEN ||
|
|
@@ -483,7 +485,9 @@ function buildManifestPayload(input) {
|
|
|
483
485
|
(typeof frontmatter.name === "string" ? frontmatter.name : "") ||
|
|
484
486
|
"";
|
|
485
487
|
const description = overrides.description ||
|
|
486
|
-
(typeof frontmatter.description === "string"
|
|
488
|
+
(typeof frontmatter.description === "string"
|
|
489
|
+
? frontmatter.description
|
|
490
|
+
: "") ||
|
|
487
491
|
"";
|
|
488
492
|
const type = overrides.type ||
|
|
489
493
|
(typeof frontmatter.type === "string" ? frontmatter.type : "") ||
|
|
@@ -496,7 +500,9 @@ function buildManifestPayload(input) {
|
|
|
496
500
|
(typeof frontmatter.license === "string" ? frontmatter.license : "") ||
|
|
497
501
|
"";
|
|
498
502
|
const repository = overrides.repository ||
|
|
499
|
-
(typeof frontmatter.repository === "string"
|
|
503
|
+
(typeof frontmatter.repository === "string"
|
|
504
|
+
? frontmatter.repository
|
|
505
|
+
: "") ||
|
|
500
506
|
(typeof frontmatter.repo === "string" ? frontmatter.repo : "") ||
|
|
501
507
|
"";
|
|
502
508
|
const homepage = overrides.homepage ||
|
|
@@ -530,11 +536,15 @@ function buildManifestPayload(input) {
|
|
|
530
536
|
const capabilities = resolveCapabilities(capabilityCandidates);
|
|
531
537
|
if (capabilities.length > 0) {
|
|
532
538
|
manifestEntry.capabilities = capabilities;
|
|
533
|
-
const
|
|
534
|
-
nexus.nexus
|
|
535
|
-
|
|
539
|
+
const metadata = (manifestEntry.metadata ?? {});
|
|
540
|
+
const existingNexus = metadata.nexus && typeof metadata.nexus === "object"
|
|
541
|
+
? metadata.nexus
|
|
542
|
+
: {};
|
|
543
|
+
metadata.nexus = {
|
|
544
|
+
...existingNexus,
|
|
536
545
|
provides: capabilities,
|
|
537
546
|
};
|
|
547
|
+
manifestEntry.metadata = metadata;
|
|
538
548
|
}
|
|
539
549
|
const readmeExcerpt = extractExcerpt(body || description);
|
|
540
550
|
return {
|
|
@@ -595,7 +605,9 @@ async function installHubSkill(params) {
|
|
|
595
605
|
return { installed: true, destDir };
|
|
596
606
|
}
|
|
597
607
|
export function registerSkillsHubCommand(program) {
|
|
598
|
-
const skills = program
|
|
608
|
+
const skills = program
|
|
609
|
+
.command("skills")
|
|
610
|
+
.description("Search and install skills from the Nexus Hub");
|
|
599
611
|
skills
|
|
600
612
|
.command("search <query>")
|
|
601
613
|
.description("Search local skills and the Nexus Hub")
|
|
@@ -625,7 +637,10 @@ export function registerSkillsHubCommand(program) {
|
|
|
625
637
|
config: cfg,
|
|
626
638
|
managedSkillsDir,
|
|
627
639
|
});
|
|
628
|
-
const metadataByName = new Map(metadataEntries.map((entry) => [
|
|
640
|
+
const metadataByName = new Map(metadataEntries.map((entry) => [
|
|
641
|
+
entry.skill.name,
|
|
642
|
+
getSkillMetadata(entry),
|
|
643
|
+
]));
|
|
629
644
|
const installedNames = new Set(metadataEntries.map((entry) => normalizeSkillKey(entry.skill.name)));
|
|
630
645
|
const installedSlugs = await loadManagedSkillSlugs(managedSkillsDir);
|
|
631
646
|
const installedIndex = {
|
|
@@ -666,7 +681,7 @@ export function registerSkillsHubCommand(program) {
|
|
|
666
681
|
})
|
|
667
682
|
.filter((skill) => skill.score > 0)
|
|
668
683
|
.sort((a, b) => b.score - a.score)
|
|
669
|
-
.map(({ score, ...rest }) => rest);
|
|
684
|
+
.map(({ score: _score, ...rest }) => rest);
|
|
670
685
|
let hubMatches = [];
|
|
671
686
|
if (!opts.localOnly) {
|
|
672
687
|
try {
|
|
@@ -801,7 +816,10 @@ export function registerSkillsHubCommand(program) {
|
|
|
801
816
|
console.log(` Requires → ${deps.join(" | ")}`);
|
|
802
817
|
}
|
|
803
818
|
if (skill.install.length > 0) {
|
|
804
|
-
console.log(` Install → ${skill.install
|
|
819
|
+
console.log(` Install → ${skill.install
|
|
820
|
+
.map((i) => i.label)
|
|
821
|
+
.filter(Boolean)
|
|
822
|
+
.join(", ")}`);
|
|
805
823
|
}
|
|
806
824
|
}
|
|
807
825
|
console.log("");
|
|
@@ -873,20 +891,32 @@ export function registerSkillsHubCommand(program) {
|
|
|
873
891
|
const capOverrides = [
|
|
874
892
|
...(Array.isArray(opts.capability) ? opts.capability : []),
|
|
875
893
|
...normalizeStringList(opts.capabilities),
|
|
876
|
-
]
|
|
894
|
+
]
|
|
895
|
+
.map((cap) => cap.trim())
|
|
896
|
+
.filter(Boolean);
|
|
877
897
|
const overrides = {
|
|
878
898
|
path: skillDir,
|
|
879
899
|
slug: typeof opts.slug === "string" ? opts.slug.trim() : undefined,
|
|
880
900
|
name: typeof opts.name === "string" ? opts.name.trim() : undefined,
|
|
881
|
-
description: typeof opts.description === "string"
|
|
901
|
+
description: typeof opts.description === "string"
|
|
902
|
+
? opts.description.trim()
|
|
903
|
+
: undefined,
|
|
882
904
|
type: typeof opts.type === "string" ? opts.type.trim() : undefined,
|
|
883
905
|
version: typeof opts.version === "string" ? opts.version.trim() : undefined,
|
|
884
906
|
license: typeof opts.license === "string" ? opts.license.trim() : undefined,
|
|
885
|
-
repository: typeof opts.repository === "string"
|
|
886
|
-
|
|
887
|
-
|
|
907
|
+
repository: typeof opts.repository === "string"
|
|
908
|
+
? opts.repository.trim()
|
|
909
|
+
: undefined,
|
|
910
|
+
homepage: typeof opts.homepage === "string"
|
|
911
|
+
? opts.homepage.trim()
|
|
912
|
+
: undefined,
|
|
913
|
+
visibility: typeof opts.visibility === "string"
|
|
914
|
+
? opts.visibility.trim()
|
|
915
|
+
: "public",
|
|
888
916
|
capabilities: capOverrides.length > 0 ? capOverrides : undefined,
|
|
889
|
-
sourcePath: typeof opts.source === "string"
|
|
917
|
+
sourcePath: typeof opts.source === "string"
|
|
918
|
+
? resolveUserPath(opts.source)
|
|
919
|
+
: undefined,
|
|
890
920
|
sourceCommit: typeof opts.sourceCommit === "string"
|
|
891
921
|
? opts.sourceCommit.trim()
|
|
892
922
|
: undefined,
|