@asobacloud/nehanda 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -29
- package/bin/agent.mjs +70 -59
- package/bin/nehanda-ui.mjs +147 -143
- package/lib/configureProvider.mjs +254 -0
- package/lib/modelConfig.mjs +20 -10
- package/lib/modelDiscovery.mjs +125 -1
- package/lib/openaiCompat.mjs +11 -1
- package/lib/orchestrate.mjs +32 -9
- package/lib/slashCommands.mjs +128 -0
- package/lib/ui.mjs +15 -10
- package/package.json +1 -1
- package/lib/scripts/__pycache__/laya_worker.cpython-314.pyc +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for nehanda-ui slash commands + help (#9).
|
|
3
|
+
* Autocomplete uses top-level entries (or nested when the user has typed a parent prefix).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @typedef {{ name: string, desc: string, autocomplete?: boolean }} SlashEntry */
|
|
7
|
+
|
|
8
|
+
/** @type {SlashEntry[]} */
|
|
9
|
+
export const TUI_SLASH_COMMANDS = [
|
|
10
|
+
{ name: '/model', desc: 'Configure provider → key → host → discover → model' },
|
|
11
|
+
{ name: '/key', desc: 'Set Nehanda API key, then continue configure' },
|
|
12
|
+
{ name: '/key openrouter [VALUE]', desc: 'OpenRouter key → continue configure', autocomplete: false },
|
|
13
|
+
{ name: '/key openai [VALUE]', desc: 'OpenAI-compat key → continue configure', autocomplete: false },
|
|
14
|
+
{ name: '/key zhipu [VALUE]', desc: 'Zhipu (z.ai) key → continue configure', autocomplete: false },
|
|
15
|
+
{ name: '/key nehanda [VALUE]', desc: 'Nehanda key → continue configure', autocomplete: false },
|
|
16
|
+
{ name: '/config', desc: 'Show provider, model, endpoint, key status' },
|
|
17
|
+
{ name: '/mcp', desc: 'MCP server & tool management' },
|
|
18
|
+
{ name: '/mcp list', desc: 'List tools from configured MCP servers', autocomplete: false },
|
|
19
|
+
{ name: '/mcp status', desc: 'Show configured servers', autocomplete: false },
|
|
20
|
+
{ name: '/mcp reload [server]', desc: 'Reload mcp.json into this session', autocomplete: false },
|
|
21
|
+
{ name: '/mcp add <name> <command> [args…]', desc: 'Register a server (~/.config/nehanda/mcp.json)', autocomplete: false },
|
|
22
|
+
{ name: '/mcp env', desc: 'Show env vars for every server (values masked)', autocomplete: false },
|
|
23
|
+
{ name: '/mcp env <server>', desc: 'Show env vars for one server', autocomplete: false },
|
|
24
|
+
{ name: '/mcp env <server> <NAME> <VALUE>', desc: 'Set env var e.g. /mcp env jupyter JUPYTER_TOKEN sk-…', autocomplete: false },
|
|
25
|
+
{ name: '/mcp env unset <server> <NAME>', desc: 'Remove an env var from a server', autocomplete: false },
|
|
26
|
+
{ name: '/clear', desc: 'New conversation' },
|
|
27
|
+
{ name: '/retry', desc: 'Resend the last failed message' },
|
|
28
|
+
{ name: '/help', desc: 'Show this help' },
|
|
29
|
+
{ name: '/exit', desc: 'Quit (/quit)' },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
/** Top-level commands only (slash menu when typing `/`). */
|
|
33
|
+
export function topLevelSlashCommands() {
|
|
34
|
+
return TUI_SLASH_COMMANDS.filter(c => c.autocomplete !== false && !c.name.slice(1).includes(' '))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Filter for Ink slash autocomplete.
|
|
39
|
+
* - Bare `/foo` → top-level matches
|
|
40
|
+
* - `/mcp…` → MCP entries (including subcommands)
|
|
41
|
+
* - `/key…` → /key variants
|
|
42
|
+
*/
|
|
43
|
+
export function filterSlashAutocomplete(filter) {
|
|
44
|
+
const f = String(filter || '')
|
|
45
|
+
const lower = f.toLowerCase()
|
|
46
|
+
if (lower.startsWith('/mcp')) {
|
|
47
|
+
const needle = lower.trimEnd()
|
|
48
|
+
return TUI_SLASH_COMMANDS.filter(c => {
|
|
49
|
+
if (!c.name.startsWith('/mcp')) return false
|
|
50
|
+
if (needle === '/mcp') return true
|
|
51
|
+
return c.name.toLowerCase().startsWith(needle)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
if (lower.startsWith('/key')) {
|
|
55
|
+
const needle = lower.trimEnd()
|
|
56
|
+
return TUI_SLASH_COMMANDS.filter(c => {
|
|
57
|
+
if (!c.name.startsWith('/key')) return false
|
|
58
|
+
if (needle === '/key') return true
|
|
59
|
+
return c.name.toLowerCase().startsWith(needle)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
return topLevelSlashCommands().filter(c =>
|
|
63
|
+
c.name.startsWith(f) || c.name.includes(f.slice(1)))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Full help text for `/help` (no chalk — caller may colorize). */
|
|
67
|
+
export function formatTuiHelpText() {
|
|
68
|
+
const pad = Math.max(...TUI_SLASH_COMMANDS.map(c => c.name.length), 12) + 2
|
|
69
|
+
const lines = ['', 'Commands:', '']
|
|
70
|
+
let lastGroup = ''
|
|
71
|
+
for (const c of TUI_SLASH_COMMANDS) {
|
|
72
|
+
const group = c.name.split(/\s+/)[0]
|
|
73
|
+
if (lastGroup && group !== lastGroup && (group === '/mcp' || lastGroup === '/mcp' || group === '/clear')) {
|
|
74
|
+
lines.push('')
|
|
75
|
+
}
|
|
76
|
+
lastGroup = group
|
|
77
|
+
lines.push(` ${c.name.padEnd(pad)}${c.desc}`)
|
|
78
|
+
}
|
|
79
|
+
lines.push('')
|
|
80
|
+
return lines.join('\n')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parse `/mcp env …` argument tokens (after `/mcp`).
|
|
85
|
+
* @param {string[]} parts - e.g. ['env'] | ['env','jupyter'] | ['env','unset','jupyter','TOKEN'] | ['env','jupyter','TOKEN','sk-…']
|
|
86
|
+
* @returns {{ action: string, server?: string, name?: string, value?: string, error?: string }}
|
|
87
|
+
*/
|
|
88
|
+
export function parseMcpEnvParts(parts) {
|
|
89
|
+
// parts[0] should be 'env'
|
|
90
|
+
const rest = parts.slice(1)
|
|
91
|
+
if (rest.length === 0) return { action: 'list_all' }
|
|
92
|
+
|
|
93
|
+
const head = (rest[0] || '').toLowerCase()
|
|
94
|
+
if (head === 'unset' || head === 'clear') {
|
|
95
|
+
const server = rest[1]
|
|
96
|
+
const name = rest[2]
|
|
97
|
+
if (!server || !name) {
|
|
98
|
+
return {
|
|
99
|
+
action: 'usage_error',
|
|
100
|
+
error: 'Usage: /mcp env unset <server> <NAME>\n Example: /mcp env unset jupyter JUPYTER_TOKEN',
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (rest.length > 3) {
|
|
104
|
+
return {
|
|
105
|
+
action: 'usage_error',
|
|
106
|
+
error: 'Usage: /mcp env unset <server> <NAME>\n Do not pass a value when unsetting.',
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { action: 'unset', server, name }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const server = rest[0]
|
|
113
|
+
if (rest.length === 1) return { action: 'list_server', server }
|
|
114
|
+
|
|
115
|
+
const name = rest[1]
|
|
116
|
+
const valueParts = rest.slice(2)
|
|
117
|
+
if (valueParts.length === 0) {
|
|
118
|
+
return {
|
|
119
|
+
action: 'usage_error',
|
|
120
|
+
error: [
|
|
121
|
+
`Missing VALUE for ${name}.`,
|
|
122
|
+
` Set: /mcp env ${server} ${name} <VALUE>`,
|
|
123
|
+
` Unset: /mcp env unset ${server} ${name}`,
|
|
124
|
+
].join('\n'),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { action: 'set', server, name, value: valueParts.join(' ') }
|
|
128
|
+
}
|
package/lib/ui.mjs
CHANGED
|
@@ -120,16 +120,18 @@ export function formatHelp(provider, dbPath) {
|
|
|
120
120
|
cmd('/compact', 'Compact conversation'),
|
|
121
121
|
cmd('/team', 'Manage teams'),
|
|
122
122
|
'',
|
|
123
|
-
cmd('/model
|
|
123
|
+
cmd('/model', 'Configure provider, key, host, model'),
|
|
124
|
+
cmd('/key [provider] [value]', 'Save API key then continue configure'),
|
|
124
125
|
cmd('/login', 'Store credentials'),
|
|
125
126
|
cmd('/logout', 'Clear credentials'),
|
|
126
127
|
cmd('/status', 'Auth status'),
|
|
127
|
-
cmd('/config', 'Show /
|
|
128
|
-
cmd('/config set <key> <value>', 'Set any config value'),
|
|
128
|
+
cmd('/config', 'Show provider / model / key status'),
|
|
129
129
|
cmd('/mcp', 'MCP server management & guide'),
|
|
130
130
|
cmd('/mcp list', 'List all discovered tools & descriptions'),
|
|
131
131
|
cmd('/mcp status', 'Show configured MCP servers'),
|
|
132
|
-
cmd('/mcp env
|
|
132
|
+
cmd('/mcp env', 'Show env vars (masked)'),
|
|
133
|
+
cmd('/mcp env <server> <NAME> <VALUE>', 'Set an env var'),
|
|
134
|
+
cmd('/mcp env unset <server> <NAME>', 'Remove an env var'),
|
|
133
135
|
cmd('/clear', 'New conversation'),
|
|
134
136
|
cmd('/exit', 'Quit (/quit)'),
|
|
135
137
|
'',
|
|
@@ -141,15 +143,18 @@ export function formatHelp(provider, dbPath) {
|
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
export function formatMcpHelp() {
|
|
144
|
-
const cmd = (name, desc) => ` ${colors.command(name.padEnd(
|
|
146
|
+
const cmd = (name, desc) => ` ${colors.command(name.padEnd(40))} ${colors.dim(desc)}`
|
|
145
147
|
const lines = [
|
|
146
148
|
'',
|
|
147
149
|
colors.header('MCP Server & Tool Commands'),
|
|
148
|
-
cmd('/mcp list', '
|
|
149
|
-
cmd('/mcp status', 'Show configured servers
|
|
150
|
-
cmd('/mcp reload [server]', 'Reload mcp.json
|
|
151
|
-
cmd('/mcp add <name> <
|
|
152
|
-
cmd('/mcp env
|
|
150
|
+
cmd('/mcp list', 'List tools from configured MCP servers'),
|
|
151
|
+
cmd('/mcp status', 'Show configured servers'),
|
|
152
|
+
cmd('/mcp reload [server]', 'Reload mcp.json into this session'),
|
|
153
|
+
cmd('/mcp add <name> <command> [args…]', 'Register a server'),
|
|
154
|
+
cmd('/mcp env', 'Show env vars for every server (values masked)'),
|
|
155
|
+
cmd('/mcp env <server>', 'Show env vars for one server'),
|
|
156
|
+
cmd('/mcp env <server> <NAME> <VALUE>', 'Set env var e.g. /mcp env jupyter JUPYTER_TOKEN sk-…'),
|
|
157
|
+
cmd('/mcp env unset <server> <NAME>', 'Remove an env var'),
|
|
153
158
|
'',
|
|
154
159
|
colors.dim(' Tip: Run /mcp list to inspect tools injected into the assistant.'),
|
|
155
160
|
'',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asobacloud/nehanda",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agentic terminal REPL and single-process engine for governed AI deep research and software development. Connects to Nehanda v3, OpenRouter, Ollama, LM Studio, ZhipuAI, or any OpenAI-compatible API. Includes Laya System-1 control plane for memory management, pre-execution safety gating, and intent-driven instruction shaping.",
|
|
6
6
|
"keywords": [
|
|
Binary file
|