@raolin2025/claude-code-node 2.2.5 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/core/cli.js +80 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raolin2025/claude-code-node",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "description": "Node.js AI Code Agent CLI - Zero dependencies, pure JavaScript, security hardened, multi-channel notifications",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/core/cli.js CHANGED
@@ -129,6 +129,7 @@ const HELP_TEXT = `
129
129
  Commands:
130
130
  /help — Show this help
131
131
  /model NAME — Switch model
132
+ /models — List available models from current API
132
133
  /tools — List available tools
133
134
  /session — Show session info
134
135
  /sessions — List all sessions
@@ -142,8 +143,43 @@ Commands:
142
143
  /allow [tool] — Allow a tool for the current session (default: all)
143
144
  /exit — Exit (also Ctrl+C)
144
145
  /quit — Same as /exit
146
+
147
+ Use "/help <cmd>" for detailed help on a specific command.
145
148
  `
146
149
 
150
+ const DETAILED_HELP = {
151
+ help: "/help [command]\n Show help. Without argument: list all commands.\n With a command name: show detailed help for that command.\n\n Example: /help model",
152
+
153
+ model: "/model <model_name>\n Switch the LLM model in real-time.\n The change takes effect immediately for the next message.\n You can use any model name supported by your current API provider.\n\n Example: /model deepseek-chat\n Example: /model gpt-4o",
154
+
155
+ models: "/models\n Fetch and display all available models from the current API provider.\n Shows a numbered list, then prompts you to select by number or name.\n Requires a configured API key (the one you used to start cc-node).\n Uses the endpoint: <apiBase>/models",
156
+
157
+ tools: "/tools\n List all available tools that cc-node can use.\n Shows tool names with their short descriptions.\n\n Tools include: Bash, Read, Edit, Write, Glob, Grep,\n WebFetch, WebSearch, AskUserQuestion, GitTool",
158
+
159
+ session: "/session\n Show current session information:\n - Session ID\n - Session title\n - Number of messages\n - Number of tool call turns",
160
+
161
+ sessions:"/sessions\n List all saved sessions.\n Shows session ID, title, message count, and last update time.",
162
+
163
+ clear: "/clear\n Clear the current conversation context.\n Starts a fresh session. Previous messages are not sent to the API anymore.\n\n Note: Does not delete saved sessions.",
164
+
165
+ config: "/config [key]\n Without key: show the entire config as JSON.\n With a key path: show the value for that specific path.\n\n Example: /config\n Example: /config model",
166
+
167
+ budget: "/budget\n Show token budget usage for the current session.\n Displays how many tokens have been used vs the limit.",
168
+
169
+ channel: "/channel <list|send|test>\n Manage notification channels.\n\n Subcommands:\n list — List all configured notification channels\n send <msg> — Send a message via all channels\n test — Send a test message to verify channels\n\n Requires channel environment variables to be set at startup.",
170
+
171
+ cost: "/cost\n Show API cost report.\n Displays total tokens used and estimated cost in USD.\n Supports pricing for: DeepSeek, OpenAI, Qwen, GLM, Kimi.",
172
+
173
+ compact: "/compact\n Manually trigger context compression.\n Compresses the conversation history to fit within the token budget.\n Keeps recent turns intact, compresses older ones.\n\n Typically triggered automatically at 80% budget usage.",
174
+
175
+ cd: "/cd <path>\n Change the working directory of cc-node.\n Affects all subsequent tool executions (Bash, Read, Write, etc.).\n\n Without path: show the current working directory.\n\n Example: /cd /home/raolin/projects\n Example: /cd ..",
176
+
177
+ allow: "/allow [tool_name]\n Allow a tool to execute without confirmation for this session.\n Without tool name: allows ALL tools.\n\n Example: /allow\n Example: /allow Bash",
178
+
179
+ exit: "/exit\n Exit cc-node. Same as Ctrl+C or /quit.",
180
+ quit: "/quit\n Exit cc-node. Same as Ctrl+C or /exit.",
181
+ }
182
+
147
183
  // ============================================================
148
184
  // 参数解析
149
185
  // ============================================================
@@ -351,11 +387,54 @@ export async function main() {
351
387
  if (input.startsWith('/')) {
352
388
  const [cmd, ...rest] = input.slice(1).split(' ')
353
389
  switch (cmd) {
354
- case 'help': console.log(HELP_TEXT); break
390
+ case 'help':
391
+ if (rest[0]) {
392
+ const detail = DETAILED_HELP[rest[0].toLowerCase()]
393
+ if (detail) console.log(detail)
394
+ else console.log(`No detailed help for /${rest[0]}. Type /help for all commands.`)
395
+ } else {
396
+ console.log(HELP_TEXT)
397
+ }
398
+ break
355
399
  case 'model':
356
400
  if (rest[0]) { engine.config.model = rest.join(' '); console.log(`Model → ${engine.config.model}`) }
357
401
  else console.log(`Model: ${engine.config.model}`)
358
402
  break
403
+ case 'models': {
404
+ const apiBase = engine.config.apiBase
405
+ const apiKey = engine.config.apiKey
406
+ if (!apiKey) { console.log('❌ API key not configured'); break }
407
+ const modelsUrl = apiBase.replace(/\/+$/, '') + '/models'
408
+ console.log(`📡 Fetching models from ${modelsUrl}...`)
409
+ try {
410
+ const res = await fetch(modelsUrl, { headers: { 'Authorization': `Bearer ${apiKey}` } })
411
+ if (!res.ok) { console.log(`❌ API error: ${res.status}`); break }
412
+ const data = await res.json()
413
+ const models = data.data || []
414
+ if (models.length === 0) { console.log('No models returned'); break }
415
+ console.log(`\nAvailable models (${models.length}):`)
416
+ models.forEach((m, i) => {
417
+ const id = m.id || m
418
+ console.log(` ${(i + 1).toString().padStart(2)}. ${id}`)
419
+ })
420
+ console.log('\nType a number to select, or model name directly:')
421
+ const answer = await new Promise(resolve => rl.question('> ', resolve))
422
+ const num = parseInt(answer, 10)
423
+ let selected
424
+ if (!isNaN(num) && num >= 1 && num <= models.length) {
425
+ selected = models[num - 1].id || models[num - 1]
426
+ } else if (answer.trim()) {
427
+ selected = answer.trim()
428
+ }
429
+ if (selected) {
430
+ engine.config.model = selected
431
+ console.log(`Model → ${selected}`)
432
+ }
433
+ } catch (err) {
434
+ console.log(`❌ ${err.message}`)
435
+ }
436
+ break
437
+ }
359
438
  case 'tools':
360
439
  console.log('Available tools:')
361
440
  for (const name of registry.getNames()) {