specgen-ts-cli
TypeScriptで書かれた配布可能なNode.js CLIアプリケーションの構築に向けた詳細な仕様書を生成します。Commander.jsによるコマンド解析、tsupによるバンドリング、@yao-pkg/pkgによるWindows・macOS・Linux対応のクロスプラットフォームバイナリパッケージング機能を使用できます。インタラクティブプロンプト(Inquirer.js)、永続的なユーザー設定(conf)、プロジェクトレベル設定(cosmiconfig)、シェル実行(execa)、HTTPクライアント(got)、プラグインシステム、自動更新チェック機能をアプリケーションのニーズに応じて設定可能です。標準化された入力:アプリケーション名(必須)、バージョン(必須)、コマンド(オプション)。ユーザーが新しいCLIツール、コマンドラインアプリケーション、ターミナルツール、開発者ツールの仕様書・設計書・技術仕様の作成を依頼された場合に使用します。「CLIの仕様を作成してほしい」「TypeScript CLIを設計してほしい」「CLIツールの技術仕様を書いてほしい」といった要望の際にも対応します。
description の原文を見る
Generate a detailed specification document for building a distributable Node.js CLI application written in TypeScript. Uses Commander.js for command parsing, tsup for bundling, and @yao-pkg/pkg for cross-platform binary packaging (Windows, macOS, Linux). Interactive prompts (Inquirer.js), persistent user config (conf), project-level config (cosmiconfig), shell execution (execa), HTTP client (got), plugin system, and auto-update checking are configurable based on application needs. Standardized input: application name (mandatory), version (mandatory), command (optional). Use this skill whenever the user asks to create a spec, specification, blueprint, or technical design document for a new CLI tool, command-line application, terminal tool, or developer tool. Also trigger when the user says things like "spec out a new CLI", "design a TypeScript CLI", "write a technical spec for my CLI tool", "scaffold spec for a Node CLI", or any request describing a command-line application. Even if the user only mentions a subset (e.g., "CLI with config file support" or "distributable Node tool"), this skill likely applies — ask and confirm.
SKILL.md 本文
TypeScript CLI Application Specification Generator
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.
Technology Stack
Core Stack (Always Included)
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
"type": "module"inpackage.jsonand targets ESM output from tsup. All imports use the.jsextension suffix even for TypeScript source files.
Optional Integration Versions
Include 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.
Core Dependencies (package.json)
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 payloads
Always 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— Linting
Conditional Dependencies
If Prompts = yes:
@inquirer/prompts— Modern interactive prompts (officially maintained Inquirer v9+)
If User Config = yes:
conf— Persistent user-level configuration stored in OS config directory
If 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 support
If 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 registry
If Binary Packaging = yes (devDependency):
@yao-pkg/pkg— Compiles Node.js application into standalone executables
If Table Output = yes:
cli-table3— Tabular terminal output with borders and alignment
If Banner/Box UI = yes:
boxen— Bordered terminal box output (ESM-only)
If File Operations = yes:
glob— File system globbing with ESM support
When the Skill Triggers
Generate 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.0
The skill then locates the matching context folder and reads all input files automatically.
Version Gate
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):
- If
<app_folder>/CHANGELOG.mddoes not exist, skip this check (first-ever execution for this application). - If
<app_folder>/CHANGELOG.mdexists, scan all## vX.Y.Zheadings and determine the highest version using semantic versioning comparison. - Compare the requested version against the highest version:
- If requested version >= highest version: proceed normally.
- If requested version < highest version: STOP immediately. Print:
"Version {requested} is lower than the current application version {highest} recorded in <app_folder>/CHANGELOG.md. Execution rejected."Do NOT proceed with any work.
Input Resolution
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 |
Application Folder Resolution
The application name is matched against root-level application folders:
- Strip any leading
<number>_prefix from folder names (e.g.,1_my_tool→my_tool) - Match case-insensitively against the provided application name
- Accept snake_case, kebab-case, or title-case input (all match the same folder)
- If no match found, list available applications and stop
Auto-Resolved Paths
| File | Resolved Path |
|---|---|
| PRD.md | <app_folder>/context/PRD.md |
| Command Models | <app_folder>/context/model/ |
| Output | <app_folder>/context/specification/ |
Version Filtering
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:
- Include items tagged
[v0.1.0],[v0.2.0],[v0.3.0] - Exclude items tagged
[v0.4.0]or later - Version comparison uses semantic versioning order
Command Filtering
When command:<name> is provided:
- Only generate the
SPEC.mdfor that specific command - Other existing command spec files remain untouched
SPECIFICATION.md(root) gets a partial update — only that command's TOC entry is added or updated; all other entries are preserved as-is
Gathering Input
The 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.
Input 1: Application Name (from CLAUDE.md)
From CLAUDE.md (already loaded in context), locate the target application under the Custom Applications section. Extract:
- Application name: The section heading (e.g., "My Tool", "Deploy CLI")
- Binary name: The kebab-case executable name (e.g.,
my-tool,deploy) - Application description: The description paragraph below the heading
- Target audiences: The "Used by" or "Consumers" list
- Dependencies: The "Depends on" list — primary source for determining optional components
The application name is used to derive:
- Package name: kebab-case of the application name with
@scope/prefix if scoped - Binary name: The
binentry inpackage.json - Main TypeScript namespace: PascalCase (e.g.,
MyTool)
Input 2: User Stories (from PRD.md)
Read <app_folder>/context/PRD.md. This file contains all user stories organized by
command. Extract:
- Commands: Each
## Command: <name>or### <CommandName>section - User stories per command: Tagged items like
[USCLI00101] As a user, I want to...These define the functional requirements for each command's action handler and prompts
The user stories directly inform:
- Which arguments and options each command exposes
- What interactive prompts are shown (if any)
- What output the command produces (table, JSON, plain text)
- Which service methods are called
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.
Input 3: Non-Functional Requirements (from PRD.md)
Within the same PRD.md, each command has a ### Non Functional Requirement section
with tagged items like [NFRCLI0120]. These inform:
- Output format requirements (JSON, table, plain text,
--outputflag) - Performance constraints (timeout, caching)
- Security requirements (credential storage, token handling)
- Platform constraints (Windows compatibility, PATH requirements)
Input 4: Command Model (from model/ folder)
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:
- Summary table of all commands with argument/option counts
- Links to each command's detailed model files
Per-command files (e.g., model/deploy/model.md):
- Complete argument definitions with types and descriptions
- Option definitions with flags, types, defaults
- Config keys read or written by this command
- External services or APIs called
The command model directly maps to:
Commandargument and option declarations in Commander.js- Zod schemas for config validation
- Service interface method signatures
--helptext content
PRD.md Extended Sections
Before determining optional components, check PRD.md for the following extended sections:
Architecture Principle Extraction
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.
High Level Process Flow Extraction
If PRD.md contains a # High Level Process Flow section:
- Process flows describing CLI-initiated operations inform command action handler sequences
- Each flow step can emit a spinner/progress update
- Error handling chains follow the flow's error paths
If the section is absent, derive command flow from user stories only (existing behavior).
Determining Optional Components
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.
Prompts Detection
| 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 |
User Config Detection
| 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 |
Project Config Detection
| 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 |
Local Database Detection
| 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.
Shell Execution Detection
| 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 |
HTTP Client Detection
| 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 |
Auto-Update Detection
| 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 |
Binary Packaging Detection
| 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) |
Async Pattern Detection
Three sub-types are detected independently. A single application may use more than one.
Polling Detection
| 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 |
Inline Batch Detection
| 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 |
Background Daemon Detection
| 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.
Table Output Detection
| Content Pattern | Table Output Selection |
|---|---|
| Commands produce list output (resources, records, results) | Table Output = yes |
| NFRs mention "tabular", "list view", "columns" | Table Output = yes |
Summary of Determination
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.
Generating the Specification
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:
- Root
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. - Per-command
<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:
- Commands must use the actual command names from PRD.md (e.g.,
init,deploy,status) - Arguments and options must match the actual definitions in
model/<command>/model.md - Service methods must map to the actual user stories
- Config keys must match what the model defines
- Version tags on every user story ID, NFR ID, constraint ID
- Removed / Replaced items listed for deprecated items
Output Structure
<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
What Goes in SPECIFICATION.md (Root)
1. Project Overview
Application metadata, description, tech stack summary, binary name, target platforms, the complete command list, and distribution strategy.
2. package.json Configuration
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).
3. TypeScript & Build Configuration
Complete tsconfig.json targeting ESM. Complete tsup.config.ts with shebang injection,
ESM format, and conditional sourcemap/minify for production. ESLint configuration.
3a. Application Version 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": {...}}).
3b. .env File Generation from SECRET.md
Generate 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:
- Read
SECRET.mdfrom the project root - Extract relevant values from
# Credentialsection (API hosts, ports, tokens) and# Platformsection (Node.js path, etc.) - Map each value to the corresponding environment variable name used by the CLI
- Generate the
.envfile withKEY=valuepairs
Example .env output (derived from SECRET.md):
# API
API_BASE_URL=http://localhost:8080/api/v1
API_KEY=
# Platform
NODE_HOME=C:\nvm4w\nodejs
Rules:
- Only include variables that are actually used by the application (via
process.env) - Use actual values from SECRET.md — never use placeholders or
TODO - If SECRET.md does not exist or a value is not found, use sensible defaults for local
development (e.g.,
localhost, default ports) - The
.envfile must be loaded usingdotenv(add as a dependency if not already present) - The
.envfile is gitignored
4. Application Entry Point
src/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.
5. Project Directory Structure
Full src/ directory tree with all files, named after actual commands and services
from the context.
6. Shared Types
src/types/index.ts — all shared TypeScript interfaces and type aliases used across
commands and services.
7. Terminal UI Utilities
src/ui/logger.ts— chalk-based logger withinfo,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--jsonflag
8. Error Handling
src/errors.ts — Base CliError class with exit code mapping. handleError() function
used in every command action handler to catch, format, and exit cleanly.
9. User Configuration Management (conditional — include only if User Config = yes)
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.
10. Project Configuration Management (conditional — include only if Project Config = yes)
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.
11. HTTP Client Setup (conditional — include only if HTTP Client = yes)
src/services/http.client.ts — got instance with base URL, auth header injection,
retry configuration, and typed error handling.
12. Shell Execution Utilities (conditional — include only if Shell Execution = yes)
src/utils/shell.ts — execa wrapper with logging, timeout, and error handling.
13. Auto-Update Notifier (conditional — include only if Auto-update = yes)
src/utils/updater.ts — update-notifier integration called once at CLI startup with
non-blocking async check.
14. Testing Strategy
Vitest configuration, command testing patterns with process.argv mocking, service unit
testing, config testing with temp directories.
See references/testing-patterns.md.
15. Packaging & Distribution (conditional — include only if Binary Packaging = yes)
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.
16. Local Database (conditional — include only if Local Database = yes)
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.
17. Async Patterns (conditional — include only if any async pattern is detected)
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.
What Goes in Each <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:
- Header with command name and back-reference to root
SPECIFICATION.md - Traceability: user story IDs, NFR IDs, constraint IDs
- Command definition: full Commander.js
.command()chain with all arguments, options, and description strings - Argument & Option contracts: Zod schema for validation, complete option flag table
- Prompt flow (conditional):
@inquirer/promptssequence for interactive mode - Service interface: methods this command calls with TypeScript signatures
- Service implementation: full business logic, no terminal I/O
- Output contract: exact structure for
--jsonoutput and human-readable output, including async output contracts if Polling or Inline Batch apply to this command - Error cases: every error type, exit code, and user-facing message
- Complete code samples for every component
See 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.
Changelog Append
After all specification files are successfully generated, append an entry to CHANGELOG.md in the application folder (<app_folder>/CHANGELOG.md):
- Read
<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. --- - Search for a
## {version}heading matching the current version. - If the section exists: append a new row to its table.
- If the section does not exist: insert a new section after the
---below the context header and before any existing## vX.Y.Zsection (newest-first ordering), with a new table header and the first row. - Row format:
| {YYYY-MM-DD} | {application_name} | specgen-ts-cli | {module or "All"} | Generated TypeScript CLI technical specification | - Never modify or delete existing rows.
Output Format
<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.
Constraints
These constraints are non-negotiable. Every code sample in the generated spec must follow them.
Universal Constraints
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
orchestrator: parse options → call service → format output → handle errors. All domain
logic lives in src/services/.
Separate output from logic. Service methods return typed data structures. The command
handler is responsible for formatting and printing. Services never call console.log
or manipulate terminal state.
Always handle exit codes. Every command action handler wraps its body in
try/catch. On error: print formatted message, call process.exit(1) (or the
appropriate code). On success: process.exit(0) is implicit.
ESM import paths use .js extension. In TypeScript source files:
import { UserConfig } from '../config/user.config.js' // ✓ correct
import { UserConfig } from '../config/user.config' // ✗ wrong
All user-facing strings go through the logger or output module. Never call
console.log directly in commands or services. Use logger.info(), logger.error(),
output.print(), etc.
Zod for all external data. Any data from config files, API responses, or CLI arguments that crosses a system boundary must be parsed with a Zod schema before use.
Use @inquirer/prompts not inquirer. The modern scoped package is the officially
maintained version. Import individual prompt functions, not the legacy inquirer default.
Conditional Constraints
If Binary Packaging = yes:
- Use dynamic
import()for any modules not compatible with pkg's static analysis - Avoid
__filename/__dirname— useimport.meta.urlwithfileURLToPath - All asset files (templates, default configs) must be embedded via
pkg'sassetsconfig
If User Config = yes:
- Credentials and tokens must be stored using
conf(uses OS keychain on macOS) - Never write secrets to plain text files manually
- The
confschema must be defined with Zod and validated on read
If Project Config = yes:
- Config loading must be non-fatal if no config file is found (use defaults)
- Loaded config must be merged with defaults using a predictable precedence order: CLI flags > environment variables > project config file > user config > hardcoded defaults
If HTTP Client = yes:
- All API calls must use the shared
gotinstance (never rawfetchoraxios) - HTTP errors must be caught, parsed for
statusCode, and rethrown asCliError - Authentication tokens must come from the config system, never hardcoded
If Local Database = yes:
- All database access must go through repository classes — services never use
dbdirectly - Repositories receive
DbClientvia constructor injection — no globalgetDb()calls inside service files (only in the composition rootcli.tsor service constructors) - Every write statement (
insert,update,delete) must call.run()to execute — Drizzle returns a prepared statement object that is not automatically executed - All multi-step writes that must succeed or fail together must use
db.transaction() - JSON columns must be serialised with
JSON.stringifyon insert andJSON.parseon read — never store raw objects intextcolumns - Migration files in
drizzle/are committed to source control; never edit generated SQL files after commit — always generate new ones viadrizzle-kit generate - The database file path must come from
getDatabasePath()— never hardcode a path
If any Async Pattern is yes (Polling, Inline Batch, or Background Daemon):
- Every long-running loop MUST register an
onSignalhandler before entering the loop and dispose it via the returned function when the loop exits normally - Ctrl+C must NEVER produce a Node.js stack trace — the signal handler catches it,
runs cleanup, and calls
process.exit(130) - SIGTERM must exit cleanly with code
0— it signals intentional shutdown (e.g.daemon stop), not an error
If Polling = yes:
- Every command with a watch flag must also work without it (fire-and-forget behaviour
is always supported;
--watch/--followis additive) - The polling timeout must be configurable via a
--timeout <seconds>option; the default (300s) is a maximum, not a guarantee
If Inline Batch = yes:
- Concurrency must be configurable via
--concurrency <n>— never hardcode parallelism - Both
--bail(stop on first error) and the default (collect all errors, continue) must be supported - Exit code must be
1when any items fail, even if the majority succeeded - The
onProgresscallback updates the spinner — service code never touches ora directly
If Background Daemon = yes:
- The daemon process is the SAME binary launched with
MY_TOOL_DAEMON_MODE=1in the environment — never ship a separate daemon binary - The daemon entry point check in
cli.tsmust appear BEFORE Commander parsesargv, so the daemon mode is invisible to all user-facing--helpoutput - PID storage requires User Config (
conf) — Background Daemon detection automatically forces User Config = yes in the determination summary DaemonManager.isRunning()must use signal-0 (process.kill(pid, 0)) to test process liveness — never rely on PID alone (PIDs are reused by the OS)
Principles Embedded in the Spec
Always-Applicable Principles
- ESM-first TypeScript with strict mode (
"strict": true) - Thin command action handlers — orchestrate, don't implement
- Services are pure TypeScript — no CLI concerns, fully testable
- Single output module respects
--jsonflag for machine-readable output - Every command has
--helptext that matches the user stories - Errors produce clean, human-friendly messages with non-zero exit codes
- Verbose mode (
--verbose,-v) reveals internal steps vialogger.debug() - All file paths use
node:pathandnode:url— no raw string concatenation
Conditional Principles
- If Prompts = yes: Interactive and non-interactive modes are both supported. Every prompt has a corresponding flag so the command can run non-interactively in CI.
- If User Config = yes: The
configcommand exposesget,set,list, andresetsub-commands for user-visible settings. - If Local Database = yes: SQLite is the single source of truth for all structured
local data.
confremains for user preferences and tokens; SQLite is for anything with multiple rows, history, or relational structure. The database opens and migrates automatically on first command run — no manualinit-dbstep required. - If Polling = yes: Every watched command has a non-watch path that exits immediately.
The
poll()utility owns all timing and spinner state; the command handler only provides thecheckfunction and interprets the final result. - If Inline Batch = yes: The
runBatch()utility owns concurrency and error collection; the service owns per-item logic; the command handler owns progress display and final output. These three responsibilities must never be mixed. - If Background Daemon = yes: The daemon runner (
runDaemon) is a plain async function — it has no Commander, no prompts, and no user interaction. Its only interface isMY_TOOL_DAEMON_MODE=1(start) and SIGTERM (stop). - If Binary Packaging = yes: The binary must work with zero Node.js dependency on the target machine. Asset embedding and path resolution must be validated for each platform.
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- rashidee
- リポジトリ
- rashidee/co2-skills
- ライセンス
- MIT
- 最終更新
- 2026/4/26
Source: https://github.com/rashidee/co2-skills / ライセンス: MIT