npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agenttk

v0.1.4

Published

A TypeScript toolkit for building deterministic, agent-friendly CLIs.

Readme

AgentTK

npm version Verify license: MIT

AgentTK is a small TypeScript toolkit for building deterministic, agent-friendly CLIs. It is a framework for agent-authored CLIs, not a built-in CLI generator.

It gives you a clean core for tools that need:

  • deterministic command dispatch and built-in help
  • structured success and failure envelopes
  • JSON-first output for agents
  • concise human output for operators
  • reusable auth, lookup, adapter, and config patterns
  • dry-run semantics for mutation commands
  • lightweight test helpers for CLI behavior

What it is

AgentTK is a narrow toolkit for building reliable CLIs with a stable machine-facing contract.

It is designed for a workflow like this:

  1. an agent reads a skill, API, or integration contract
  2. the agent writes the actual CLI code
  3. AgentTK supplies the reusable plumbing so the tool does not have to reinvent it

Use it when you want:

  • predictable command dispatch with alias and help support
  • machine-readable results without string parsing
  • simple human output layered on top of the same result envelope
  • reusable framework blocks for auth, lookup, config, and adapter contracts
  • small test helpers instead of shell-heavy CLI tests

What it is not

AgentTK intentionally does not include:

  • a built-in CLI generator
  • plugin systems
  • workflow engines
  • auth doctor flows
  • provenance helpers
  • dynamic runtime loading
  • provider SDK wrappers
  • domain-specific adapters

Product boundary

AgentTK owns

  • runtime and command structure
  • help metadata and rendering
  • result envelopes
  • validation and corrective guidance
  • dry-run semantics
  • auth, lookup, adapter, and config primitives
  • test helpers

Your agent owns

  • reading the skill or API contract
  • deciding the command surface
  • writing the actual tool code
  • composing the AgentTK primitives correctly

The downstream tool repo owns

  • provider integrations
  • business rules
  • safety constraints
  • domain quirks
  • live operational behavior

Install

npm install agenttk

If you want schema validation:

npm install zod

Example

import { createTool, defineCommand, ok } from 'agenttk'

const tool = createTool({
  name: 'demo',
  description: 'Example CLI built with AgentTK',
  commands: [
    defineCommand({
      name: 'hello',
      description: 'Say hello',
      aliases: ['hi'],
      usage: 'demo hello [name]',
      examples: ['demo hello', 'demo hi Dushyant'],
      handler: async ({ rawArgs }) =>
        ok({
          type: 'greeting',
          record: { message: `hello, ${rawArgs[0] ?? 'world'}` }
        })
    })
  ]
})

await tool.run(process.argv.slice(2))

Validation + dry-run

import { z } from 'zod'
import { asDryRun, createTool, defineCommand, isFailure, ok, validateInput } from 'agenttk'

const schema = z.object({
  title: z.string().min(1, 'title is required'),
  dryRun: z.boolean().default(false)
})

const tool = createTool({
  name: 'tasks',
  commands: [
    defineCommand({
      name: 'add',
      handler: async () => {
        const parsed = validateInput(schema, { title: '', dryRun: true }, {
          nextStep: 'Run tasks add --title "Send estimate" [--dry-run]'
        })

        if (isFailure(parsed)) return parsed

        const result = ok({
          type: 'task',
          id: 'task-123',
          destination: 'demo_tasks',
          record: { title: parsed.title }
        })

        return parsed.dryRun ? asDryRun(result) : result
      }
    })
  ]
})

API

Core runtime

  • createTool
  • defineCommand
  • ok
  • fail
  • isFailure
  • renderResult

Validation and command behavior

  • validateInput
  • validationError
  • expectedPayloadShape
  • nextStepGuidance
  • asDryRun

Auth blocks

  • authRequired
  • authInvalid
  • accountMismatch
  • requireAuth

Lookup resolution

  • notFound
  • ambiguousMatch
  • resolveById
  • resolveByQuery
  • resolveOne

Adapter contracts

  • defineAdapter
  • supportsCapability
  • requireCapability
  • adapterFailure
  • unsupportedCapability

Config conventions

  • defineProfile
  • loadConfig
  • validateConfig
  • missingConfig
  • malformedConfig
  • selectProfile

Testing helpers

  • runTool
  • expectOk
  • expectFailure
  • expectDryRun
  • expectAuthFailure
  • expectLookupFailure
  • expectAdapterFailure
  • expectConfigFailure
  • authFailureFixture
  • lookupCandidatesFixture
  • fakeAdapter

Auth preflight

import { createTool, defineCommand, ok, requireAuth } from 'agenttk'

const tool = createTool({
  name: 'calendar',
  commands: [
    defineCommand({
      name: 'sync',
      handler: async () => {
        const auth = await requireAuth(async () => ({
          ok: false,
          code: 'AUTH_REQUIRED',
          provider: 'google',
          nextStep: 'Run calendar auth login'
        }))

        if (auth !== true) return auth

        return ok({ type: 'sync', record: { status: 'started' } })
      }
    })
  ]
})

Adapter contracts

import {
  adapterFailure,
  createTool,
  defineAdapter,
  defineCommand,
  requireCapability
} from 'agenttk'

const adapter = defineAdapter({
  provider: 'google',
  capabilities: ['tasks.read']
})

const tool = createTool({
  name: 'tasks',
  commands: [
    defineCommand({
      name: 'add',
      handler: async () => {
        const capability = requireCapability(adapter, 'tasks.write', {
          operation: 'createTask',
          nextStep: 'Reconnect with write scopes'
        })

        if (capability !== true) return capability

        return adapterFailure('Google API timed out', {
          provider: 'google',
          operation: 'createTask',
          category: 'timeout',
          retryable: true,
          nextStep: 'Retry in a few seconds'
        })
      }
    })
  ]
})

Provider-specific SDK calls and object models should stay in the downstream repo. AgentTK only owns the contract boundary, capability checks, and normalized failure shapes.

Config conventions

import { createTool, defineCommand, isFailure, loadConfig, ok } from 'agenttk'
import { z } from 'zod'

const schema = z.object({
  account: z.string().min(1),
  apiBaseUrl: z.string().url()
})

const config = loadConfig(schema, {
  env: { apiBaseUrl: process.env.API_BASE_URL },
  profiles: {
    work: { account: '[email protected]', apiBaseUrl: 'https://api.example.com' }
  },
  profile: 'work'
}, {
  expected: '{"account":"[email protected]","apiBaseUrl":"https://api.example.com"}',
  nextStep: 'Set API_BASE_URL or choose a valid profile'
})

if (isFailure(config)) return config
return ok({ type: 'config', record: config })

Load order is predictable: base config, then selected profile, then env overrides. AgentTK stays out of secrets management and provider-specific config policy.

Lookup resolution

import { ambiguousMatch, createTool, defineCommand, notFound, ok, resolveOne } from 'agenttk'

const tool = createTool({
  name: 'tasks',
  commands: [
    defineCommand({
      name: 'pick',
      handler: async () => {
        const matches = [
          { id: 'task-1', label: 'Invoice follow-up', description: 'Daily Focus' },
          { id: 'task-2', label: 'Invoice draft', description: 'Backlog' }
        ]

        const resolved = resolveOne({ query: 'invoice' }, matches, {
          nextStep: 'Retry with an explicit id'
        })

        if (!resolved.ok) return resolved
        return ok({ type: 'task', id: resolved.candidate.id, record: resolved.candidate })
      }
    })
  ]
})

Testing helpers

Use the richer assertions when you want cheap confidence around framework primitives, not shell-heavy rechecks of every field by hand.

import test from 'node:test'
import { authFailureFixture, expectAuthFailure, requireAuth } from 'agenttk'

test('auth failure stays structured', async () => {
  const auth = await requireAuth(authFailureFixture({
    code: 'ACCOUNT_MISMATCH',
    currentAccount: '[email protected]',
    expectedAccount: '[email protected]'
  }))

  expectAuthFailure(auth, {
    code: 'ACCOUNT_MISMATCH',
    provider: 'google',
    currentAccount: '[email protected]',
    expectedAccount: '[email protected]'
  })
})

Keep using plain runTool(...) tests for command wiring and output mode checks. Use the richer helpers when you want fast, data-first checks around auth, lookup, dry-run, adapter, and config behavior.

Examples

  • examples/minimal-cli/ - basic runtime, help, and JSON output
  • examples/tasks-cli/ - validation, dry-run, and command-style mutations
  • examples/adapter-cli/ - capability checks and normalized adapter failures
  • examples/config-cli/ - profile selection and config diagnostics
  • examples/testing-fixtures/ - richer framework-aware test assertions
node examples/minimal-cli/index.mjs hello --json
node examples/minimal-cli/index.mjs help
node examples/tasks-cli/index.mjs add --title "Send estimate" --dry-run --json
node examples/tasks-cli/index.mjs add
node examples/adapter-cli/index.mjs status --json
node examples/config-cli/index.mjs show --profile work

Development

npm run build
npm test
npm run examples:smoke
npm run verify

Changelog

See CHANGELOG.md.

Release discipline

For normal releases:

npm version patch
git push origin main --follow-tags

Rules:

  • release only from a clean main
  • let CI verify before trusting the tag
  • keep package.json, npm, and GitHub releases aligned
  • document user-visible changes in CHANGELOG.md