$37
This skill generates a comprehensive specification document (Markdown) that serves as a blueprint for building a distributable Node.js CLI application in TypeScript. The spec is intended to be followed by a developer or coding agent to produce a fully functional, packageable CLI tool.
The specification does NOT generate code. It produces a detailed, opinionated technical
document describing every layer of the application — from package.json configuration to
command action handlers to binary packaging — so that implementation becomes a mechanical
exercise.
These are the fixed versions the spec targets. Do not deviate unless the user explicitly requests different versions.
| Component | Version |
|---|---|
| Node.js | 22.x LTS |
| TypeScript | 5.x |
| Commander.js | 12.x |
| tsup | 8.x |
| Zod | 3.x |
| Chalk | 5.x |
| Ora | 8.x |
Note: Chalk 5.x and Ora 8.x are ESM-only packages. The project uses in and targets ESM output from tsup. All imports use the extension suffix even for TypeScript source files.
"type": "module"package.json.jsInclude in the version table only when the corresponding integration is selected.
| Component | Version | When Selected |
|---|---|---|
| @inquirer/prompts | 7.x | Prompts = yes |
| conf | 13.x | User Config = yes |
| cosmiconfig | 9.x | Project Config = yes |
| better-sqlite3 | 11.x | Local Database = yes |
| drizzle-orm | 0.36.x | Local Database = yes |
| env-paths | 3.x | Local Database = yes |
| drizzle-kit | 0.28.x | Local Database = yes (dev) |
| execa | 9.x | Shell Execution = yes |
| got | 14.x | HTTP Client = yes |
| update-notifier | 7.x | Auto-update = yes |
| @yao-pkg/pkg | 5.x | Binary Packaging = yes |
| cli-table3 | 0.6.x | Table Output = yes |
| boxen | 8.x | Banner/Box UI = yes |
| glob | 11.x | File Operations = yes |
Async patterns (Polling, Inline Batch, Background Daemon) use no additional npm packages — they are implemented entirely with Node.js built-ins (
setTimeout,process.kill,child_process.spawn) and the packages already in the core stack (ora for progress, chalk for output). The async pattern type is detected from PRD.md NFRs and user stories; no new dependency version rows are needed.
The spec must include these in the dependencies configuration section (always):
commander — CLI argument parsing and command routingchalk — Terminal colour output (ESM-only, v5+)ora — Spinner / progress indicatorszod — Schema validation for config, arguments, and API payloadsAlways in devDependencies:
typescript — TypeScript compilertsup — TypeScript bundler (esbuild-based, handles ESM, shebang injection)vitest — Unit and integration testing@types/node — Node.js type declarationstsx — TypeScript execution for scripts and developmenteslint + @typescript-eslint/eslint-plugin — LintingIf Prompts = yes:
@inquirer/prompts — Modern interactive prompts (officially maintained Inquirer v9+)If User Config = yes:
conf — Persistent user-level configuration stored in OS config directoryIf Project Config = yes:
cosmiconfig — Project-level config file loading (supports .json, .yaml, .js, package.json)If Local Database = yes:
better-sqlite3 — Synchronous SQLite driver (no async complexity in CLI context)drizzle-orm — TypeScript-first ORM with schema-as-code and type-safe query builderenv-paths — OS-correct data directory resolution (macOS / Linux / Windows)@types/better-sqlite3 — TypeScript type declarations (devDependency)drizzle-kit — Migration generator and Drizzle Studio (devDependency)If Shell Execution = yes:
execa — Child process execution with better ergonomics and ESM supportIf HTTP Client = yes:
got — HTTP client with TypeScript-first design (ESM-only, v14+)If Auto-update = yes:
update-notifier — Non-blocking update checks against npm registryIf Binary Packaging = yes (devDependency):
@yao-pkg/pkg — Compiles Node.js application into standalone executablesIf Table Output = yes:
cli-table3 — Tabular terminal output with borders and alignmentIf Banner/Box UI = yes:
boxen — Bordered terminal box output (ESM-only)If File Operations = yes:
glob — File system globbing with ESM supportGenerate the spec when the user provides an application name and version that
corresponds to one of the custom applications defined in CLAUDE.md. The skill
reads all required inputs from the project's context files — no interactive Q&A is needed
for the core inputs.
The user invokes this skill by specifying the target application and version, for example:
/specgen-ts-cli my_tool v0.1.0/specgen-ts-cli my_tool v0.1.0 command:deploy/specgen-ts-cli "My Tool" v0.1.0The skill then locates the matching context folder and reads all input files automatically.
Before starting any work, resolve the application folder first (see Input Resolution below), then check CHANGELOG.md in the application folder (<app_folder>/CHANGELOG.md):
<app_folder>/CHANGELOG.md does not exist, skip this check (first-ever execution for this application).<app_folder>/CHANGELOG.md exists, scan all ## vX.Y.Z headings and determine the highest version using semantic versioning comparison."Version {requested} is lower than the current application version {highest} recorded in <app_folder>/CHANGELOG.md. Execution rejected." Do NOT proceed with any work.This skill uses standardized input resolution. Provide:
| Argument | Required | Example | Description |
|---|---|---|---|
<application> | Yes | my_tool | Application name to locate the context folder |
<version> | Yes | v0.1.0 | Version to scope processing |
command:<name> | No | command:deploy | Limit generation to a single command spec |
The application name is matched against root-level application folders:
<number>_ prefix from folder names (e.g., 1_my_tool → my_tool)| File | Resolved Path |
|---|---|
| PRD.md | <app_folder>/context/PRD.md |
| Command Models | <app_folder>/context/model/ |
| Output | <app_folder>/context/specification/ |
When a version is provided, only include user stories, NFRs, and constraints from versions
<= the provided version. For example, if v0.3.0 is specified:
[v0.1.0], [v0.2.0], [v0.3.0][v0.4.0] or laterWhen command:<name> is provided:
SPEC.md for that specific commandSPECIFICATION.md (root) gets a partial update — only that command's TOC entry is
added or updated; all other entries are preserved as-isThe specification is driven by four input sources read from the project's context files. The skill does NOT ask the user for prompts, config, packaging, or other choices — it determines these automatically from the context.
From CLAUDE.md (already loaded in context), locate the target application under the Custom Applications section. Extract:
my-tool, deploy)The application name is used to derive:
@scope/ prefix if scopedbin entry in package.jsonMyTool)Read <app_folder>/context/PRD.md. This file contains all user stories organized by
command. Extract:
## Command: <name> or ### <CommandName> section[USCLI00101] As a user, I want to...
These define the functional requirements for each command's action handler and promptsThe user stories directly inform:
Important: Items with strikethrough (~~text~~) are deprecated. List them in the
"Removed / Replaced" subsection of the traceability table. Carry version tags through
to the generated specification's traceability section.
Within the same PRD.md, each command has a ### Non Functional Requirement section
with tagged items like [NFRCLI0120]. These inform:
--output flag)Read <app_folder>/context/model/MODEL.md first as the index, then read the individual
command model files in each command subfolder.
MODEL.md provides:
Per-command files (e.g., model/deploy/model.md):
The command model directly maps to:
Command argument and option declarations in Commander.js--help text contentBefore determining optional components, check PRD.md for the following extended sections:
If PRD.md contains an # Architecture Principle section, extract patterns that affect CLI decisions:
| Pattern to Extract | How It Influences the Specification |
|---|---|
| "Container based deployment" | Include Dockerfile section; favor environment variables over config files |
| "Event-driven" | CLI may need webhook listeners or polling patterns |
| Async patterns (polling, batch, daemon) | Inform command handler async strategies |
If the section is absent, proceed with existing detection.
If PRD.md contains a # High Level Process Flow section:
If the section is absent, derive command flow from user stories only (existing behavior).
Instead of asking the user, the skill determines optional components by analyzing the
dependencies and requirements in CLAUDE.md and cross-referencing with PRD.md NFRs.
| Content Pattern | Prompts Selection |
|---|---|
| NFRs mention "interactive", "ask user", "prompt for", "wizard" | Prompts = yes |
User stories describe --interactive mode or guided setup | Prompts = yes |
User stories describe init, setup, configure commands | Prompts = yes |
| All inputs are non-interactive flags | Prompts = no |
| Content Pattern | User Config Selection |
|---|---|
| NFRs mention "remember", "save preference", "user setting", "persist" | User Config = yes |
Commands like config set, config get, login, logout exist | User Config = yes |
| NFRs mention "API key", "token storage", "credential" | User Config = yes |
| No persistent state needed | User Config = no |
| Content Pattern | Project Config Selection |
|---|---|
| NFRs mention "project configuration", "workspace config", "per-project" | Project Config = yes |
A config file like .mytoolrc, mytool.config.js, or package.json#mytool is referenced | Project Config = yes |
| Tool operates at project/workspace level | Project Config = yes |
| Tool is purely global/user-scoped | Project Config = no |
| Content Pattern | Local Database Selection |
|---|---|
| NFRs mention "run history", "execution log", "audit trail", "activity log" | Local Database = yes |
| NFRs mention "cache", "local cache", "offline", "sync state", "last sync" | Local Database = yes |
User stories describe history, log, list runs, show recent commands | Local Database = yes |
| User stories describe tracking multiple entities of the same type locally | Local Database = yes |
| NFRs mention "retain for X days", "auto-prune", "retention policy" | Local Database = yes |
| Data needs filtering, sorting, or aggregation beyond a simple list | Local Database = yes |
All persistent data fits in simple key-value pairs (use conf instead) | Local Database = no |
Local Database vs
conf: If the data has multiple rows, grows over time, needs querying, or involves relationships — use SQLite. If it is a handful of user preferences or a single token — useconf.
| Content Pattern | Shell Execution Selection |
|---|---|
| NFRs mention "run command", "execute", "spawn", "shell", "subprocess" | Shell Execution = yes |
| Commands orchestrate other CLI tools (git, docker, npm, etc.) | Shell Execution = yes |
| Build/deploy commands that invoke external binaries | Shell Execution = yes |
| Content Pattern | HTTP Client Selection |
|---|---|
| CLAUDE.md "Depends on" references any REST API or web service | HTTP Client = yes |
| NFRs mention "API call", "webhook", "REST", "HTTP", "fetch" | HTTP Client = yes |
| Commands interact with cloud providers, registries, or dashboards | HTTP Client = yes |
| Content Pattern | Auto-Update Selection |
|---|---|
| NFRs mention "notify of updates", "check for new version", "self-update" | Auto-update = yes |
| Application is distributed via npm or GitHub releases | Auto-update = yes (default) |
| Internal tool used only within a controlled environment | Auto-update = no |
| Content Pattern | Binary Packaging Selection |
|---|---|
| CLAUDE.md mentions "standalone binary", "no Node.js required", "self-contained" | Binary Packaging = yes |
| Target users are unlikely to have Node.js installed | Binary Packaging = yes |
| Distribution via GitHub releases, Homebrew, or Chocolatey is mentioned | Binary Packaging = yes |
| Published to npm for developer toolchain use | Binary Packaging = no (npm distribution only) |
Three sub-types are detected independently. A single application may use more than one.
| Content Pattern | Polling Selection |
|---|---|
User stories describe --watch, --follow, --wait, --poll flags | Polling = yes |
| NFRs say "wait until complete", "follow progress", "tail status" | Polling = yes |
| Commands trigger async operations that have a terminal state (success/failed) | Polling = yes |
| All operations complete synchronously within the API call | Polling = no |
| Content Pattern | Inline Batch Selection |
|---|---|
| User stories describe processing "all records", "all items", "bulk import/export" | Inline Batch = yes |
NFRs mention --concurrency, parallel processing, rate limiting | Inline Batch = yes |
| Commands iterate over a potentially large collection (files, API resources, DB rows) | Inline Batch = yes |
| NFRs mention "skip errors", "continue on failure", "bail on first error" | Inline Batch = yes |
| NFRs mention "resume", "checkpoint", "skip already processed" | Inline Batch = yes (+ Local Database) |
| Processing is always a single item | Inline Batch = no |
| Content Pattern | Background Daemon Selection |
|---|---|
User stories describe daemon start, daemon stop, daemon status commands | Background Daemon = yes |
| NFRs mention "background process", "persistent watcher", "always-on agent" | Background Daemon = yes |
User stories describe a worker or watcher sub-command group | Background Daemon = yes |
| NFRs mention "detach", "run in background", "PID file" | Background Daemon = yes |
| All operations complete within a single command invocation | Background Daemon = no |
Async pattern vs User Config: Any application with a Background Daemon also requires User Config = yes (to store the PID). The skill must force this dependency automatically when Background Daemon is detected.
| Content Pattern | Table Output Selection |
|---|---|
| Commands produce list output (resources, records, results) | Table Output = yes |
| NFRs mention "tabular", "list view", "columns" | Table Output = yes |
After analyzing all inputs, produce a determination summary before generating the spec:
Optional Component Determination:
- Prompts: yes (from PRD.md → init command has interactive wizard)
- User Config: yes (from PRD.md → login command stores API token)
- Project Config: yes (from PRD.md → per-project .mytoolrc support)
- Local Database: yes (from PRD.md → history command lists past runs)
- Async - Polling: yes (from PRD.md → deploy --watch flag)
- Async - Inline Batch: yes (from PRD.md → sync --all processes 1000s of items)
- Async - Daemon: no
- Shell Execution: no
- HTTP Client: yes (from CLAUDE.md → depends on My API)
- Auto-update: yes (default for npm-distributed tool)
- Binary Packaging: no (npm distribution only)
- Table Output: yes (from PRD.md → list commands produce tabular output)
- Banner/Box UI: no
- File Operations: no
If the user disagrees with any determination, allow them to override before proceeding.
Once inputs are gathered and optional components are determined, generate the specification
as a multi-file output split by command. Read the spec template at
references/spec-template.md for the exact structure and content of each section.
The template is the authoritative guide — follow it closely.
The specification is split into two categories:
SPECIFICATION.md — Shared infrastructure: package.json, TypeScript config,
build tooling, CLI entry point, shared services, UI utilities, config management,
error handling, testing strategy, and packaging.<command-name>/SPEC.md — Each command gets its own folder with a
self-contained specification covering argument/option definitions, service methods,
prompt flows, and output formatting.Important: The generated spec must use real application data from the context files:
init, deploy, status)model/<command>/model.md<app_folder>/context/specification/
├── SPECIFICATION.md ← Shared infrastructure + TOC
├── init/
│ └── SPEC.md ← Command blueprint for 'init'
├── deploy/
│ └── SPEC.md ← Command blueprint for 'deploy'
├── config/
│ └── SPEC.md ← Command blueprint for 'config'
└── ... ← One folder per command from PRD.md
SPECIFICATION.md (Root)Application metadata, description, tech stack summary, binary name, target platforms, the complete command list, and distribution strategy.
Complete package.json with "type": "module", bin entry, files, engines,
all runtime dependencies (core + selected conditional), all devDependencies, and scripts
(dev, build, typecheck, test, lint, plus platform-specific pkg scripts if Binary Packaging = yes).
Complete tsconfig.json targeting ESM. Complete tsup.config.ts with shebang injection,
ESM format, and conditional sourcemap/minify for production. ESLint configuration.
The package.json version field MUST be set to the version value derived from the
version argument provided during skill invocation (e.g., 1.0.3). If multiple versions
were provided, use the highest one.
Commander.js uses package.json version automatically for --version output. The CLI
entry point (src/cli.ts) must call .version(packageJson.version) on the Commander
program instance so that <tool> --version prints the correct version.
The version MUST also be included in JSON output when --json flag is used (e.g.,
{"version": "1.0.3", "data": {...}}).
.env File Generation from SECRET.mdGenerate a .env file at the project root by reading SECRET.md from the project root.
The .env file maps SECRET.md credential and platform values to the environment variable
names used by the CLI application. The spec must define the complete .env content with
actual values from SECRET.md.
Process:
SECRET.md from the project root# Credential section (API hosts, ports, tokens) and
# Platform section (Node.js path, etc.).env file with KEY=value pairsExample .env output (derived from SECRET.md):
# API
API_BASE_URL=http://localhost:8080/api/v1
API_KEY=
# Platform
NODE_HOME=C:\nvm4w\nodejs
Rules:
process.env)TODOlocalhost, default ports).env file must be loaded using dotenv (add as a dependency if not already present).env file is gitignoredsrc/cli.ts — the Commander.js Program setup: name, description, version, global options
(--verbose, --json, --no-color), command registration imports, and program.parse().
See references/command-patterns.md for the root program setup, global option propagation
pattern, and async error handling with parseAsync.
Full src/ directory tree with all files, named after actual commands and services
from the context.
src/types/index.ts — all shared TypeScript interfaces and type aliases used across
commands and services.
src/ui/logger.ts — chalk-based logger with info, success, warn, error,
debug (gated on --verbose) methodssrc/ui/spinner.ts — ora wrapper with typed start/succeed/fail/stop helperssrc/ui/table.ts — cli-table3 wrapper (conditional on Table Output = yes)src/ui/output.ts — Unified output handler respecting --json flagsrc/errors.ts — Base CliError class with exit code mapping. handleError() function
used in every command action handler to catch, format, and exit cleanly.
src/config/user.config.ts — conf setup with schema (Zod), typed accessors, and
migration strategy. Key names, default values, and OS storage paths.
See references/config-patterns.md.
src/config/project.config.ts — cosmiconfig loader with Zod validation. Config file
search path, supported formats, and merge strategy with defaults.
See references/config-patterns.md.
src/services/http.client.ts — got instance with base URL, auth header injection,
retry configuration, and typed error handling.
src/utils/shell.ts — execa wrapper with logging, timeout, and error handling.
src/utils/updater.ts — update-notifier integration called once at CLI startup with
non-blocking async check.
Vitest configuration, command testing patterns with process.argv mocking, service unit
testing, config testing with temp directories.
See references/testing-patterns.md.
tsup build pipeline, @yao-pkg/pkg configuration, platform targets, npm scripts for
each platform binary, GitHub Actions release workflow stub.
See references/packaging-patterns.md.
For npm-only distribution: package.json files, bin, engines, prepublishOnly
script, .npmignore, semantic versioning guidance.
src/db/schema.ts — Drizzle table definitions (all entity tables derived from model files).
src/db/client.ts — better-sqlite3 singleton with WAL mode, foreign keys, and
automatic migration runner on first open.
src/db/path.ts — env-paths based OS data directory resolution.
src/db/repositories/ — One repository class per entity exposing typed CRUD and query
methods. drizzle.config.ts, drizzle/ migration folder, and db:generate / db:migrate
/ db:studio npm scripts.
See references/database-patterns.md.
Include only the sub-sections that apply. Multiple sub-sections may be included together.
17a. Shared Signal Handling (include whenever any async pattern is yes)
src/utils/signal.ts — onSignal(cleanupFn) utility that registers SIGINT and SIGTERM
handlers, runs the cleanup function, then exits with code 130 (Ctrl+C) or 0 (SIGTERM).
Used by polling loops, batch processors, and the daemon runner to ensure Ctrl+C always
produces a clean exit rather than a Node.js stack trace.
17b. Polling (conditional — include only if Polling = yes)
src/utils/poll.ts — poll<T>(options) function with configurable interval, timeout,
spinner integration, and onTick callback for dynamic status text. Command handlers
pass --watch / --follow / --wait flags down to the poll utility. Non-watch invocation
(fire-and-forget) must remain supported when the flag is absent.
See references/async-patterns.md.
17c. Inline Batch Processing (conditional — include only if Inline Batch = yes)
src/utils/batch.ts — runBatch<TInput, TOutput>(options) function with configurable
concurrency, bail-on-error mode, per-item error collection, and onProgress callback.
src/ui/progress.ts — ProgressBar wrapper over ora showing [percent%] n/total — ETA Xs.
When Local Database = yes, include the resume pattern: services filter out already-processed
IDs from the item list by querying the repository before the batch starts.
See references/async-patterns.md.
17d. Background Daemon (conditional — include only if Background Daemon = yes)
src/daemon/runner.ts — runDaemon() function implementing the daemon's event loop;
called when the process detects MY_TOOL_DAEMON_MODE=1 in the environment before
Commander parses arguments.
src/services/daemon.service.ts — DaemonManager class with start() (spawn detached),
stop() (SIGTERM + timeout + SIGKILL), isRunning() (signal-0 check), getStatus(),
and getRecentLogs(). PID stored in conf (requires User Config = yes).
src/commands/daemon/index.ts — start, stop, status, logs sub-commands.
See references/async-patterns.md.
<command-name>/SPEC.md (Per-Command)For EACH command from PRD.md, create a folder named after the command (kebab-case) and
generate a SPEC.md inside it. Each file is self-contained — a coding agent can
implement the command after the shared infrastructure is in place.
Each command SPEC.md must include:
SPECIFICATION.md.command() chain with all arguments,
options, and description strings@inquirer/prompts sequence for interactive mode--json output and human-readable output,
including async output contracts if Polling or Inline Batch apply to this commandSee references/command-patterns.md for the canonical command registration patterns
covering simple commands, sub-command groups, confirmation prompts, --dry-run,
tabular output, global option propagation, help text conventions, and exit code table.
After all specification files are successfully generated, append an entry to CHANGELOG.md in the application folder (<app_folder>/CHANGELOG.md):
<app_folder>/CHANGELOG.md. If it does not exist, create it with:
# Changelog
- This file tracks all skill executions by version for this application.
- The highest version recorded here is the current application version.
- Skills MUST NOT execute for a version lower than the highest version in this file.
---
## {version} heading matching the current version.--- below the context header and before any existing ## vX.Y.Z section (newest-first ordering), with a new table header and the first row.| {YYYY-MM-DD} | {application_name} | specgen-ts-cli | {module or "All"} | Generated TypeScript CLI technical specification |<app_folder>/context/specification/
├── SPECIFICATION.md ← Root: TOC + shared infrastructure
├── <command-1>/
│ └── SPEC.md ← Self-contained command blueprint
├── <command-2>/
│ └── SPEC.md
└── <command-N>/
└── SPEC.md
Sample code is mandatory. Every component described in any spec file must include a
complete, self-explanatory code sample. The code must be continuous (no // ... gaps)
and usable as a direct reference by a coding agent.
These constraints are non-negotiable. Every code sample in the generated spec must follow them.
Use ESM throughout. The project sets "type": "module" in package.json. All
imports in TypeScript source use the .js extension (TypeScript resolves to .ts).
Never use require() or CommonJS module.exports.
No any types. Every function signature, variable, and generic must be explicitly
typed. Use unknown with runtime narrowing (Zod) instead of any.
Constructor injection for services. Services accept their dependencies (config, HTTP client, logger) through the constructor. No global singletons imported directly in service files. This enables clean unit testing with mocks.
Commands must not contain business logic. The command action handler is a thin