initial commit
This commit is contained in:
commit
db4a1e24ca
10 changed files with 1977 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.emacs*
|
||||||
|
.oc
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
result
|
||||||
47
README.md
Normal file
47
README.md
Normal 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
34
biome.json
Normal 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
135
flake.lock
generated
Normal 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
34
flake.nix
Normal 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
1562
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal 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
115
src/index.ts
Normal 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
16
tsconfig.json
Normal 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
8
tsup.config.ts
Normal 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
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue