Token导航 LogoToken导航TokenDH.com
Notion MCP CLI logo
开发工具stdio官方级别未说明来源级核验

Notion MCP CLI

MCP Server

skills

goke是一个基于TypeScript的CLI框架,提供类似Hono的API链式调用,支持中间件和命令路由,适用于构建终端应用程序。

工具数

0

提示词数

0

GitHub Stars

51

资源数

0
TypeScriptClaude命令行工具Claude DesktopClaudeCursorVS Code

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

remorses

提供方

remorses

最后核验

2026/5/17 20:21

运行时

Node.js

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

命令预览

npx -y skills add remorses/goke

详细介绍

goke

Build CLIs like you'd build an API. Type-safe, chainable, zero dependencies.

goke是一个TypeScript CLI框架,具有 荣誉-如API。你链 .use() 用于中间件和 .command() 对于路由-与应用于终端的REST API相同的心理模型。

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke('deploy')

// middleware — runs before every command
cli
  .option('--env ', z.enum(['staging', 'production']).default('staging').describe('Target environment'))
  .use((options, { console }) => {
    console.log(`Environment: ${options.env}`)
  })

// commands — like route handlers
cli
  .command('up', 'Deploy the app')
  .option('--dry-run', 'Preview without deploying')
  .action((options, { console, process }) => {
    console.log(`Deploying from ${process.cwd}`)
  })

cli
  .command('logs ', 'Stream logs')
  .option('--lines ', z.number().default(100).describe('Lines to tail'))
  .action((id, options) => streamLogs(id, options.lines))

cli.help()
cli.completions()
cli.parse()

特性

  • 霍诺喜欢链锁.use() 对于中间件, .command() 对于处理人员。以与设计REST API相同的方式构建CLI。
  • Zod型安全 --将Zod模式传递给 .option() 并免费获得自动强制转换、TypeScript推理和帮助文本。适用于Valibot、ArkType或任何标准模式库。
  • MCP服务器分为两条线路 --将整个CLI暴露为MCP服务器 createMcpAction({ cli })每个命令都成为一个工具,为Claude Desktop、Cursor、VS Code和 任何MCP客户端.
  • JustBash支持cli.createJustBashCommand() 将CLI公开为沙盒JustBash命令。相同的操作代码,无需更改。
  • 空格分隔的子命令git remote add, mcp login, db migrate --多词命令开箱即用。
  • 注入 { fs, console, process } --命令接收可移植的运行时上下文。在测试中交换它,或者让JustBash用沙箱替换它。无全球副作用。
  • 壳体完井cli.completions() 为zsh和bash添加Tab补全功能。安装一次 mycli completions install;补全会自动保持最新,因为shell每次按Tab键都会调用二进制文件。
  • 零运行时依赖关系 --安装 goke 而无需将额外的运行时包拉入CLI。
  • Agent检测isAgentdetectAgent() 告诉您该进程是否在AI编码代理(Claude、Cursor、Codex、Gemini等)内运行。跳过提示,更喜欢结构化输出,自动调整行为。

安装

npm install goke

为AI代理安装技能

npx -y skills add remorses/goke

这为AI编码代理安装了存储库技能。在这个仓库中,所提供的技能位于 skills/goke/SKILL.md.

Agent检测

goke可以检测您的CLI是否由AI编码代理运行。这对于跳过交互式提示、首选结构化输出或在代理驱动CLI时调整行为非常有用。

import { isAgent, agent, agentInfo, detectAgent } from 'goke'

if (isAgent) {
  console.log(`Running inside ${agent}`) // e.g. "claude", "cursor", "codex"
}

// Re-run detection (e.g. after env changes)
const info = detectAgent()
console.log(info) // { name: "claude" } or {}

检测通过扫描已知代理设置的环境变量来工作。这 AI_AGENT env-var可以显式设置为覆盖检测。

支持的代理: cursor, claude, devin, replit, gemini, codex, auggie, opencode, kiro, goose, pi

一种常见的模式是将代理检测与交互式提示回退相结合:

import { goke, isAgent } from 'goke'
import * as clack from '@clack/prompts'

cli
  .command('deploy', 'Deploy the app')
  .option('--env [env]', z.enum(['staging', 'production']).optional().describe('Target environment'))
  .action(async (options) => {
    let env = options.env

    if (!env) {
      if (isAgent || !process.stdin.isTTY) {
        // Agent or non-TTY: fail with clear usage instead of hanging on a prompt
        console.error('Missing --env. Usage: deploy --env staging|production')
        process.exit(1)
      }

      const choice = await clack.select({
        message: 'Which environment?',
        options: [
          { value: 'staging', label: 'Staging' },
          { value: 'production', label: 'Production' },
        ],
      })
      if (clack.isCancel(choice)) process.exit(0)
      env = choice
    }
  })

检测逻辑从 unjs/std-env。检查的特定于代理的环境变量包括 CLAUDECODE, CURSOR_AGENT, CODEX_SANDBOX, GEMINI_CLI, OPENCODE以及其他。最后检查基于IDE的代理(Cursor、Devin、Kiro),以便在这些IDE中运行的代理首先被它们自己的环境变量检测到。

终端颜色

哥克运送一艘被出卖的船 colors 导出(picocolors API),因此使用goke构建的CLI不需要安装 picocolors, chalk, kleur或任何其他颜色库。树中的依赖性少了一个。

import { colors } from 'goke'

console.log(colors.green('success'))
console.log(colors.red('error'))
console.log(colors.bold(colors.cyan('info')))

切勿安装单独的颜色库。 导入 colors 从goke代替。它支持所有标准格式化程序: bold, dim, italic, underline, red, green, yellow, blue, magenta, cyan, gray, bgRed, bgGreen等等。颜色支持从以下位置自动检测 NO_COLOR, FORCE_COLOR, --no-color, --color以及TTY状态。

用法

简单解析

使用goke作为简单的参数解析器:

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke()

cli.option(
  '--type [type]',
  z.string().default('node').describe('Choose a project type'),
)
cli.option('--name ', 'Provide your name')

cli.command('lint [...files]', 'Lint files').action((files, options, { console, process }) => {
  console.log(files, options, process.cwd)
})

cli
  .command('build [entry]', 'Build your app')
  .option('--minify', 'Minify output')
  .example('build src/index.ts')
  .example('build src/index.ts --minify')
  .action(async (entry, options, { console, process }) => { // options is type safe! no need to type it
    console.log(entry, options, process.env.NODE_ENV)
  })

cli.example((bin) => `${bin} lint src/**/*.ts`)

// Display help message when `-h` or `--help` appears
cli.help()
cli.completions()
// Display version number when `-v` or `--version` appears
cli.version('0.0.0')

cli.parse()

定义示例时,帮助输出包括 例子 部分。

帮助中的命令示例

使用 .example(...) 根据命令(或 cli)在帮助中显示用法代码片段:

import { goke } from 'goke'

const cli = goke('mycli')

cli
  .command('deploy', 'Deploy current app')
  .option('--env ', 'Target environment')
  .example('mycli deploy --env production')
  .example('mycli deploy --env staging')
  .action(() => {})

cli.example((bin) => `${bin} deploy --env production`)

cli.help()
cli.completions()
cli.parse()

丰富的多行命令描述(string-dedent)

当命令需要长描述时(包括项目符号、引号、内联代码和 多个示例),使用 string-dedent 以保持源代码的可读性,同时保留干净的帮助输出。

安装:

npm install string-dedent

带有详细命令描述的示例:

import { goke } from 'goke'
import dedent from 'string-dedent'

const cli = goke('acme')

cli
  .command(
    'release ',
    dedent`
      Publish a versioned release to your distribution channels.

      - **Validates** release metadata and changelog before publishing.
      - **Builds** production artifacts with reproducible settings.
      - **Tags** git history using semantic version format.
      - **Publishes** to npm and creates release notes.

      > Recommended flow: run with \`--dry-run\` first in CI to verify output.

      Examples:
      - \`acme release 2.4.0 --channel stable\`
      - \`acme release 2.5.0-rc.1 --channel beta --dry-run\`
      - \`acme release 3.0.0 --notes-file ./docs/releases/3.0.0.md\`
    `,
  )
  .option('--channel ', 'Target channel: stable, beta, alpha')
  .option('--notes-file 
', 'Markdown file used as release notes')
  .option('--dry-run', 'Preview every step without publishing')
  .action((version, options, { console, process }) => {
    console.log('release', version, options, process.cwd)
  })

cli
  .command(
    'db migrate',
    dedent`
      Apply pending database migrations in a controlled sequence.

      - Runs migrations in timestamp order.
      - Stops immediately on first failure.
      - Prints SQL statements when \`--verbose\` is enabled.
      - Supports smoke-testing with \`--dry-run\`.

      > Safety: always run this command against staging before production.

      Examples:
      - \`acme db migrate\`
      - \`acme db migrate --target 20260210120000_add_users\`
      - \`acme db migrate --dry-run --verbose\`
    `,
  )
  .option('--target ', 'Apply up to a specific migration id')
  .option('--dry-run', 'Print plan only, do not execute SQL')
  .option('--verbose', 'Show each executed statement')
  .action((options, { console, process }) => {
    console.log('migrate', options, process.stdin)
  })

cli.help()
cli.completions()
cli.parse()

为什么这种模式效果很好:

  • dedent 使模板文本在源文件中可读。
  • 帮助文本保持对齐,没有额外的前导空格。
  • 您可以包含用户已经识别的丰富格式模式:

列表、引号和内联命令片段。

  • 随着CLI的增长,长篇描述仍然可以维护。
重要!string dedent的起始行和结束行必须始终为空,否则将抛出运行时错误。永远不要拒绝non empty line.

富有的 .example(...) 块与 dedent

您还可以使用 dedent.example(...) 因此,示例在代码中保持可读性 在帮助输出中呈现良好。一个有用的模式是 第一行a # 评论 这就解释了这种情况。

import { goke } from 'goke'
import dedent from 'string-dedent'

const cli = goke('tuistory')

cli
  .command('start', 'Start an interactive session')
  .example(dedent`
    # Launch and immediately check what the app shows
    tuistory launch "claude" -s ai && tuistory -s ai snapshot --trim
  `)
  .example(dedent`
    # Start a focused coding session with explicit context
    tuistory start --agent code --context "Fix OAuth callback timeout"
  `)
  .example(dedent`
    # Recover recent activity and inspect the latest run details
    tuistory runs list --limit 5 && tuistory runs show --latest
  `)
  .action(() => {
    // command implementation
  })

cli
  .command('deploy', 'Deploy current workspace')
  .example(dedent`
    # Dry-run deployment first to validate plan
    tuistory deploy --env staging --dry-run
  `)
  .example(dedent`
    # Deploy production with release notes attached
    tuistory deploy --env production --notes ./docs/release.md
  `)
  .action(() => {
    // command implementation
  })

cli.help()
cli.completions()
cli.parse()

笔记:

  • 将每个示例集中在一个工作流程上。
  • 使用第一个 # 行作为人类可读的意图标签。
  • 保持命令行可复制粘贴(避免占位符过多的示例)。

今天呈现的示例如下:

  • 获取根帮助(deploy --help),root/default命令中的示例出现在 例子 最后一节。
  • 获取子命令帮助(deploy logs --help),该特定子命令中的示例将单独显示 例子 最后一节。

内联快照样式输出(许多命令):

deploy

Usage:
  $ deploy [options]

Commands:
  deploy               Deploy the current project
  init                 Initialize a new project
  login                Authenticate with the server
  logout               Clear saved credentials
  status               Show deployment status
  logs   Stream logs for a deployment

Options:
  --env   Target environment
  --dry-run    Preview without deploying
  -h, --help   Display this message

Examples:
# Deploy to staging first
deploy --env staging --dry-run
deploy

Usage:
  $ deploy logs 

Options:
  --follow     Follow log output
  --lines   Number of lines (default: 100)
  -h, --help   Display this message

Description:
  Stream logs for a deployment

Examples:
# Stream last 200 lines for a deployment
deploy logs dep_123 --lines 200
# Keep following new log lines
deploy logs dep_123 --follow

许多带有根命令的命令

使用 '' 作为命令名,用于定义在没有给出子命令时运行的根命令。这对于具有主操作和多个子命令的CLI非常有用:

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke('deploy')

// Root command — runs when user types just `deploy`
cli
  .command('', 'Deploy the current project')
  .option(
    '--env ',
    z.string().default('production').describe('Target environment'),
  )
  .option('--dry-run', 'Preview without deploying')
  .action((options, { console, process }) => {
    console.log(`Deploying to ${options.env} from ${process.cwd}...`)
  })

// Subcommands
cli
  .command('init', 'Initialize a new project')
  .option('--template ', 'Project template')
  .action((options, { console, process }) => {
    console.log('Initializing project in', process.cwd)
  })

cli.command('login', 'Authenticate with the server').action((options, { console, process }) => {
  console.log('Opening browser for login from', process.cwd)
})

cli.command('logout', 'Clear saved credentials').action((options, { console, process }) => {
  console.log('Logged out', process.env.USER)
})

cli
  .command('status', 'Show deployment status')
  .option('--json', 'Output as JSON')
  .action((options, { console, process }) => {
    console.log('Fetching status from', process.cwd)
  })

cli
  .command('logs ', 'Stream logs for a deployment')
  .option('--follow', 'Follow log output')
  .option('--lines ', z.number().default(100).describe('Number of lines'))
  .action((deploymentId, options, { console, process }) => {
    console.log(`Streaming logs for ${deploymentId} from ${process.cwd}...`)
  })

cli.help()
cli.completions()
cli.version('1.0.0')
cli.parse()
deploy                          # runs root command (deploy to production)
deploy --env staging --dry-run  # root command with options
deploy init --template react    # subcommand
deploy login                    # subcommand
deploy logs abc123 --follow     # subcommand with args + options
deploy --help                   # shows all commands

将大型CLI拆分为文件

随着CLI的增长,在自己的文件中定义每个命令(或命令组),并用 .use()这使每个文件都保持专注,避免了一个巨大的入口点。

// commands/deploy.ts
import { goke } from 'goke'
import { z } from 'zod'

export default goke()
  .command('deploy', 'Deploy the app')
  .option('--env ', z.enum(['staging', 'production']).describe('Target environment'))
  .option('--dry-run', 'Preview without deploying')
  .action((options, { console, process }) => {
    console.log(`Deploying to ${options.env} from ${process.cwd}`)
  })
// commands/auth.ts — multiple commands need a variable
import { goke } from 'goke'
import { z } from 'zod'

const auth = goke()

auth
  .command('login', 'Authenticate with the server')
  .option('--token [token]', z.string().describe('API token (skips browser login)'))
  .action(async (options, { fs, console }) => {
    const token = options.token ?? await browserOAuth()
    await fs.mkdir('.mycli', { recursive: true })
    await fs.writeFile('.mycli/auth.json', JSON.stringify({ token }), 'utf8')
    console.log('Logged in')
  })

auth
  .command('logout', 'Clear saved credentials')
  .action(async (options, { fs, console }) => {
    await fs.rm('.mycli/auth.json', { force: true })
    console.log('Logged out')
  })

export default auth
// cli.ts
import { goke } from 'goke'
import deploy from './commands/deploy.js'
import auth from './commands/auth.js'

const cli = goke('mycli')
  .use(deploy)
  .use(auth)

cli.help()
cli.completions()
cli.version('1.0.0')
cli.parse()

比起只导入动作函数,更喜欢这样。 当您在goke中内联定义命令时,动作回调的类型将根据 .command().option() 链。如果将操作拆分为单独的函数,则必须手动复制选项类型以保持同步。随着 .use(subCli),这些类型仍然来源于真理的来源。

// BAD: duplicated types that can drift out of sync
import type { DeployOptions } from './types.js'
export function deployAction(options: DeployOptions) { /* ... */ }

// GOOD: types are always derived from the goke chain
export default goke()
  .command('deploy', 'Deploy')
  .option('--env ', z.enum(['staging', 'production']).describe('Environment'))
  .action((options) => {
    options.env // ← always in sync, inferred from the chain
  })

.use(subCli) 仅被调用 命令 组成了父。子CLI上定义的中间件和全局选项不会被复制。改为在父CLI上定义共享中间件。

全局选项和中间件

全局选项在CLI实例上定义,并应用于所有命令。使用 .use() 注册在任何命令操作之前运行的中间件,这对于对设置日志、初始化状态或配置服务等全局选项做出反应非常有用。

更喜欢注射 { fs, console, process } 全球争论 console, process.exit,或直接 node:fs/promises 进口。它使命令更容易测试,并允许相同的命令代码在JustBash等备用运行时中运行。

process.cwd, process.stdin,以及 process.env 来自活动运行时:

  • 在正常的Node.js运行中, process.cwdprocess.env 反映宿主进程,同时 process.stdin 默认为空字符串,除非您自己注入。
  • 在JustBash运行中,这些相同的字段是从沙盒执行上下文中填充的。

中间件按照注册顺序运行,在选项解析和验证之后,但在匹配的命令之前 .action() 回拨。

文件系统访问

注射 fs object是读取或写入CLI状态的推荐方式。

  • 在正常的Node.js运行中, fs 默认为 node:fs/promises
  • 在JustBash运行中, goke 在JustBash虚拟文件系统上交换兼容适配器

这使得存储风格的命令在两种环境中都能工作,而无需在运行时细节上进行分支。

cli
  .command('login', 'Save auth token')
  .option('--token ', z.string().describe('Auth token'))
  .action(async (options, { fs, console, process }) => {
    await fs.mkdir('.mycli', { recursive: true })
    await fs.writeFile('.mycli/auth.json', JSON.stringify({ token: options.token }), 'utf8')
    console.log('saved credentials in', process.cwd)
  })

cli
  .command('whoami', 'Read saved auth token')
  .action(async (options, { fs, console, process }) => {
    const auth = await fs.readFile('.mycli/auth.json', 'utf8')
    console.log(auth, process.env.USER)
  })

更喜欢注射 fs 用于CLI存储,而不是导入 node:fs/promises 直接内部行动。这使得该命令可以移植到JustBash,更容易测试。

路径处理

使用注射的相对路径 fs 用于常规CLI存储路径。当助手需要从当前目录解析时,注入pass process.cwd 进入那个助手,并从那里下定决心。

await fs.mkdir('.mycli', { recursive: true })
await fs.writeFile('.mycli/auth.json', json, 'utf8')
console.log('running from', process.cwd)

为什么这样做:

  • 在正常的Node.js运行中,相对路径会根据主机cwd解析。
  • 在JustBash运行中,相同的相对路径对沙箱cwd进行解析。
  • process.cwd 在两种环境中都反映了特定于运行时的cwd。

goke 还导出运行时类型,因此辅助函数可以使用依赖注入,而无需使用全局变量:

import { goke } from 'goke'
import type { GokeFs, GokeProcess } from 'goke'

async function saveAuthToken(args: {
  fs: GokeFs
  process: GokeProcess
  token: string
}) {
  await args.fs.mkdir('.mycli', { recursive: true })
  await args.fs.writeFile('.mycli/auth.json', JSON.stringify({
    token,
    cwd: args.process.cwd,
  }), 'utf8')
}

const cli = goke('mycli')

cli
  .command('login ', 'Save auth token')
  .action(async (token, options, { fs, process, console }) => {
    await saveAuthToken({ fs, process, token })
    console.log('saved credentials')
  })
import { goke } from 'goke'
import { z } from 'zod'

const cli = goke('mycli')

cli
  .option('--verbose', z.boolean().default(false).describe('Enable verbose logging'))
  .option('--api-url [url]', z.string().default('https://api.example.com').describe('API base URL'))
  .use((options, { console, process }) => {
    // options.verbose and options.apiUrl are fully typed here
    if (options.verbose) {
      console.log('verbose mode enabled in', process.cwd)
    }
  })

cli
  .command('deploy ', 'Deploy to an environment')
  .option('--dry-run', 'Preview without deploying')
  .action((env, options, { console, process }) => {
    // options includes both command options (dryRun) and global options (verbose, apiUrl)
    console.log(`Deploying to ${env} via ${options.apiUrl} from ${process.cwd}`)
  })

cli
  .command('status', 'Show deployment status')
  .action((options, { console, process }) => {
    console.log('Checking status...', process.stdin)
  })

cli.help()
cli.completions()
cli.parse()

类型安全是位置安全的——每个 .use() callback只看到链中前面声明的选项:

cli
  .option('--verbose', z.boolean().default(false).describe('Verbose'))
  .use((options, { process }) => {
    options.verbose   // boolean — typed
    process.argv      // string[] — typed
    process.cwd       // string — typed
    process.env       // Record — typed
    process.stdin     // string — typed
    options.port      // TypeScript error — not declared yet
  })
  .option('--port 
', z.number().describe('Port'))
  .use((options, { console, process }) => {
    options.verbose   // boolean — still visible
    options.port      // number — now visible
    console.error('ready', process.cwd)
  })

中间件支持异步功能。如果任何中间件是异步的,则剩余的中间件和命令操作将作为promise链接:

cli
  .option('--token ', z.string().describe('API token'))
  .use(async (options, { console, process }) => {
    const client = await connectToApi(options.token)
    globalState.client = client
    console.log('connected', process.env.NODE_ENV)
  })

命令特定选项

您可以将选项附加到命令。

import { goke } from 'goke'

const cli = goke()

cli
  .command('rm ', 'Remove a dir')
  .option('-r, --recursive', 'Remove recursively')
  .action((dir, options, { console, process }) => {
    console.log('remove ' + dir + (options.recursive ? ' recursively' : ''), process.cwd)
  })

cli.help()
cli.completions()

cli.parse()

空格分隔的子命令

goke支持类似git的嵌套子命令的多字命令名:

import { goke } from 'goke'

const cli = goke('mycli')

cli.command('mcp login ', 'Login to MCP server').action((url, options, { console, process }) => {
  console.log('Logging in to', url, 'from', process.cwd)
})

cli.command('mcp logout', 'Logout from MCP server').action((options, { console, process }) => {
  console.log('Logged out', process.env.USER)
})

cli
  .command('git remote add  ', 'Add a git remote')
  .action((name, url, options, { console, process }) => {
    console.log('Adding remote', name, url, 'from', process.cwd)
  })

cli.help()
cli.completions()
cli.parse()

基于模式的类型强制

将标准模式(如Zod)作为第二个参数传递给 .option() 用于自动类型强制。从架构中提取描述和默认值:

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke()

cli
  .command('serve', 'Start server')
  .option('--port 
', z.number().describe('Port number'))
  .option('--host [host]', z.string().default('localhost').describe('Hostname'))
  .option('--workers ', z.int().describe('Worker count'))
  .option('--tags ', z.array(z.string()).describe('Tags (repeatable)'))
  .option('--verbose', 'Verbose output')
  .action((options, { console, process }) => {
    // options.port is number, options.host is string, etc.
    console.log(options, process.env.NODE_ENV)
  })

cli.parse()

重要提示: 使用架构时 .default(),做 重复描述字符串中的默认值。框架会自动附加 (default: ) 以帮助从模式默认值输出。写作 .default(100).describe('Number of lines (default: 100)') 将显示默认值两次。

第二个参数接受任何实现 标准架构,包括:

  • 萨德 v4.2+(例如。 z.number(), z.string(), z.array(z.number()))
  • 选项, ArkType,以及其他与标准架构兼容的库

隐藏弃用的选项

使用Zod将选项标记为已弃用 .meta({ deprecated: true })。弃用的选项在帮助输出中隐藏,但仍适用于解析,这对向后兼容性很有用。

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke()

cli
  .command('serve', 'Start server')
  // Deprecated option: hidden from --help, still parses
  .option('--old-port 
', z.number().meta({ deprecated: true, description: 'Use --port instead' }))
  // Current option: visible in help
  .option('--port 
', z.number().describe('Port number'))
  .action((options, { console, process }) => {
    const port = options.port ?? options.oldPort
    console.log('Starting on port', port, 'from', process.cwd)
  })

cli.help()
cli.completions()
cli.parse()

当用户运行时 --help,不推荐的选项不会出现,但是 --old-port 3000 仍然有效。

括号

在命令名称中使用括号时,尖括号表示必需的命令参数,方括号表示可选参数。

在选项名称中使用括号时,尖括号表示需要字符串/数字值,而方括号表示该值是可选的。

可选性仅由括号语法决定,而不是由模式决定。 [square brackets] 使选项可选,而不管架构是否为 z.string()z.string().optional().模式的 .optional() 从未对此进行过咨询——它只影响类型强制。这意味着 z.string() 随着 [--name] 被视为可选:如果省略了标记, options.nameundefined 即使模式没有 .optional().

可选值标志-- --flag vs --flag value vs省略

用方括号声明的旗帜(--host [host])有 三种不同的运行时状态,不是两个。用户可以:

  1. 完全省略国旗 --没有 --host 完全在命令行上
  2. 光着身子传递旗帜--host 它本身没有任何价值
  3. 传递带有值的标志--host example.com

goke通过一个单一的程序处理所有三个箱子 string | undefined 类型。没有 boolean 在联盟中,裸露的旗帜被正常化 空字符串 '' 因此调用者只处理字符串:

cli
  .command('serve', 'Start the server')
  .option('--host [host]', 'Optional host override')
  .action((options) => {
    // options.host: string | undefined
    //   --host              → ''                (flag present, no value)
    //   --host example.com  → 'example.com'
    //   (omitted)           → undefined
  })

检测每个病例:

.action((options) => {
  if (options.host === undefined) {
    // Flag was not passed at all — use a sensible default
    console.log('using default host: localhost')
  } else if (options.host === '') {
    // Flag was passed bare: `--host` with no value following it
    // Treat this as an explicit "opt in, but use the default/automatic value"
    console.log('host flag passed with no value — enabling auto-discovery')
  } else {
    // Flag was passed with an explicit value
    console.log(`host = ${options.host}`)
  }
})

在大多数情况下,你不需要三重区别 --一个简单的truthy检查将“省略”和“裸旗”折叠到同一个“回退到默认”分支中:

.action((options) => {
  // `--host` bare AND omitted both fall through to the default
  const host = options.host || 'localhost'
  startServer({ host })
})

保留 === '' 检查“无值选择加入”是否是一个有意义的信号,与“省略标志”不同——例如, --direct 意思是“自动发现Chrome实例”vs --direct ws://… 意思是“连接到此特定端点”与否 --direct 意思是“不要使用直接模式”。

重大变更通知(代码6.6.0): 以前的版本显示了裸机标志 boolean true 内部a string | boolean | undefined 工会,迫使每个呼叫站点都写 typeof options.host === 'string' ? options.host : undefined使用的代码 options.host === true 要检测裸标志,必须更新为 options.host === ''基于模式的可选标志 .default(...) 不受影响——当标志被裸传递时,默认值仍然生效。

被否定的选项

允许值为的选项 false,您需要手动指定一个否定选项:

cli
  .command('build [project]', 'Build a project')
  .option('--no-config', 'Disable config file')
  .option('--config 
', 'Use a custom config file')

变量论证

命令的最后一个参数可以是可变的。要使参数可变,您必须添加 ... 到参数名称的开头:

cli
  .command('build  [...otherFiles]', 'Build your app')
  .option('--foo', 'Foo option')
  .action((entry, otherFiles, options, { console, process }) => {
    console.log(entry)
    console.log(otherFiles)
    console.log(options, process.stdin)
  })

双破折号 -- (选项结束)

-- 代币标志着期权的结束。一切之后 -- 可通过以下方式获得 options['--'] 作为一个单独的数组,不混合到位置参数中。这使您可以区分命令自己的参数和passthrough参数——与 doppler, npm, pnpm,以及 docker.

options['--']始终在场 关于推断选项类型 string[].如果没有 -- 令牌出现在命令行上,它是空数组——你永远不需要用 ||?. 或a Array.isArray 演员。

import { goke } from 'goke'
import { z } from 'zod'
import { execSync } from 'child_process'

const cli = goke('runner')

cli
  .command('run ', 'Run a script with injected environment variables')
  .option('--env ', z.enum(['dev', 'staging', 'production']).describe('Target environment'))
  .example('# Pass extra flags to the child script via --')
  .example('runner run --env staging server.js -- --port 3000 --verbose')
  .action((script, options) => {
    // runner run --env staging server.js -- --port 3000 --verbose
    // script = 'server.js'           (positional arg)
    // options.env = 'staging'         (runner's own option)
    // options['--'] = ['--port', '3000', '--verbose']  (passthrough, always string[])

    const secrets = loadSecrets(options.env)
    const extraArgs = options['--'].join(' ')
    execSync(`node ${script} ${extraArgs}`, {
      env: { ...process.env, ...secrets },
      stdio: 'inherit',
    })
  })

cli.help()
cli.completions()
cli.parse()
runner run --env staging server.js -- --port 3000 --verbose
#          ^^^^^^^^^^^^  ^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^
#          runner option  positional   passthrough (options['--'])

没有 --,旗帜像 --port 将被解析为runner选项,并以“未知选项”失败 --port“The -- 告诉goke停止解析并单独收集其余部分。

点嵌套选项

点嵌套选项将合并为单个选项。

cli
  .command('build', 'desc')
  .option('--env ', 'Set envs')
  .example('--env.API_SECRET xxx')
  .action((options, { console, process }) => {
    console.log(options, process.env.API_SECRET)
  })

默认命令

注册一个在没有其他命令匹配时使用的命令。

cli
  .command('[...files]', 'Build files')
  .option('--minimize', 'Minimize output')
  .action((files, options, { console, process }) => {
    console.log(files)
    console.log(options.minimize, process.cwd)
  })

错误处理

要全局处理命令错误,请执行以下操作:

cli.parse(process.argv).catch((error) => {
  const message = error instanceof Error ? error.stack : String(error)
  process.stderr.write(String(message) + '\n')
  process.exit(1)
})

操作后流程关闭

parse() 仅在匹配的命令操作和中间件完成后解析。这使得在解析启动后台工作、本机句柄或操作期间长时间运行的子进程的CLI后添加进程关闭代码是安全的。

import { goke } from 'goke'

const cli = goke('render')

cli
  .command('screenshot ', 'Render a screenshot')
  .action(async (file, options, { console }) => {
    await renderScreenshot(file)
    console.log('saved screenshot')
  })

await cli.parse(process.argv)

// The command is done here. Use manual exit only for runtimes that leave
// background handles alive after the user-visible work has completed.
setTimeout(() => process.exit(0), 500).unref()

设置强制出口 之后 等待的解析调用,不在操作内部。这避免了切断仍属于该命令的未决写入、日志或清理。

使用模拟控制台进行测试并退出

因为goke得到了它的注射 { fs, console, process } 根据CLI配置的运行时依赖关系,测试可以直接覆盖它们并对调用进行断言。

import { describe, expect, test, vi } from 'vitest'
import { goke, GokeProcessExit } from 'goke'

describe('deploy command', () => {
  test('writes output and exits with injected mocks', async () => {
    const stdout = { write: vi.fn void>() }
    const stderr = { write: vi.fn void>() }
    const exit = vi.fn void>()

    const cli = goke('acme', { stdout, stderr, exit })

    cli
      .command('deploy', 'Deploy the project')
      .action((options, { console, process }) => {
        console.log('deploying')
        process.exit(2)
      })

    await expect(cli.parse(['node', 'acme', 'deploy'], { run: true }))
      .rejects.toThrow(GokeProcessExit)

    expect(stdout.write).toHaveBeenCalledWith('deploying\n')
    expect(exit).toHaveBeenCalledWith(2)
    expect(stderr.write).not.toHaveBeenCalled()
  })
})

直接测试命令操作

使用 .getAction() 从命令中提取键入的动作回调。这允许您在测试中直接调用操作,而无需解析argv,同时保持操作内联以确保类型安全。

import { describe, expect, test, vi } from 'vitest'
import { goke } from 'goke'
import { z } from 'zod'

const cli = goke('mycli')

const deployCmd = cli
  .command('deploy', 'Deploy the app')
  .option('--env ', z.enum(['staging', 'production']).describe('Target environment'))
  .action((options, { console }) => {
    console.log(`Deploying to ${options.env}`)
  })

test('deploys to staging', () => {
  const stdout = { write: vi.fn void>() }
  const action = deployCmd.getAction()
  action({ env: 'staging', '--': [] }, cli.createExecutionContext({ stdout }))
  expect(stdout.write).toHaveBeenCalledWith('Deploying to staging\n')
})

返回的函数与 .action() 回调,因此TypeScript在编译时捕获错误的参数类型。如果没有登记动作, .getAction() 投掷。

使用TypeScript

import { goke } from 'goke'

const cli = goke('my-program')

不要手动键入 action 回调参数。goke根据命令签名和选项模式自动推断参数和选项类型。

import { goke } from 'goke'
import { z } from 'zod'

const cli = goke('my-program')

cli
  .command('serve ', 'Start the app')
  .option('--port 
', z.number().default(3000).describe('Port number'))
  .option('--watch', 'Watch files')
  .action((entry, options, { console, process }) => {
    // entry: string
    // options.port: number
    // options.watch: boolean
    console.log(entry, options.port, options.watch, process.cwd)
  })

在浏览器中打开

openInBrowser 在默认浏览器中打开一个URL。在非TTY环境(CI、管道输出、代理)中,它会将URL打印到stderr,从而保持stdout对JSON和结构化输出的干净。

重要提示: openInBrowser 是异步的,必须等待。没有 await,在进程退出之前,浏览器可能无法打开。
import { openInBrowser } from 'goke'

await openInBrowser('https://example.com/dashboard')

将goke CLI暴露给JustBash

使用 cli.createJustBashCommand() 将goke CLI作为单个JustBash自定义命令公开。JustBash命令名是单令牌可执行文件,但它们后面的goke CLI仍然可以使用多字子命令,如 child commandwithspaces.

import { goke } from 'goke'
import { z } from 'zod'
import { Bash } from 'just-bash'

const cli = goke('parent')

cli
  .command('child commandwithspaces', 'Run nested command')
  .option('--name ', z.string().describe('Name'))
  .action((options, { console, process }) => {
    console.log(`hello ${options.name} from ${process.cwd}`)
  })

const bash = new Bash({
  customCommands: [await cli.createJustBashCommand()],
})

await bash.exec('parent child commandwithspaces --name Tommy')

更喜欢注射 { fs, console, process } 命令实现中的助手,因此相同的命令代码在常规CLI运行时和通过JustBash桥都能清晰地工作。注射 fs 默认为Node fs/promises,以及 process.cwd / process.env / process.stdin 在节点中反映主机值,但在内部反映沙盒值 createJustBashCommand().

使用真实的JustBash进行测试

当命令读取或写入文件时,通过real测试它 just-bash 使用现有的应用程序CLI。不要在测试体内定义CLI。

import { describe, expect, test } from 'vitest'
import { Bash, InMemoryFs } from 'just-bash'
import { cli } from '../src/cli'

describe('login command', () => {
  test('writes auth state through the sandbox fs', async () => {
    const virtualFs = new InMemoryFs()
    await virtualFs.mkdir('/project', { recursive: true })

    const bash = new Bash({
      fs: virtualFs,
      cwd: '/project',
      customCommands: [await cli.createJustBashCommand()],
    })

    const result = await bash.exec('parent login --token Tommy')

    expect(result.stdout).toBe('saved credentials\n')
    expect(await virtualFs.readFile('/project/.mycli/auth.json', 'utf8')).toBe(
      '{"token":"Tommy","cwd":"/project"}',
    )
  })
})

每当CLI接触存储时,这是推荐的兼容性测试:在正常测试中运行同一CLI一次,在实际测试中运行一次 just-bash.

将CLI作为一种技能进行展示

如果使用goke构建CLI,请尽量减少技能,并将代理指向CLI帮助输出。将详细用法放在CLI代码和README中,而不是放在重复的技能文件中。


---
name: acme
description: >
  acme is a deployment CLI. Always run `acme --help` before using it
  to discover available commands, options, and usage examples.
---

# acme

Always run `acme --help` before using this CLI.
For subcommand details: `acme  --help`

用于代理友好CLI的YAML输出

当命令返回结构化数据时,在stdout上将其打印为YAML。YAML是人类可读输出和机器可处理输出之间的最佳中间地带:

  • 人类可以一目了然地阅读它——按键上没有引号,标点符号噪音比JSON少。
  • 代理商可以用 yq,相当于YAML jq,以提取特定字段或过滤结果。
  • 它比冗长的散文更有上下文效率:一个紧凑的YAML块在更少的令牌中传达相同的信息。
import { goke } from 'goke'
import { stringify } from 'yaml'

const cli = goke('deploy')

cli
  .command('status', 'Show deployment status')
  .action(async (options, { console }) => {
    const status = await fetchStatus()
    // Output structured data as YAML on stdout
    console.log(stringify(status))
  })

输出示例:

deployment: prod-v2
status: running
replicas: 3
lastDeploy: "2026-01-15T10:30:00Z"
health:
  cpu: 42%
  memory: 1.2GB

使用yq处理YAML输出

代理可以通过管道传输输出 yq 提取特定字段或过滤结果——与他们使用的方式相同 jq 使用JSON,但输出更清晰、更易读:

# Extract a single field
deploy status | yq '.deployment'

# Access nested fields
deploy status | yq '.health.cpu'

# Filter an array of results
deploy list | yq '.[] | select(.status == "running")'

# Combine multiple fields
deploy list | yq '.[] | {name: .name, status: .status}'

# Count items matching a condition
deploy list | yq '[.[] | select(.status == "error")] | length'

保持stdout干净——将非YAML发送到stderr

如果命令在stdout上输出YAML,则所有无关的内容都必须转到stderr:错误消息、进度指示器、信息日志、警告。这使得stdout可以通过管道传输 yq 而不会破坏YAML解析。

cli
  .command('deploy ', 'Deploy to environment')
  .action(async (env, options, { console }) => {
    // Progress and logs → stderr (won't pollute yq pipes)
    console.error(`Deploying to ${env}...`)
    console.error('Building artifacts...')

    const result = await deploy(env)

    // Structured result → stdout as YAML
    console.log(stringify(result))
  })

现在代理可以干净地处理输出:

# Only the YAML result reaches yq — progress lines go to the terminal
deploy deploy production | yq '.version'

如果发生错误,请抛出或写入 console.error / process.stderr,并以非零代码退出。当命令预期输出YAML时,切勿将错误文本混合到stdout中。

生成Markdown文档

generateDocs 为CLI中的每个命令创建markdown文档页面。每个页面都包含一个使用块、参数表、选项表、全局选项和示例。你会得到一个数组 { command, slug, content } 您可以根据需要将对象写入磁盘。

这对于为CLI自动生成文档并将其提供给文档平台非常有用,例如 全息照相机明特利.

// scripts/generate-docs.ts
import { goke, generateDocs } from 'goke'
import { z } from 'zod'
import fs from 'node:fs'

const cli = goke('sentry')
  .version('1.0.0')
  .help()
  .completions()

cli
  .command('event view ', 'View details of a specific event')
  .option('-w, --web', 'Open in browser')
  .option('--spans ', z.string().default('3').describe('Span tree depth limit'))
  .example('```\nsentry event view abc123\n```')

cli
  .command('event list ', 'List events for an issue')
  .option('-n, --limit 
', z.number().default(25).describe('Number of events'))
  .option('-q, --query ', 'Search query')

const pages = generateDocs({ cli })

fs.mkdirSync('docs', { recursive: true })
for (const page of pages) {
  fs.writeFileSync(`docs/${page.slug}.md`, page.content)
  console.log(`wrote docs/${page.slug}.md`)
}

跑步 npx tsx scripts/generate-docs.ts 生成以下文件 docs/index.md, docs/event-view.md, docs/event-list.md。每个命令页看起来像这样:

# event view

View details of a specific event

## Usage

\```sh
sentry event view 
\```

## Arguments

| Argument | Required | Description |
|----------|----------|-------------|
| `` | Yes | id |

## Options

| Option | Default | Description |
|--------|---------|-------------|
| `-w, --web` | - | Open in browser |
| `--spans ` | `3` | Span tree depth limit |

## Global Options

| Option | Default | Description |
|--------|---------|-------------|
| `-v, --version` | - | Display version number |
| `-h, --help` | - | Display this message |

## Examples

\```
sentry event view abc123
\```

索引页面列出了所有带有链接的命令:

# sentry

Version: 1.0.0

## Commands

| Command | Description |
|---------|-------------|
| [`event view`](./event-view.md) | View details of a specific event |
| [`event list`](./event-list.md) | List events for an issue |

## Global Options

| Option | Default | Description |
|--------|---------|-------------|
| `-v, --version` | - | Display version number |
| `-h, --help` | - | Display this message |

隐藏的命令和已弃用的选项将被自动排除。将此脚本添加到您的CI或 package.json 用于使文档与CLI代码保持同步的脚本。

贡献者备注

规则

  1. 对类型值使用基于架构的选项。
  1. 不要在中重复默认值 .describe(...) 使用时 .default().
  1. 不要手动键入操作回调参数;让戈克来推断它们。
  1. 更喜欢注射 { fs, console, process } 全球 console, process.exit,或直接 node:fs/promises 进口。
  1. 使用带有注入的隐式cwd fs 用于CLI存储。当助手需要当前的cwd语义时,传递 process.cwd 从注入的上下文到该助手。
  1. 在应用程序代码中定义CLI,并在测试中导入相同的CLI;不要在兼容性测试中构建单独的CLI。
  1. 保持行动一致。 将操作内联到 .command().option().action() 链。将其提取到单独的函数中会失去推断的类型安全性,并迫使您手动复制选项/arg类型。如果文件太大,请拆分为单独的文件,每个文件导出一个 goke() 实例并将其组合起来 .use()。参见 将大型CLI拆分为文件。要直接测试操作,请使用 .getAction() 提取键入的回调。看 直接测试命令操作.
   // BAD: action in a separate function — types are lost, args/options duplicated
   projectsCli
     .command("projects list", "List all projects")
     .action((_options, ctx) => listProjectsAction(ctx));

   // GOOD: action inline — types inferred from the chain
   projectsCli
     .command("projects list", "List all projects")
     .action((_options, { console }) => {
       // implementation here, fully typed
     });

   // GOOD: split into files with .use() when commands get large
   // commands/projects.ts
   export default goke()
     .command("projects list", "List all projects")
     .action((_options, { console }) => {
       // implementation here
     });

版本

导入 package.json 并使用其版本字段,以便CLI自动保持同步:

import pkg from './package.json' with { type: 'json' }

cli.version(pkg.version)

参考文献

CLI实例

CLI实例是通过调用 goke 功能:

import { goke } from 'goke'
const cli = goke()

郭(名字?)

创建CLI实例,可选择指定将用于在帮助和版本消息中显示的程序名称。当未设置时,我们使用basename argv[1].

cli.命令(名称、描述、配置?)

  • 类型: (name: string, description: string) => Command

创建命令实例。支持空格分隔的子命令,如 mcp login.

  • config.allowUnknownOptions: boolean 在此命令中允许未知选项。
  • config.ignoreOptionDefaultValue: boolean 不要在解析的选项中使用选项的默认值,只在帮助消息中显示它们。

cli.选项(名称、描述或模式?)

  • 类型: (name: string, descriptionOrSchema?: string | StandardJSONSchemaV1) => CLI

添加全局选项。第二个论点是:

  • A. 字符串 用作描述文本
  • A. 标准架构 (例如。 z.number().describe('Port'))--描述和默认值会自动从模式中提取

cli.use(回调)

  • 类型: (callback: (options: Opts, { fs, console, process }) => void | Promise) => CLI

注册一个在匹配的命令操作之前运行的中间件函数。中间件在经过选项解析和验证后,按照注册顺序运行。回调函数接收解析后的全局选项,根据所有 .option() 之前的呼叫 .use() 在链中,加上注射 { fs, console, process } 辅助对象。

cli.use(subCli)

  • 类型: (subCli: Goke) => CLI

将来自另一个goke实例的命令组合到此CLI中。上定义的所有命令 subCli 被合并到父级中。子CLI中的中间件和全局选项包括 复制。看 将大型CLI拆分为文件.

cli.parse(argv?)

  • 类型: `(argv = process.argv) => Promise

`

解析argv,默认运行匹配的命令,并在异步中间件和操作完成后进行解析。通过 { run: false } 在不运行匹配命令的情况下解析和检查args/选项。

cli.version(版本,自定义标志?)

  • 类型: (version: string, customFlags = '-v, --version') => CLI

cli.help(回调?)

  • 类型: (callback?: HelpCallback) => CLI

cli.outHelp()

  • 类型: () => CLI

将帮助消息打印到stdout。

cli.clone(选项?)

  • 类型: (options?: GokeOptions) => Goke

使用所有命令、选项、中间件和事件侦听器创建CLI实例的深度副本。覆盖任何 GokeOptions 克隆中的(stdout、stderr、cwd、env、fs、argv、columns、exit)不会影响原始文件。主要用于从同一CLI定义运行多个隔离解析的测试:

const cli = goke('mycli')
cli.command('build', 'Build project').action((options, { console }) => {
  console.log('building')
})
cli.help()

// In tests: override streams without touching the original CLI
const stdout = { write: vi.fn void>() }
const isolated = cli.clone({ stdout })
isolated.parse(['node', 'mycli', 'build'])
expect(stdout.write).toHaveBeenCalledWith('building\n')

cli.helpText()

  • 类型: () => string

返回格式化的帮助字符串,不打印。可用于在文档、测试或其他编程用途中嵌入帮助文本。

生成Docs({cli})

  • 类型: (options: { cli: Goke }) => DocPage[]

为每个非隐藏命令生成markdown文档页面。返回一个数组 { command, slug, content } 物体。看 生成Markdown文档 完全使用。

const cli = goke('mycli')
cli.command('build', 'Build project')
cli.option('--watch', 'Watch mode')
cli.help()

const help = cli.helpText()
// => "mycli\n\nUsage:\n  $ mycli ..."

cli.用法(文本)

  • 类型: (text: string) => CLI

cli.example(示例)

  • 类型: (example: CommandExample) => CLI

命令实例

command.option()

基本相同 cli.option 但这为特定命令添加了选项。

command.action(回调)

  • 类型: (callback: ActionCallback) => Command

命令回调首先接收位置参数,然后解析选项,然后注入 { fs, console, process } 对象。比起全球,更喜欢那些注射的助手 console, process.exit,直接 node:fs/promises 导入,使命令更容易测试,并可以在JustBash等备用运行时中运行。

command.alias(name)

  • 类型: (name: string) => Command

command.allowUnknownOptions()

  • 类型: () => Command

command.hidden()

  • 类型: () => Command

在帮助输出列表中隐藏命令。直接调用时,该命令仍然匹配并运行——只有帮助显示被抑制。适用于您不想宣传的内部、已弃用或实验性命令。

cli
  .command('internal-reset', 'Reset internal state')
  .hidden()
  .action((options, { console }) => {
    console.log('reset done')
  })

command.example.(示例)

  • 类型: (example: CommandExample) => Command

command.用法(文本)

  • 类型: (text: string) => Command

command.helpText()

  • 类型: () => string

返回此特定命令的格式化帮助字符串,而不打印它。适用于测试或以编程方式嵌入帮助文本。

事件

听命令:

cli.on('command:foo', () => {
  // Do something
})

cli.on('command:!', () => {
  // Default command
})

cli.on('command:*', () => {
  process.stderr.write(`Invalid command: ${cli.args.join(' ')}\n`)
  process.exit(1)
})

学分

许可证

麻省理工学院

目录标签

目录标签

TypeScriptClaude命令行工具CLI框架本地部署终端工具中间件链式调用CLI工具

支持客户端

Claude DesktopClaudeCursorVS Code

接入字段

传输方式(transport,传输协议)

stdio

鉴权方式(authType,认证方式)

oauth

运行时(runtime,运行环境)

Node.js

来源包(packageName,安装包名)

skills

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

stdiooauth部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

来源信息

继续浏览同类 MCP