initial commit

This commit is contained in:
EatThePooh 2026-02-15 17:38:53 +07:00
commit db4a1e24ca
10 changed files with 1977 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.emacs*
.oc
dist
node_modules
result

47
README.md Normal file
View file

@ -0,0 +1,47 @@
# CLI Template
A minimal CLI template using Stricli and Dax.
## Quick Start
```bash
nix develop # optional: provides Node.js, TypeScript, biome
npm install
npm run build
node dist/index.js --help
```
Alternatively, use Nix directly:
```bash
nix run . -- --help # runs the CLI directly
nix shell . --command name-placeholder --help # provides CLI in PATH
```
## Development
Uses TypeScript and Biome for checks:
```bash
npm run check
```
## Renaming
To rename the CLI from `name-placeholder` to your desired name:
```bash
sed -i 's/name-placeholder/your-new-name/g' package.json src/index.ts flake.nix
```
Then rebuild:
```bash
nix build .
```
In case Nix complains about the hash, run this to get the correct one and replace in flake.nix:
```bash
nix run nixpkgs#prefetch-npm-deps -- package-lock.json
```

34
biome.json Normal file
View file

@ -0,0 +1,34 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": ["**", "!!**/dist"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}

135
flake.lock generated Normal file
View file

@ -0,0 +1,135 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1770726378,
"narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1770073757,
"narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "47472570b1e607482890801aeaf29bfb749884f6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1769909678,
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "72716169fe93074c333e8d0173151350670b824c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}

34
flake.nix Normal file
View file

@ -0,0 +1,34 @@
{
description = "name-placeholder";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
git-hooks.url = "github:cachix/git-hooks.nix";
};
outputs = inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } (
{
systems = [ "x86_64-linux" ];
perSystem = { config, system, pkgs, ... }:
{
packages.default = pkgs.buildNpmPackage {
pname = "name-placeholder";
version = "0.1.0";
src = ./.;
npmDepsHash = "sha256-+30FObMk/z+ondarQzErirWkefJZR9dNGO6JPdbwSZI=";
npmBuildScript = "build";
};
devShells.default = pkgs.mkShell {
packages = with pkgs;
[ nodejs typescript-language-server biome ];
};
formatter = pkgs.nixpkgs-fmt;
};
}
);
}

1562
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

21
package.json Normal file
View file

@ -0,0 +1,21 @@
{
"name": "name-placeholder",
"version": "0.1.0",
"type": "module",
"bin": {
"name-placeholder": "./dist/index.js"
},
"dependencies": {
"@stricli/core": "^1.2.5",
"dax": "^0.45.0"
},
"devDependencies": {
"@types/node": "^25.2.3",
"tsup": "^8.5.1",
"typescript": "^5.9.3"
},
"scripts": {
"build": "tsup src/index.ts",
"check": "tsc --noEmit && biome check ."
}
}

115
src/index.ts Normal file
View file

@ -0,0 +1,115 @@
#!/usr/bin/env node
import {
buildApplication,
buildCommand,
buildRouteMap,
numberParser,
run,
} from "@stricli/core";
import { $, CommandBuilder } from "dax";
$.setPrintCommand(true);
interface EchoFlags {
count: number;
verbose: boolean;
name: string | undefined;
level: "info" | "warn" | "error" | undefined;
timeout: number | undefined;
}
const echo = buildCommand<EchoFlags, [string]>({
docs: {
brief: "Echo a message with optional flags",
fullDescription:
"This command echoes a message and demonstrates various flag types.",
},
parameters: {
aliases: {
c: "count",
v: "verbose",
n: "name",
t: "timeout",
},
flags: {
count: { kind: "counter", brief: "Number of times to repeat" },
verbose: { kind: "boolean", brief: "Enable verbose output" },
name: {
kind: "parsed",
parse: (s: string) => s,
brief: "Name to include",
optional: true,
},
level: {
kind: "enum",
values: ["info", "warn", "error"] as const,
brief: "Log level",
optional: true,
},
timeout: {
kind: "parsed",
parse: numberParser,
brief: "Timeout in ms",
optional: true,
},
},
positional: {
kind: "tuple",
parameters: [
{
parse: (s: string) => s,
placeholder: "MESSAGE",
brief: "Message to echo",
},
],
},
},
func: async (flags, message) => {
$.logStep("Starting echo command");
$.log("Flags:", flags);
$.log("Message:", message);
$.logStep("Executing echo via $");
const result = await $`echo ${message}`.stdout("piped").stderr("piped");
$.logWarn("Stderr:", result.stderr);
$.log("Stdout:", result.stdout);
if (flags.count > 0) {
$.logStep(`Repeating ${flags.count} times via commandBuilder`);
const cmd = new CommandBuilder()
.command(["echo", message])
.env({ MY_VAR: "test" })
.timeout(flags.timeout ?? 30 * 1000);
for (let i = 0; i < flags.count; i++) {
await cmd;
}
}
if (flags.name) {
$.log("Name flag was:", flags.name);
}
if (flags.level) {
$.logError(`Level set to: ${flags.level}`);
}
$.logStep("Done!");
},
});
const root = buildRouteMap({
docs: {
brief: "My CLI tool",
},
routes: {
echo,
},
});
const app = buildApplication(root, { name: "name-placeholder" });
await run(app, process.argv.slice(2), {
process:
process as /* biome-ignore lint/suspicious/noExplicitAny: Stricli types don't align with @types/node */ any,
});

16
tsconfig.json Normal file
View file

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"moduleDetection": "force",
"skipLibCheck": true
}
}

8
tsup.config.ts Normal file
View file

@ -0,0 +1,8 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm"],
clean: true,
shims: true, // Add shims for __dirname/__filename in ESM
});