@master4n/master-cli 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,2 @@
1
- export * from './HRAInput';
2
1
  export * from './InquirerPrompt';
3
2
  export * from './ProcessInfo';
@@ -1,2 +1,8 @@
1
- /**Display the welcome art and message*/
1
+ /**
2
+ * The welcome banner — master-cli's signature.
3
+ *
4
+ * Rendered only on an interactive terminal (and never with `--json`), and
5
+ * written to **stderr**, so stdout stays a clean machine-readable channel for
6
+ * agents while humans still get the full experience.
7
+ */
2
8
  export declare const logoWelcome: () => Promise<void>;
@@ -1,4 +1,3 @@
1
- import { InquirerOptions } from '../interface';
2
1
  export declare function Logger(): {
3
2
  info: (info: string) => void;
4
3
  warn: (warn: string) => void;
@@ -9,42 +8,22 @@ export declare function CommandBuilder(instance: any): {
9
8
  add: (alias: string, describe: string, builder: object, handler: (argv: any) => any) => void;
10
9
  };
11
10
  /**
12
- * Function to get the current user's name based on the operating system
13
- * @returns user
11
+ * Get the current user's name based on the operating system.
14
12
  */
15
13
  export declare function getCurrentUserName(): string;
16
14
  /**
17
- * Function to create a hidden cache directory
15
+ * Create the hidden cache directory (`~/.mfn/cache`) if it doesn't exist.
18
16
  */
19
17
  export declare function createHiddenCacheDirectory(): void;
20
18
  /**
21
- * Get the location of cache directory.
22
- * @returns cacheDir.
19
+ * Get the location of the cache directory (`~/.mfn/cache`).
23
20
  */
24
21
  export declare function getCacheDirectory(): string;
25
22
  /**
26
- * Validate arugment of type numbers and range of 1 to 1000.
27
- * @param args
28
- * @returns true if valid number
23
+ * Validate argument(s) of type number within the range 1 to 1000.
24
+ * @returns true if every argument is a valid in-range number.
29
25
  */
30
26
  export declare function validateNumberArguments(...args: number[]): boolean;
31
- /**
32
- * Validate arugment of type numbers.
33
- * @param args
34
- * @returns true if valid number
35
- */
36
- export declare function validatePositiveNumber(...args: number[]): boolean;
37
- /**
38
- * Currency formatter function
39
- * @param value
40
- * @param currency
41
- * @param locale
42
- * @returns
43
- */
44
- export declare function formatCurrency(value: number, currency?: string, locale?: string): string;
45
- export declare function inquirerPrompt(options: Array<InquirerOptions>): Promise<any>;
46
27
  export declare function getLatestVersion(packageName: string): string;
47
- export declare function installDependencies(command: string, projectPath: any, spinner: any): void;
48
- export declare function getDefaultPackageContent(moduleType: string, path: any, projectPath: any): {};
49
28
  export declare const colorQuestions: (message: string) => string;
50
29
  export declare function saveIgnoresToCache(ignore: any, cachePath: any): Promise<void>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Shared I/O for AI-agent-friendly AND human-friendly commands.
3
+ *
4
+ * Contract every command follows:
5
+ * - **Headless first**: runs from flags/stdin; an interactive prompt is only a
6
+ * fallback when stdout is a TTY and required input is missing.
7
+ * - **Machine-readable**: with `--json`, or whenever stdout is NOT a TTY (piped
8
+ * into an agent/file), the command prints exactly one JSON object to stdout:
9
+ * success → `{ "ok": true, ... }`, failure → `{ "ok": false, "error", "message" }`.
10
+ * - **Human-friendly**: on a TTY without `--json`, it renders the rich/coloured
11
+ * output instead.
12
+ * - **Stable exit codes**: `0` success, non-zero on failure.
13
+ *
14
+ * Logs/spinners/banners go to **stderr** (see Logger) so stdout stays a clean
15
+ * data channel for agents.
16
+ */
17
+ /** True when stdout is an interactive terminal. */
18
+ export declare const isTTY: () => boolean;
19
+ /** Add the standard `--json` option to a yargs builder. */
20
+ export declare const withJsonFlag: (yargs: any) => any;
21
+ /** Should this invocation produce JSON? (explicit flag, or non-TTY stdout). */
22
+ export declare const wantsJson: (argv: {
23
+ json?: boolean;
24
+ }) => boolean;
25
+ /**
26
+ * Emit a successful result. In JSON mode prints `{ ok: true, ...data }`;
27
+ * otherwise calls `renderHuman` for the pretty output. Returns exit code 0.
28
+ */
29
+ export declare function emit(argv: {
30
+ json?: boolean;
31
+ }, data: Record<string, unknown>, renderHuman: () => void): void;
32
+ /**
33
+ * Emit a failure and exit with `code` (default 1). In JSON mode prints
34
+ * `{ ok: false, error, message }` on stdout; otherwise logs the message to
35
+ * stderr. Never returns.
36
+ *
37
+ * @param error short stable machine code, e.g. `"InvalidInput"`.
38
+ * @param message human-readable explanation.
39
+ */
40
+ export declare function fail(argv: {
41
+ json?: boolean;
42
+ }, error: string, message: string, code?: number): never;
43
+ /** True when a TTY-interactive fallback is appropriate (TTY and not --json). */
44
+ export declare const canPrompt: (argv: {
45
+ json?: boolean;
46
+ }) => boolean;
47
+ /**
48
+ * Read all of stdin as a UTF-8 string. Returns '' immediately when stdin is a
49
+ * TTY (nothing piped). Lets commands accept input via `echo x | mfn ...`.
50
+ */
51
+ export declare function readStdin(): Promise<string>;
52
+ /**
53
+ * Emit a `{ ok: false, error, message }` failure and exit, WITHOUT a parsed
54
+ * argv. Used by the yargs `.fail()` handler, where errors originate in the
55
+ * parser layer (missing required arg, unknown command/flag, coerce throw)
56
+ * before a command's own `fail()` can run. JSON mode is inferred from
57
+ * `--json` in argv or a non-TTY stdout. Never returns.
58
+ */
59
+ export declare function failEnvelope(error: string, message: string, code?: number): never;
package/llms.txt ADDED
@@ -0,0 +1,58 @@
1
+ # @master4n/master-cli (`mfn`)
2
+
3
+ > A headless-friendly developer CLI that replaces boilerplate agents otherwise
4
+ > regenerate on every machine: epoch/date conversions, JWT decoding, freeing
5
+ > ports, finding files, and printing directory trees. Every command is designed
6
+ > to be called by both humans and AI agents.
7
+
8
+ ## Agent contract (read this first)
9
+
10
+ - **Discover commands:** `mfn capabilities --json` returns the full manifest
11
+ (`{ name, version, bin, conventions, commands:[{name,summary,examples}] }`).
12
+ - **Machine output:** pass `--json`, OR just pipe the command (when stdout is not
13
+ a TTY the CLI auto-emits JSON). Output is exactly one object on stdout:
14
+ success → `{ "ok": true, ... }`, failure → `{ "ok": false, "error", "message" }`.
15
+ - **Exit codes:** `0` success · `1` runtime error · `2` usage error.
16
+ - **Clean channels:** the welcome banner, spinners, and logs go to **stderr**;
17
+ **stdout carries only the JSON result**, so `mfn <cmd> --json | jq` always works.
18
+ - **No prompts in headless mode:** interactive prompts appear only on a TTY when
19
+ required input is missing; with `--json` or when piped, commands never block.
20
+ - **Strict parsing:** unknown commands, unknown flags, and missing required
21
+ arguments are rejected with `{ "ok": false, "error": "UsageError", ... }` and
22
+ exit `2` — a typo never silently "succeeds".
23
+
24
+ ## Commands
25
+
26
+ - `mfn id [-t uuid|uuid7|nano] [-n count] [--size N] --json` — generate ids. UUID v4
27
+ (default), time-ordered UUID v7 (RFC 9562), or a URL-safe nano id. Returns `{ type, count, ids }`.
28
+ - `mfn hash [text] [-a md5|sha1|sha256|sha512] [-f file] [-e hex|base64|base64url] --json`
29
+ — hash a string, a file, or piped stdin. Returns `{ algo, encoding, source, bytes, hash }`.
30
+ - `mfn encode [text] [--as base64|base64url|hex|url] [-d] --json` — encode (or `-d` decode)
31
+ text or stdin. Returns `{ operation, codec, input, output }`.
32
+ - `mfn random [-b bytes] [-e hex|base64|base64url] | [-p [-l length]] --json` — secure random
33
+ bytes, or an unbiased password. Returns `{ kind, ..., value }`.
34
+ - `mfn port [--json] | [-c port] | [-n count]` — find free port(s), or check a specific port.
35
+ Returns `{ port }` / `{ count, ports }` / `{ port, available }`.
36
+ - `mfn epoch <value> [--tz] [--format] --json` — epoch → date (unit auto-detected:
37
+ s/ms/µs/ns). `mfn epoch --from <dateString> [--format] [--tz] --json` — date → epoch.
38
+ Fractional/invalid epochs fail with exit 1.
39
+ - `mfn date [from] [--tz] [--format] [--in-format] [--in-tz] --json` — convert/format
40
+ a date across timezones; omit `from` for now. Returns epoch + UTC + target-zone.
41
+ - `mfn decode -t <jwt> --json` — decode a JWT's header and payload (signature NOT verified).
42
+ If the payload has a numeric `exp`, also returns `expiry: { exp, expired, expiresInSeconds }`.
43
+ - `mfn kill -p <port...> [-y] --json` — kill the process(es) listening on the given
44
+ ports. Headless/`--json` (and `-y`) kill all matches without prompting; in headless
45
+ mode you MUST pass `-p` (no cached-port replay). Returns `{ killed, failed, notFound }`.
46
+ - `mfn sc [pattern] [--ignore...] [--depth] [--limit] --json` — fuzzy-find files/folders
47
+ under the current directory. Returns `{ pattern, root, count, truncated, matches }`.
48
+ - `mfn cts [--type text|svg|png|jpeg] [--ignore...] --json` — print the directory tree as
49
+ text (default) or export it to an image. Ignores node_modules/.git/.nx by default.
50
+ - `mfn update [package] --json` — update the CLI (or a named package) globally via npm.
51
+ - `mfn capabilities --json` — self-describing manifest of all of the above.
52
+
53
+ ## Notes
54
+
55
+ - Time/date features are powered by `@master4n/temporal-transformer` v2 (Luxon-backed,
56
+ integer epochs; Luxon format tokens, e.g. `yyyy-MM-dd HH:mm:ss`).
57
+ - Zero shell interpolation: process/port/package operations use `execFile` (no shell),
58
+ so inputs cannot inject commands.
package/package.json CHANGED
@@ -1,68 +1,65 @@
1
1
  {
2
2
  "name": "@master4n/master-cli",
3
- "version": "2.3.0",
4
- "description": "Command Line Interface For Various Useful Operations",
5
- "main": "./bin/index.js",
6
- "types": "./bin/index.d.ts",
3
+ "version": "3.0.0",
4
+ "description": "AI-agent-friendly command-line toolkit: timestamp/date conversion, JWT decoding, port killing, file finding, and directory trees — headless, --json, with a self-describing manifest.",
7
5
  "type": "module",
8
- "bin": {
9
- "mfn": "./bin/index.js"
10
- },
11
- "scripts": {
12
- "clean": "rimraf dist",
13
- "lint": "eslint src/**/*.ts",
14
- "format": "prettier --write src/**/*.ts",
15
- "build": "npm run clean && rollup --config"
16
- },
17
6
  "repository": {
18
7
  "type": "git",
19
- "url": "git+https://github.com/Master4Novice/common.git"
8
+ "url": "git+https://github.com/Master4Novice/master-cli.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/Master4Novice/master-cli/issues"
20
12
  },
21
13
  "keywords": [
22
14
  "cli",
23
- "helper",
24
- "module"
15
+ "command-line",
16
+ "command-line-tool",
17
+ "developer-tools",
18
+ "devtools",
19
+ "nodejs",
20
+ "typescript",
21
+ "ai-agents",
22
+ "agent-tools",
23
+ "llm-tools",
24
+ "automation",
25
+ "headless",
26
+ "json-output",
27
+ "jwt",
28
+ "jwt-decode",
29
+ "epoch",
30
+ "epoch-converter",
31
+ "unix-timestamp",
32
+ "timezone",
33
+ "kill-port",
34
+ "free-port",
35
+ "directory-tree",
36
+ "fuzzy-finder",
37
+ "mfn",
38
+ "master-cli"
25
39
  ],
26
40
  "author": "dwivna",
27
41
  "license": "MIT",
28
- "homepage": "",
29
- "publishConfig": {
30
- "directory": "dist",
31
- "access": "public"
32
- },
42
+ "homepage": "https://github.com/Master4Novice/master-cli#readme",
33
43
  "contributors": [
34
44
  {
35
45
  "name": "dwivna"
36
46
  }
37
47
  ],
38
48
  "dependencies": {
39
- "@master4n/temporal-transformer": "^1.0.9",
40
- "@types/fs-extra": "^11.0.4",
41
- "@types/yargs": "^17.0.32",
49
+ "@master4n/temporal-transformer": "^2.0.4",
42
50
  "boxen": "^7.1.1",
43
51
  "chalk": "^5.3.0",
44
52
  "figlet": "^1.7.0",
45
53
  "fs-extra": "^11.2.0",
46
54
  "fuzzy": "^0.1.3",
47
55
  "inquirer": "^9.2.23",
48
- "inquirer-autocomplete-prompt": "^3.0.1",
49
- "inquirer-date-prompt": "^3.0.0",
50
- "inquirer-fuzzy-path": "^2.3.0",
51
- "is-root": "^3.0.0",
52
- "moment": "^2.30.1",
53
- "moment-timezone": "^0.5.45",
54
- "open": "^10.1.0",
56
+ "ora": "^8.0.1",
55
57
  "sharp": "^0.33.4",
56
- "update-notifier": "^7.0.0",
57
58
  "yargs": "^17.7.2"
58
59
  },
59
- "devDependencies": {
60
- "@types/figlet": "^1.5.8",
61
- "@types/inquirer": "^9.0.7",
62
- "@types/inquirer-fuzzy-path": "^2.3.9",
63
- "@types/node": "^20.12.12",
64
- "@types/update-notifier": "^6.0.8",
65
- "ora": "^8.0.1"
66
- },
67
- "gitHead": "2ef1d0cbf62f6f2742140d6e47cfb1ade721f180"
68
- }
60
+ "main": "bin/index.js",
61
+ "types": "bin/index.d.ts",
62
+ "bin": {
63
+ "mfn": "bin/index.js"
64
+ }
65
+ }
@@ -1,7 +0,0 @@
1
- declare const createApolloExpress: {
2
- command: string;
3
- describe: string;
4
- builder: (yargs: any) => any;
5
- handler: (argv: any) => Promise<void>;
6
- };
7
- export default createApolloExpress;
@@ -1,7 +0,0 @@
1
- declare const hra: {
2
- command: string;
3
- describe: string;
4
- builder: (yargs: any) => void;
5
- handler: (argv: any) => void;
6
- };
7
- export default hra;
@@ -1,7 +0,0 @@
1
- declare const md: {
2
- command: string;
3
- describe: string;
4
- builder: (yargs: any) => void;
5
- handler: (argv: any) => void;
6
- };
7
- export default md;
@@ -1,7 +0,0 @@
1
- declare const sr: {
2
- command: string;
3
- describe: string;
4
- builder: (yargs: any) => void;
5
- handler: (argv: any) => void;
6
- };
7
- export default sr;
@@ -1,3 +0,0 @@
1
- export declare const comjsIndexContent = "const express = require('express');\nconst { ApolloServer } = require('@apollo/server');\nconst { expressMiddleware } = require('@apollo/server/express4');\nconst { readFileSync } = require('fs');\nconst { join } = require('path');\nconst cors = require('cors');\n \nconst getSchema = () => {\n const schemaPath = join(process.cwd(), `resources/schema.graphql`);\n return readFileSync(schemaPath, 'utf-8');\n};\n \nconst typeDefs = getSchema();\n \nconst resolvers = {\n Query: {\n hello: () => 'Hello world!',\n },\n};\n \nconst app = express();\n\nconst server = new ApolloServer({\n typeDefs,\n resolvers,\n});\n \nserver.start().then(() => {\n app.use('/graphql', cors(), express.json(), expressMiddleware(server));\n});\n \napp.listen(4000, () => {\n console.log('\uD83D\uDE80 Server ready at http://localhost:4000/graphql');\n});\n";
2
- export declare const comjsIndexContentVersion = "const express = require('express');\nconst { ApolloServer } = require('@apollo/server');\nconst { expressMiddleware } = require('@apollo/server/express4');\nconst { versionMiddleware } = require('./versionMiddleware.js');\n\nconst app = express();\n\napp.use(versionMiddleware);\n\napp.use('/graphql', express.json(), async (req, res, next) => {\n const { typeDefs, resolvers } = req.app.locals;\n const server = new ApolloServer({\n typeDefs,\n resolvers,\n });\n await server.start();\n expressMiddleware(server)(req, res, next);\n});\n\napp.listen(4000, () => {\n console.log(\u2018\uD83D\uDE80 Server ready at http://localhost:4000/graphql\u2019);\n});\n";
3
- export declare const comjsMiddleContent = "const { readFileSync } = require('fs');\nconst { join } = require('path');\nconst { resolvers: resolversV1 } = require('./v1/resolvers.js');\nconst { resolvers: resolversV2 } = require('./v2/resolvers.js');\n\nconst getSchema = (version) => {\n const schemaPath = join(process.cwd(), `resources/${version}/schema.graphql`);\n return readFileSync(schemaPath, 'utf8');\n};\n\nconst versionMiddleware = (req, res, next) => {\n const version = req.headers['api-version'];\n\n if (version === 'v1') {\n req.app.locals.typeDefs = getSchema('v1');\n req.app.locals.resolvers = resolversV1;\n } else if (version === 'v2') {\n req.app.locals.typeDefs = getSchema('v2');\n req.app.locals.resolvers = resolversV2;\n } else {\n res.status(400).send('Unsupported or missing API version');\n return;\n }\n next();\n};\n\nmodule.exports = { versionMiddleware };\n";
@@ -1,3 +0,0 @@
1
- export * from './comjs-content';
2
- export * from './type-content';
3
- export * from './md-contents';
@@ -1 +0,0 @@
1
- export declare const apolloContent: (projectName: string, moduleType: string) => string;
@@ -1,3 +0,0 @@
1
- export declare const typeIndexContent = "import express from 'express';\nimport { ApolloServer } from '@apollo/server';\nimport { expressMiddleware } from '@apollo/server/express4';\nimport cors from 'cors';\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\n\nconst getSchema = (): string => {\n const schemaPath = join(process.cwd(), `resources/schema.graphql`);\n return readFileSync(schemaPath, 'utf-8');\n};\n\nconst typeDefs = getSchema();\n\nconst resolvers = {\n Query: {\n hello: () => 'Hello world!',\n },\n};\n\nconst app = express();\n\nconst server = new ApolloServer({\n typeDefs,\n resolvers\n});\n\nawait server.start();\n\napp.use('/graphql', cors<cors.CorsRequest>(), express.json(), expressMiddleware(server));\n\napp.listen(4000, () => {\n console.log('\uD83D\uDE80 Server ready at http://localhost:4000/graphql');\n});\n";
2
- export declare const typeIndexContentVersion = "import express from 'express';\nimport { ApolloServer } from '@apollo/server';\nimport { expressMiddleware } from '@apollo/server/express4';\nimport { versionMiddleware } from './versionMiddleware.js';\n\nconst app = express();\n\napp.use(versionMiddleware);\n\napp.use('/graphql', express.json(), async (req, res, next) => {\n const { typeDefs, resolvers } = req.app.locals;\n const server = new ApolloServer({\n typeDefs,\n resolvers,\n });\n\n await server.start();\n expressMiddleware(server)(req, res, next);\n});\n\napp.listen(4000, () => {\n console.log('\uD83D\uDE80 Server ready at http://localhost:4000/graphql');\n});\n";
3
- export declare const typeMiddleContent = "import { Request, Response, NextFunction } from 'express';\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\nimport { resolvers as resolversV1 } from './v1/resolvers.js';\nimport { resolvers as resolversV2 } from './v2/resolvers.js';\n \nconst getSchema = (version: string): string => {\n const schemaPath = join(process.cwd(), `resources/${version}/schema.graphql`);\n return readFileSync(schemaPath, 'utf8');\n};\n \nexport const versionMiddleware = (req: Request, res: Response, next: NextFunction) => {\n const version = req.headers['api-version'];\n \n if (version === 'v1') {\n req.app.locals.typeDefs = getSchema('v1');\n req.app.locals.resolvers = resolversV1;\n } else if (version === 'v2') {\n req.app.locals.typeDefs = getSchema('v2');\n req.app.locals.resolvers = resolversV2;\n } else {\n res.status(400).send('Unsupported or missing API version');\n return;\n }\n next();\n};\n";
@@ -1,5 +0,0 @@
1
- export declare const schema = "type Query {\n hello: String\n}\n";
2
- export declare const schemaV1 = "type User {\n id: ID!\n firstName: String\n lastName: String\n}\n\ntype Query {\n getUser(id: ID!): User\n allUsers: [User]\n}\n";
3
- export declare const resolversV1: (typescript: any) => "export const resolvers = {\n Query: {\n getUser: (parent: any, args: { id: string }) => {\n // Mock data for v1\n return { id: args.id, firstName: 'John', lastName: 'Doe' };\n },\n allUsers: () => {\n // Mock data for v1\n return [{ id: '1', firstName: 'John', lastName: 'Doe' }];\n },\n },\n };\n " | "exports.resolvers = {\n Query: {\n getUser: (parent, args) => {\n // Mock data for v1\n return { id: args.id, firstName: 'John', lastName: 'Doe' };\n },\n allUsers: () => {\n // Mock data for v1\n return [{ id: '1', firstName: 'John', lastName: 'Doe' }];\n },\n },\n };\n";
4
- export declare const schemaV2 = "type User {\n id: ID!\n firstName: String\n lastName: String\n age: Int\n }\n \n type Query {\n getUser(id: ID!): User\n allUsers: [User]\n usersByAge(age: Int!): [User]\n }\n";
5
- export declare const resolversV2: (typescript: any) => "export const resolvers = {\n Query: {\n getUser: (parent: any, args: { id: string }) => {\n // Mock data for v2\n return { id: args.id, firstName: 'John', lastName: 'Doe', age: 30 };\n },\n allUsers: () => {\n // Mock data for v2\n return [{ id: '1', firstName: 'John', lastName: 'Doe', age: 30 }];\n },\n usersByAge: (parent: any, args: { age: number }) => {\n // Mock data for v2\n return [{ id: '1', firstName: 'John', lastName: 'Doe', age: args.age }];\n },\n },\n };\n " | "exports.resolvers = {\n Query: {\n getUser: (parent, args) => {\n // Mock data for v2\n return { id: args.id, firstName: 'John', lastName: 'Doe', age: 30 };\n },\n allUsers: () => {\n // Mock data for v2\n return [{ id: '1', firstName: 'John', lastName: 'Doe', age: 30 }];\n },\n usersByAge: (parent, args) => {\n // Mock data for v2\n return [{ id: '1', firstName: 'John', lastName: 'Doe', age: args.age }];\n },\n },\n };\n";
@@ -1,6 +0,0 @@
1
- export interface HRAInput {
2
- monthlyBasic: number;
3
- monthlyRent: number;
4
- hraReceived: number;
5
- isMetro: boolean;
6
- }