Skip to content

Instantly share code, notes, and snippets.

@roninjin10
Last active May 24, 2026 23:24
Show Gist options
  • Select an option

  • Save roninjin10/22efc76ccb4c321cf90c262ef67eea44 to your computer and use it in GitHub Desktop.

Select an option

Save roninjin10/22efc76ccb4c321cf90c262ef67eea44 to your computer and use it in GitHub Desktop.
Claude smithers incur suggestions

Features Incur Could Add to Help Smithers

Smithers is incur's most demanding consumer — every one of its 59 CLI commands is built on incur. Reading the actual integration code (apps/cli/src/index.js), there are documented "Findings" where Smithers works around incur limitations. These translate directly into features.

Tier 1: Eliminate existing workarounds (high impact, directly unblock Smithers)

1. Per-command exit code mapping

Smithers has elaborate workaround code for Finding #1: incur hardcodes exit code 4 for validation errors, but Smithers devtools commands need exit code 1. Smithers pre-validates argv before incur sees it and remaps exit codes after cli.serve() returns. Incur should let commands declare their own exit code table:

command('tree', {
  exitCodes: { validation: 1, notFound: 4 },
  // ...
})

2. CTA suppression per command / raw output mode

Finding #2: Some commands (agents capabilities, agents doctor) own stdout completely and emit raw JSON. Smithers intercepts argv before incur runs via runRawJsonAgentCommandIfMatched() and calls process.exit(0) directly — completely bypassing incur. Incur should support:

command('capabilities', {
  outputPolicy: 'raw', // no envelope, no CTAs
})

3. Global flag namespacing / opt-out

Finding #3: --json collides — incur treats it as --format json, but Smithers devtools commands use --json as a boolean "emit JSON tree" option. Smithers rewrites argv (--json-j) before incur parses it. Incur should let commands reserve or shadow global flags:

command('tree', {
  shadowGlobals: ['json'], // --json is mine, not --format json
  options: z.object({ json: z.boolean() }),
})

4. Exit callback on all paths

Line 5680: "Incur does not call the exit callback on success paths." Smithers has to check commandExitOverride separately after serve() returns. This should just work — call the exit callback with code 0 on success too.

Tier 2: Replace manual boilerplate (medium impact)

5. File-pattern routing

Smithers rewrites smithers workflow.tsxsmithers up workflow.tsx by detecting .tsx extensions before incur parses. Incur could support declarative file routing:

cli.fileRoute({ pattern: /\.tsx$/, command: 'up' })

6. Pre-parse argv middleware

Smithers has 5 separate rewriteXxxArgv() functions that transform argv before cli.serve(). Incur should support a pre-parse hook:

cli.beforeParse((argv) => {
  // transform, rewrite, validate
  return argv
})

7. Long-running command primitives

Commands like logs --follow and tree --watch manually set up AbortController, SIGINT/SIGTERM handlers, and cleanup. Incur could provide:

command('logs', {
  streaming: true,
  async *run(c) {
    // c.signal is an AbortSignal wired to SIGINT/SIGTERM
    // cleanup runs automatically on abort
    for await (const event of stream) {
      if (c.signal.aborted) break
      yield event
    }
  }
})

8. Semantic MCP tool registration

Smithers maintains two separate MCP servers: one "raw" (1:1 CLI→tool mapping via incur) and one "semantic" (hand-crafted tool names/descriptions for better agent UX). Incur could support MCP tool transformers:

cli.mcp({
  surface: 'semantic',
  transform: (tool) => ({
    name: `smithers_${tool.name}`,
    description: rewriteForAgents(tool.description),
  })
})

Tier 3: Strategic features for the orchestrator use case

9. Interactive prompts with agent fallback

Smithers has approval gates (<Approval>, <HumanTask>) that need human input. Incur could add structured prompt primitives that render as interactive TUI for humans and structured JSON requests for agents:

const decision = await c.prompt.select({
  message: 'Approve this task?',
  choices: ['approve', 'deny', 'skip'],
})

10. Event streaming on fetch handler (SSE)

Smithers exposes an HTTP server (up --serve --port 7331). For real-time workflow monitoring, incur's cli.fetch handler could support Server-Sent Events or WebSocket subscriptions alongside request/response.

11. Dynamic command registration / plugins

Smithers has .smithers/workflows/ where users add custom workflows. These could auto-contribute CLI commands. Incur could support lazy command registration from external packages:

cli.plugin('./workflows/', {
  pattern: '*.tsx',
  mount: (file) => command(file.name, { /* ... */ })
})

12. TypeScript config files

Smithers already uses .smithers/smithers.config.ts. Incur's config system only supports JSON loaders. Since incur runs on Bun, it could natively support .ts config files.

13. Daemon lifecycle management

smithers up --detach forks a background process. Incur could provide built-in daemon primitives: PID file management, start/stop/status lifecycle, and log file routing — since this is a common pattern for CLI tools that manage long-running processes.


Summary

The highest-ROI features are 1-4 — they eliminate actual workaround code in Smithers today (roughly 200 lines of argv rewriting, exit code remapping, and pre-incur interception). Features 5-8 replace repeated boilerplate patterns. Features 9-13 are strategic bets that align incur with orchestrator/daemon use cases where the CLI is a control plane, not a one-shot tool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment