bob-core 2.0.0-beta.14 → 2.0.0-beta.15

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.
package/dist/cjs/index.js CHANGED
@@ -1,17 +1,17 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("prompts"),a=require("chalk"),H=require("minimist"),T=require("node:fs"),S=require("path"),W=require("string-similarity");function N(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const M=N(T),_=N(W);class R{logger;constructor(e){this.logger=e}log(...e){this.logger.log(...e)}info(...e){this.logger.info(...e)}warn(...e){this.logger.warn(...e)}error(...e){this.logger.error(...e)}debug(...e){this.logger.debug(...e)}async askForConfirmation(e="Do you want to continue?",t){return(await y({type:"confirm",name:"value",message:e,initial:t??!1})).value}async askForInput(e,t,n){return(await y({type:"text",name:"value",message:e,initial:t,...n}))?.value??null}async askForDate(e,t,n){return(await y({type:"date",name:"value",message:e,initial:t,...n}))?.value??null}async askForList(e,t,n){return(await y({type:"list",name:"value",message:e,initial:t,...n}))?.value??null}async askForToggle(e,t,n){return(await y({type:"toggle",name:"value",message:e,initial:t,...n}))?.value??null}async askForSelect(e,t,n){if(t.length===0)throw new Error("No options provided");const i=[];for(const o of t)typeof o=="string"?i.push({title:o,value:o}):i.push(o);return(await y({type:"select",name:"value",message:e,choices:i,...n}))?.value??null}newLoader(e="",t=["⠙","⠘","⠰","⠴","⠤","⠦","⠆","⠃","⠋","⠉"],n=100){let i=e,s=null,o=0;const u=setInterval(function(){s&&(process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(s.length+5)+"\r")),s=null),process.stdout.write(new TextEncoder().encode("\r"+t[o++]+" "+i)),o=o%t.length},n),m=()=>{clearInterval(u),process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(i.length+5)+"\r"))};return{[Symbol.dispose]:m,[Symbol.asyncDispose]:m,updateText:c=>{s=i,i=c},stop:m}}}class f extends Error{}function q(r){if(r==="string"||r==="number")return null;if(r==="boolean")return!1;if(Array.isArray(r)&&r.length===1){if(r[0]==="string")return[];if(r[0]==="number")return[]}throw new Error("Invalid option type: "+r)}function k(r){return typeof r=="string"||Array.isArray(r)?q(r):r.default!==void 0?r.default:q(r.type)}function g(r){return typeof r=="string"||Array.isArray(r)?{alias:[],default:k(r),description:"",required:!1,secret:!1,type:r,variadic:!1}:{alias:r.alias?Array.isArray(r.alias)?r.alias:[r.alias]:[],default:r.default??k(r.type),description:r.description??"",required:r.required??!1,secret:r.secret??!1,type:r.type,variadic:r.variadic??!1}}class b extends f{constructor(e,t={}){super(`Invalid option ${e} in not recognized`),this.option=e,this.optionsSchema=t}pretty(e){const t=Object.entries(this.optionsSchema);if(t.length>0){e.log(`
2
- ${a.yellow("Available options")}:`);for(const[n,i]of t){const s=g(i),o=typeof s.alias=="string"?[s.alias]:s.alias,u=Array.isArray(s.type)?`[${s.type[0]}]`:s.type,m=`--${n}${o.length>0?o.map(l=>`, -${l}`).join(""):""}`,c=" ".repeat(30-m.length);e.log(` ${a.green(m)} ${c} ${s.description||"\b"} ${a.white(`(${u})`)}`)}e.log("")}e.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is not recognized.`)}}class O extends f{constructor(e){super(`Argument "${e}" is required.`),this.argument=e}pretty(e){e.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`)}}class P extends f{constructor(e){super(`Argument "${e}" is required.`),this.option=e}pretty(e){e.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is required.`)}}class B extends f{constructor(e){let t=`Argument "${e.param}" value is invalid.`;e.reason?t+=` Reason: ${e.reason}`:t+=` Value: "${e.value}"`,super(t),this.param=e}pretty(e){e.log(` ${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.param.param)} value is invalid. `),(this.param.value||this.param.reason)&&e.log(""),this.param.value&&e.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&e.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class C extends f{constructor(e){let t=`Option "${e.option}" value is invalid.`;e.reason?t+=` Reason: ${e.reason}`:t+=` Value: "${e.value}"`,super(t),this.param=e}pretty(e){e.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `),(this.param.value||this.param.reason)&&e.log(""),this.param.value&&e.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&e.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class L extends f{constructor(e){super(`Command "${e}" not found.`),this.command=e}pretty(e){e.log(`${a.bgRed(" ERROR ")} Command ${a.yellow(this.command)} not found.`)}}function w(r,e,t,n){if(r==null)return n??null;if(e==="string")return String(r);if(e==="number"){const i=Number(r);if(isNaN(i))throw new C({option:t,reason:`Expected a number, got "${r}"`});return i}if(e==="boolean")return typeof r=="boolean"?r:r==="true"||r==="1"?!0:r==="false"||r==="0"?!1:!!r;if(Array.isArray(e)){const i=e[0],s=Array.isArray(r)?r:[r];if(i==="string")return s.map(o=>String(o));if(i==="number")return s.map(o=>{const u=Number(o);if(isNaN(u))throw new C({option:t,reason:`Expected array of numbers, got "${o}" in array`});return u})}return r}class x{options;parsedOptions=null;arguments;parsedArguments=null;io;shouldPromptForMissingOptions=!0;constructor(e){this.options=e.options,this.arguments=e.arguments,this.io=e.io}init(e){const{_:t,...n}=H(e);return this.validateUnknownOptions(n),this.parsedOptions=this.handleOptions(n),this.parsedArguments=this.handleArguments(t),{options:this.parsedOptions,arguments:this.parsedArguments}}async validate(){for(const e in this.options)if(g(this.options[e]).required&&(this.parsedOptions?.[e]===void 0||this.parsedOptions?.[e]===null))throw new P(e);for(const e in this.arguments){const t=g(this.arguments[e]),n=this.parsedArguments?.[e];if(t.required&&n==null){if(this.shouldPromptForMissingOptions){const i=await this.promptForArgument(e,t);if(i&&this.parsedArguments){this.parsedArguments[e]=w(i,t.type,e);continue}}throw new O(e)}if(t.required&&t.variadic&&Array.isArray(n)&&n.length===0){if(this.shouldPromptForMissingOptions){const i=await this.promptForArgument(e,t);if(i&&this.parsedArguments){this.parsedArguments[e]=w(i,t.type,e);continue}}throw new O(e)}}}option(e,t){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedOptions[e])&&t!==void 0?t:this.parsedOptions[e]}setOption(e,t){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");if(!(e in this.options))throw new b(e,this.options);this.parsedOptions[e]=t}argument(e,t){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedArguments[e])&&t!==void 0?t:this.parsedArguments[e]}setArgument(e,t){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");if(!(e in this.arguments))throw new b(e,this.arguments);this.parsedArguments[e]=t}isEmptyValue(e){return e==null||Array.isArray(e)&&e.length===0}validateUnknownOptions(e){const t=new Set;for(const n in this.options){t.add(n);const i=g(this.options[n]);for(const s of i.alias)t.add(s)}for(const n in e)if(!t.has(n))throw new b(n,this.options)}handleOptions(e){const t={};for(const n in this.options){const i=g(this.options[n]);t[n]=this.resolveOptionValue(n,i,e)}return t}handleArguments(e){const t={},n=[...e];for(const i in this.arguments){const s=g(this.arguments[i]);if(s.variadic){t[i]=this.handleVariadicArgument(i,s,n);continue}t[i]=this.resolveArgumentValue(i,s,n.shift())}return t}handleVariadicArgument(e,t,n){return n.length?w(n,t.type,e,t.default):t.default}resolveArgumentValue(e,t,n){return n===void 0?t.default:w(n,t.type,e,t.default)}resolveOptionValue(e,t,n){let i;const s=[e,...t.alias];for(const o of s)if(o in n){i=n[o];break}if(i===void 0){if(t.required)throw new C({option:e,reason:"Required option is missing"});return t.default}return w(i,t.type,e,t.default)}optionDefinitions(){const e={};for(const t in this.options)e[t]=g(this.options[t]);return e}argumentDefinitions(){const e={};for(const t in this.arguments)e[t]=g(this.arguments[t]);return e}availableOptions(){return Object.keys(this.options)}availableArguments(){return Object.keys(this.arguments)}disablePrompting(){return this.shouldPromptForMissingOptions=!1,this}async promptForArgument(e,t){if(!Array.isArray(t.type)&&!["string","number","secret"].includes(t.type))return null;let n=`${a.yellow.bold(e)} is required`;return t.description&&(n+=`: ${a.gray(`(${t.description})`)}`),n+=` ${a.green(`(${t.type}${t.variadic==!0?"[]":""})`)}
3
- `,Array.isArray(t.type)?(n+=`Please provide one or more values, separated by commas:
4
- `,await this.io.askForList(n,void 0,{separator:",",validate:i=>{if(!i.length)return"Please enter at least one value";if(t.type[0]==="number"){for(const s of i.split(","))if(isNaN(Number(s)))return"Please enter only valid numbers"}return!0}})):await this.io.askForInput(n,void 0,{type:t.type==="number"?"number":t.secret?"password":"text",validate:i=>{if(i==null||typeof i=="string"&&!i.length)return"This value is required";if(t.type==="number"){const s=Number(i);if(isNaN(s))return"Please enter a valid number"}else if(t.type==="string"&&(typeof i!="string"||!i.length))return"Please enter a valid text";return!0}})}}function A(r){return new Array(r+5).join(" ")}class V{type="boolean";option="help";alias=["h"];default=!1;description=`Display help for the given command. When no command is given display help for the ${a.green("list")} command`;async handler(){const e=this.parser.argumentDefinitions(),t=this.parser.optionDefinitions(),n=Object.entries(e),i=Object.entries(t),s=i.map(([l,d])=>{const p=Array.isArray(d.alias)?d.alias:d.alias?[d.alias]:[];return{name:l,...d,optionWithAlias:`--${l}${p.map(h=>`, -${h}`).join("")}`}}),o=n.filter(([,l])=>l.required);this.io.log(a.yellow("Description:")),this.io.log(` ${this.description}
5
- `),this.io.log(a.yellow("Usage:")),this.io.log(` ${this.command} ${o.length>0?o.map(([l])=>`<${l}>`).join(" "):"\b"} [options]`);const u=Math.max(...s.map(l=>l.optionWithAlias.length),0),m=Math.max(...n.map(([l])=>l.length),0),c=m>u?m:u;if(n.length>0){this.io.log(`
6
- ${a.yellow("Arguments")}:`);for(const[l,d]of n){const p=A(c-l.length);let h=` ${a.green(l)} ${p} ${d.description??"\b"}`;if(d.default!==void 0&&!d.required){const j=(Array.isArray(d.type)?`[${d.type[0]}]`:d.type)==="array"||Array.isArray(d.type)?JSON.stringify(d.default):d.default;h+=` ${a.yellow(`[default: ${j}]`)}`}d.variadic&&(h+=` ${a.white("(variadic)")}`),this.io.log(h)}}if(i.length>0){this.io.log(`
7
- ${a.yellow("Options")}:`);for(const l of s){const d=A(c-l.optionWithAlias.length);let p=`${a.green(l.optionWithAlias)} ${d} ${l.description??"\b"}`;if(l.type){const h=Array.isArray(l.type)?`[${l.type[0]}]`:l.type;p+=` ${a.white(`(${h})`)}`}if(l.default!==void 0&&!l.required){const F=(Array.isArray(l.type)?`[${l.type[0]}]`:l.type)==="array"||Array.isArray(l.type)?JSON.stringify(l.default):l.default;p+=` ${a.yellow(`[default: ${F}]`)}`}this.io.log(p)}}if(this.commandsExamples.length>0){this.io.log(`
8
- ${a.yellow("Examples")}:`);let l=process.argv[0].split("/").pop();l==="node"&&(l+=" "+process.argv[1].split("/").pop());for(const[d,p]of this.commandsExamples.entries())d>0&&this.io.log(""),this.io.log(` ${p.description}
9
- `),this.io.log(` ${a.green(`${l} ${p.command}`)}`)}return-1}}class v{_command;description;group;commandsExamples=[];get command(){return this._command}ctx;io;logger;parser;disablePromptingFlag=!1;_preHandler;_handler;tmp;defaultOptions(){return[new V]}newCommandParser(e){return new x({io:e.io,options:e.options,arguments:e.arguments})}newCommandIO(e){return new R(e.logger)}constructor(e,t){this._command=e,this.description=t?.description??"",this.group=t?.group,this.tmp={options:t?.options??{},arguments:t?.arguments??{}};const n=this.defaultOptions();if(n.length>0)for(const i of n)this.tmp.options[i.option]=i}disablePrompting(){return this.disablePromptingFlag=!0,this}preHandler(e){return this._preHandler=e,this}handler(e){return this._handler=e,this}options(e){return this.tmp={options:{...this.tmp?.options??{},...e},arguments:this.tmp?.arguments??{}},this}arguments(e){return this.tmp={options:this.tmp?.options??{},arguments:{...this.tmp?.arguments??{},...e}},this}async run(e){if(!this._handler&&!this.handle)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);let t;if(this.ctx=e.ctx,this.logger=e.logger,this.io=this.newCommandIO({logger:e.logger}),e&&"args"in e){const s=this.tmp?.options??{};for(const o of this.defaultOptions())o.option in s||(s[o.option]=o);this.parser=this.newCommandParser({io:this.io,options:s,arguments:this.tmp?.arguments??{}}),t=this.parser.init(e.args);for(const o of this.defaultOptions())if(t.options[o.option]===!0){const u=await o.handler.call(this);if(u&&u!==0)return u}this.disablePromptingFlag&&this.parser.disablePrompting(),await this.parser.validate()}else t={options:e.options,arguments:e.arguments};const n=this.preHandle?await this.preHandle():null;if(n&&n!==0)return n;if(!this._handler&&this.handle)this._handler=this.handle.bind(this);else if(!this._handler)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);return await this._handler(e.ctx,t)??0}}class $ extends x{command;constructor(e){const t=$.parseSignature(e.signature,e.helperDefinitions,e.defaultOptions);super({io:e.io,options:t.options,arguments:t.arguments}),this.command=t.command}static parseSignature(e,t,n){const[i,...s]=e.split(/\{(.*?)\}/g).map(m=>m.trim()).filter(Boolean),o={},u={};for(const m of s){const{name:c,isOption:l,definition:d}=$.parseParamSignature(m,t);l?o[c]=d:u[c]=d}for(const m of n)o[m.option]={type:m.type,required:m.required,alias:m.alias,variadic:m.variadic??!1,description:m.description,default:m.default??null};return{command:i,options:o,arguments:u}}static parseParamSignature(e,t){let n=e,i=!1;const s={required:!0,type:"string",description:void 0,default:null,variadic:!1};if(n.includes(":")){const[o,u]=n.split(":");n=o.trim(),s.description=u.trim()}if(n.includes("=")){const[o,u]=n.split("=");n=o.trim(),s.default=u.trim(),s.required=!1,typeof s.default=="string"&&!s.default.length?s.default=null:s.default==="true"?(s.default=!0,s.type="boolean"):s.default==="false"&&(s.default=!1,s.type="boolean")}else n.startsWith("--")&&(s.required=!1,s.default=!1,s.type="boolean");if(n.includes("|")){const[o,...u]=n.split("|");n=o.trim(),s.alias=u.map(m=>m.trim())}return n.startsWith("--")&&(i=!0,n=n.slice(2)),s.default==="*"&&(s.default=[],s.type=["string"]),n.endsWith("?")&&(s.required=!1,n=n.slice(0,-1)),n.endsWith("*")&&(s.type=["string"],s.variadic=!0,s.default=[],n=n.slice(0,-1)),s.description=s.description??t[n]??t[`--${n}`],{name:n,isOption:i,definition:s}}}class G extends v{helperDefinitions={};get command(){return this.parser?this.parser.command:this.signature.split(" ")[0]}newCommandParser(e){return new $({io:e.io,signature:this.signature,helperDefinitions:this.helperDefinitions,defaultOptions:this.defaultOptions()})}constructor(){super("")}option(e,t=null){return this.parser.option(e,t)}argument(e,t=null){return this.parser.argument(e,t)}async askForConfirmation(...e){return this.io.askForConfirmation(...e)}async askForInput(...e){return this.io.askForInput(...e)}async askForSelect(...e){return this.io.askForSelect(...e)}newLoader(...e){return this.io.newLoader(...e)}}class E{level;constructor(e={}){this.level=e.level??"info"}shouldLog(e){const t=["debug","info","warn","error"],n=t.indexOf(this.level);return t.indexOf(e)>=n}setLevel(e){this.level=e}getLevel(){return this.level}log(...e){console.log(...e)}info(...e){this.shouldLog("info")&&console.log(...e)}warn(...e){this.shouldLog("warn")&&console.warn(...e)}error(...e){this.shouldLog("error")&&console.error(...e)}debug(...e){this.shouldLog("debug")&&console.log(...e)}}class I{commands={};io;logger;get CommandIOClass(){return R}constructor(e){this.logger=e??new E,this.io=new this.CommandIOClass(this.logger)}getAvailableCommands(){return Object.keys(this.commands)}getCommands(){return Object.values(this.commands)}importFile=async e=>(await import(e)).default;commandResolver=async e=>{let t=await this.importFile(e);if(!t)throw new Error(`The command at path ${e} does not have a default export.`);return t&&typeof t=="object"&&"default"in t&&(t=t.default),typeof t=="function"?new t:t instanceof v?t:null};withCommandResolver(e){return this.commandResolver=e,this}withFileImporter(e){return this.importFile=e,this}registerCommand(e,t=!1){const n=e.command;if(!n)throw new Error("Command signature is invalid, it must have a command name.");if(!t&&this.commands[n])throw new Error(`Command ${n} already registered.`);this.commands[n]=e}async loadCommandsPath(e){for await(const t of this.listCommandsFiles(e))try{const n=await this.commandResolver(t);n instanceof v&&this.registerCommand(n)}catch(n){throw new Error(`Command ${t} failed to load. ${n}`,{cause:n})}}async runCommand(e,t,...n){const i=typeof t=="string"?this.commands[t]:t,s=typeof t=="string"?t:i.command;if(!i){const o=await this.suggestCommand(s);return o?await this.runCommand(e,o,...n):1}return await i.run({ctx:e,logger:this.logger,args:n})??0}async suggestCommand(e){const t=this.getAvailableCommands(),{bestMatch:n,bestMatchIndex:i,ratings:s}=_.findBestMatch(e,t),o=s.filter(u=>u.rating>.3).map(u=>u.target);if(n.rating>0&&o.length<=1||n.rating>.7&&o.length>1){const u=t[i];return await this.askRunSimilarCommand(e,u)?u:null}if(o.length){this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(e)} not found.
10
- `);const u=await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"),o);if(u)return u}throw new L(e)}async askRunSimilarCommand(e,t){return this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(e)} not found.
11
- `),this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(t)} instead?`)} `)}async*listCommandsFiles(e){const t=M.readdirSync(e,{withFileTypes:!0});for(const n of t){const i=S.resolve(e,n.name);if(n.isDirectory())yield*this.listCommandsFiles(S.resolve(e,n.name));else{if(!i.endsWith(".ts")&&!i.endsWith(".js")&&!i.endsWith(".mjs")&&!i.endsWith(".cjs"))continue;yield i}}}}class D{logger;constructor(e){this.logger=e}handle(e){if(e instanceof f)return e.pretty(this.logger),-1;throw e}}class U extends v{constructor(e){super("help",{description:a.bold("Show help information about the CLI and its commands")}),this.opts=e}async handle(){const e=this.opts.commandRegistry.getCommands(),t=this.opts.cliName??"Bob CLI",n=this.opts.cliVersion??"0.0.0",i=(await Promise.resolve().then(()=>require("./package-BfDTyN7_.cjs")))?.default?.version??"0.0.0";this.io.log(`${t} ${a.green(n)} (core: ${a.yellow(i)})
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("prompts"),a=require("chalk"),H=require("minimist"),M=require("node:fs"),F=require("path");function T(s){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const e in s)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(s,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>s[e]})}}return t.default=s,Object.freeze(t)}const W=T(M);class R{logger;constructor(t){this.logger=t.logger}log(...t){this.logger.log(...t)}info(...t){this.logger.info(...t)}warn(...t){this.logger.warn(...t)}error(...t){this.logger.error(...t)}debug(...t){this.logger.debug(...t)}async askForConfirmation(t="Do you want to continue?",e){return(await y({type:"confirm",name:"value",message:t,initial:e??!1})).value}async askForInput(t,e,i){return(await y({type:"text",name:"value",message:t,initial:e,...i}))?.value??null}async askForDate(t,e,i){return(await y({type:"date",name:"value",message:t,initial:e,...i}))?.value??null}async askForList(t,e,i){return(await y({type:"list",name:"value",message:t,initial:e,...i}))?.value??null}async askForToggle(t,e,i){return(await y({type:"toggle",name:"value",message:t,initial:e,...i}))?.value??null}async askForSelect(t,e,i){if(e.length===0)throw new Error("No options provided");const n=[];for(const o of e)typeof o=="string"?n.push({title:o,value:o}):n.push(o);return(await y({type:"select",name:"value",message:t,choices:n,...i}))?.value??null}newLoader(t="",e=["⠙","⠘","⠰","⠴","⠤","⠦","⠆","⠃","⠋","⠉"],i=100){let n=t,r=null,o=0;const m=setInterval(function(){r&&(process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(r.length+5)+"\r")),r=null),process.stdout.write(new TextEncoder().encode("\r"+e[o++]+" "+n)),o=o%e.length},i),u=()=>{clearInterval(m),process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(n.length+5)+"\r"))};return{[Symbol.dispose]:u,[Symbol.asyncDispose]:u,updateText:c=>{r=n,n=c},stop:u}}}class f extends Error{}function k(s){if(s==="string"||s==="number")return null;if(s==="boolean")return!1;if(Array.isArray(s)&&s.length===1){if(s[0]==="string")return[];if(s[0]==="number")return[]}throw new Error("Invalid option type: "+s)}function q(s){return typeof s=="string"||Array.isArray(s)?k(s):s.default!==void 0?s.default:k(s.type)}function g(s){return typeof s=="string"||Array.isArray(s)?{alias:[],default:q(s),description:"",required:!1,secret:!1,type:s,variadic:!1}:{alias:s.alias?Array.isArray(s.alias)?s.alias:[s.alias]:[],default:s.default??q(s.type),description:s.description??"",required:s.required??!1,secret:s.secret??!1,type:s.type,variadic:s.variadic??!1}}class $ extends f{constructor(t,e={}){super(`Invalid option ${t} in not recognized`),this.option=t,this.optionsSchema=e}pretty(t){const e=Object.entries(this.optionsSchema);if(e.length>0){t.log(`
2
+ ${a.yellow("Available options")}:`);for(const[i,n]of e){const r=g(n),o=typeof r.alias=="string"?[r.alias]:r.alias,m=Array.isArray(r.type)?`[${r.type[0]}]`:r.type,u=`--${i}${o.length>0?o.map(l=>`, -${l}`).join(""):""}`,c=" ".repeat(30-u.length);t.log(` ${a.green(u)} ${c} ${r.description||"\b"} ${a.white(`(${m})`)}`)}t.log("")}t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is not recognized.`)}}class O extends f{constructor(t){super(`Argument "${t}" is required.`),this.argument=t}pretty(t){t.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`)}}class N extends f{constructor(t){super(`Argument "${t}" is required.`),this.option=t}pretty(t){t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is required.`)}}class B extends f{constructor(t){let e=`Argument "${t.param}" value is invalid.`;t.reason?e+=` Reason: ${t.reason}`:e+=` Value: "${t.value}"`,super(e),this.param=t}pretty(t){t.log(` ${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.param.param)} value is invalid. `),(this.param.value||this.param.reason)&&t.log(""),this.param.value&&t.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&t.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class C extends f{constructor(t){let e=`Option "${t.option}" value is invalid.`;t.reason?e+=` Reason: ${t.reason}`:e+=` Value: "${t.value}"`,super(e),this.param=t}pretty(t){t.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `),(this.param.value||this.param.reason)&&t.log(""),this.param.value&&t.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&t.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class P extends f{constructor(t){super(`Command "${t}" not found.`),this.command=t}pretty(t){t.log(`${a.bgRed(" ERROR ")} Command ${a.yellow(this.command)} not found.`)}}function w(s,t,e,i){if(s==null)return i??null;if(t==="string")return String(s);if(t==="number"){const n=Number(s);if(isNaN(n))throw new C({option:e,reason:`Expected a number, got "${s}"`});return n}if(t==="boolean")return typeof s=="boolean"?s:s==="true"||s==="1"?!0:s==="false"||s==="0"?!1:!!s;if(Array.isArray(t)){const n=t[0],r=Array.isArray(s)?s:[s];if(n==="string")return r.map(o=>String(o));if(n==="number")return r.map(o=>{const m=Number(o);if(isNaN(m))throw new C({option:e,reason:`Expected array of numbers, got "${o}" in array`});return m})}return s}class x{options;parsedOptions=null;arguments;parsedArguments=null;io;shouldPromptForMissingOptions=!0;constructor(t){this.options=t.options,this.arguments=t.arguments,this.io=t.io}init(t){const{_:e,...i}=H(t);return this.validateUnknownOptions(i),this.parsedOptions=this.handleOptions(i),this.parsedArguments=this.handleArguments(e),{options:this.parsedOptions,arguments:this.parsedArguments}}async validate(){for(const t in this.options)if(g(this.options[t]).required&&(this.parsedOptions?.[t]===void 0||this.parsedOptions?.[t]===null))throw new N(t);for(const t in this.arguments){const e=g(this.arguments[t]),i=this.parsedArguments?.[t];if(e.required&&i==null){if(this.shouldPromptForMissingOptions){const n=await this.promptForArgument(t,e);if(n&&this.parsedArguments){this.parsedArguments[t]=w(n,e.type,t);continue}}throw new O(t)}if(e.required&&e.variadic&&Array.isArray(i)&&i.length===0){if(this.shouldPromptForMissingOptions){const n=await this.promptForArgument(t,e);if(n&&this.parsedArguments){this.parsedArguments[t]=w(n,e.type,t);continue}}throw new O(t)}}}option(t,e){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedOptions[t])&&e!==void 0?e:this.parsedOptions[t]}setOption(t,e){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");if(!(t in this.options))throw new $(t,this.options);this.parsedOptions[t]=e}argument(t,e){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedArguments[t])&&e!==void 0?e:this.parsedArguments[t]}setArgument(t,e){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");if(!(t in this.arguments))throw new $(t,this.arguments);this.parsedArguments[t]=e}isEmptyValue(t){return t==null||Array.isArray(t)&&t.length===0}validateUnknownOptions(t){const e=new Set;for(const i in this.options){e.add(i);const n=g(this.options[i]);for(const r of n.alias)e.add(r)}for(const i in t)if(!e.has(i))throw new $(i,this.options)}handleOptions(t){const e={};for(const i in this.options){const n=g(this.options[i]);e[i]=this.resolveOptionValue(i,n,t)}return e}handleArguments(t){const e={},i=[...t];for(const n in this.arguments){const r=g(this.arguments[n]);if(r.variadic){e[n]=this.handleVariadicArgument(n,r,i);continue}e[n]=this.resolveArgumentValue(n,r,i.shift())}return e}handleVariadicArgument(t,e,i){return i.length?w(i,e.type,t,e.default):e.default}resolveArgumentValue(t,e,i){return i===void 0?e.default:w(i,e.type,t,e.default)}resolveOptionValue(t,e,i){let n;const r=[t,...e.alias];for(const o of r)if(o in i){n=i[o];break}if(n===void 0){if(e.required)throw new C({option:t,reason:"Required option is missing"});return e.default}return w(n,e.type,t,e.default)}optionDefinitions(){const t={};for(const e in this.options)t[e]=g(this.options[e]);return t}argumentDefinitions(){const t={};for(const e in this.arguments)t[e]=g(this.arguments[e]);return t}availableOptions(){return Object.keys(this.options)}availableArguments(){return Object.keys(this.arguments)}disablePrompting(){return this.shouldPromptForMissingOptions=!1,this}async promptForArgument(t,e){if(!Array.isArray(e.type)&&!["string","number","secret"].includes(e.type))return null;let i=`${a.yellow.bold(t)} is required`;return e.description&&(i+=`: ${a.gray(`(${e.description})`)}`),i+=` ${a.green(`(${e.type}${e.variadic==!0?"[]":""})`)}
3
+ `,Array.isArray(e.type)?(i+=`Please provide one or more values, separated by commas:
4
+ `,await this.io.askForList(i,void 0,{separator:",",validate:n=>{if(!n.length)return"Please enter at least one value";if(e.type[0]==="number"){for(const r of n.split(","))if(isNaN(Number(r)))return"Please enter only valid numbers"}return!0}})):await this.io.askForInput(i,void 0,{type:e.type==="number"?"number":e.secret?"password":"text",validate:n=>{if(n==null||typeof n=="string"&&!n.length)return"This value is required";if(e.type==="number"){const r=Number(n);if(isNaN(r))return"Please enter a valid number"}else if(e.type==="string"&&(typeof n!="string"||!n.length))return"Please enter a valid text";return!0}})}}function A(s){return new Array(s+5).join(" ")}class L{type="boolean";option="help";alias=["h"];default=!1;description=`Display help for the given command. When no command is given display help for the ${a.green("list")} command`;async handler(){const t=this.parser.argumentDefinitions(),e=this.parser.optionDefinitions(),i=Object.entries(t),n=Object.entries(e),r=n.map(([l,d])=>{const h=Array.isArray(d.alias)?d.alias:d.alias?[d.alias]:[];return{name:l,...d,optionWithAlias:`--${l}${h.map(p=>`, -${p}`).join("")}`}}),o=i.filter(([,l])=>l.required);this.io.log(a.yellow("Description:")),this.io.log(` ${this.description}
5
+ `),this.io.log(a.yellow("Usage:")),this.io.log(` ${this.command} ${o.length>0?o.map(([l])=>`<${l}>`).join(" "):"\b"} [options]`);const m=Math.max(...r.map(l=>l.optionWithAlias.length),0),u=Math.max(...i.map(([l])=>l.length),0),c=u>m?u:m;if(i.length>0){this.io.log(`
6
+ ${a.yellow("Arguments")}:`);for(const[l,d]of i){const h=A(c-l.length);let p=` ${a.green(l)} ${h} ${d.description??"\b"}`;if(d.default!==void 0&&!d.required){const j=(Array.isArray(d.type)?`[${d.type[0]}]`:d.type)==="array"||Array.isArray(d.type)?JSON.stringify(d.default):d.default;p+=` ${a.yellow(`[default: ${j}]`)}`}d.variadic&&(p+=` ${a.white("(variadic)")}`),this.io.log(p)}}if(n.length>0){this.io.log(`
7
+ ${a.yellow("Options")}:`);for(const l of r){const d=A(c-l.optionWithAlias.length);let h=`${a.green(l.optionWithAlias)} ${d} ${l.description??"\b"}`;if(l.type){const p=Array.isArray(l.type)?`[${l.type[0]}]`:l.type;h+=` ${a.white(`(${p})`)}`}if(l.default!==void 0&&!l.required){const E=(Array.isArray(l.type)?`[${l.type[0]}]`:l.type)==="array"||Array.isArray(l.type)?JSON.stringify(l.default):l.default;h+=` ${a.yellow(`[default: ${E}]`)}`}this.io.log(h)}}if(this.commandsExamples.length>0){this.io.log(`
8
+ ${a.yellow("Examples")}:`);let l=process.argv[0].split("/").pop();l==="node"&&(l+=" "+process.argv[1].split("/").pop());for(const[d,h]of this.commandsExamples.entries())d>0&&this.io.log(""),this.io.log(` ${h.description}
9
+ `),this.io.log(` ${a.green(`${l} ${h.command}`)}`)}return-1}}class v{_command;description;group;commandsExamples=[];get command(){return this._command}ctx;io;logger;parser;disablePromptingFlag=!1;_preHandler;_handler;tmp;defaultOptions(){return[new L]}newCommandParser(t){return new x({io:t.io,options:t.options,arguments:t.arguments})}newCommandIO(t){return new R(t)}constructor(t,e){this._command=t,this.description=e?.description??"",this.group=e?.group,this.tmp={options:e?.options??{},arguments:e?.arguments??{}};const i=this.defaultOptions();if(i.length>0)for(const n of i)this.tmp.options[n.option]=n}disablePrompting(){return this.disablePromptingFlag=!0,this}preHandler(t){return this._preHandler=t,this}handler(t){return this._handler=t,this}options(t){return this.tmp={options:{...this.tmp?.options??{},...t},arguments:this.tmp?.arguments??{}},this}arguments(t){return this.tmp={options:this.tmp?.options??{},arguments:{...this.tmp?.arguments??{},...t}},this}async run(t){if(!this._handler&&!this.handle)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);let e;if(this.ctx=t.ctx,this.logger=t.logger,this.io=this.newCommandIO({logger:t.logger}),t&&"args"in t){const r=this.tmp?.options??{};for(const o of this.defaultOptions())o.option in r||(r[o.option]=o);this.parser=this.newCommandParser({io:this.io,options:r,arguments:this.tmp?.arguments??{}}),e=this.parser.init(t.args);for(const o of this.defaultOptions())if(e.options[o.option]===!0){const m=await o.handler.call(this);if(m&&m!==0)return m}this.disablePromptingFlag&&this.parser.disablePrompting(),await this.parser.validate()}else e={options:t.options,arguments:t.arguments};const i=this.preHandle?await this.preHandle():null;if(i&&i!==0)return i;if(!this._handler&&this.handle)this._handler=this.handle.bind(this);else if(!this._handler)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);return await this._handler(t.ctx,e)??0}}class b extends x{command;constructor(t){const e=b.parseSignature(t.signature,t.helperDefinitions,t.defaultOptions);super({io:t.io,options:e.options,arguments:e.arguments}),this.command=e.command}static parseSignature(t,e,i){const[n,...r]=t.split(/\{(.*?)\}/g).map(u=>u.trim()).filter(Boolean),o={},m={};for(const u of r){const{name:c,isOption:l,definition:d}=b.parseParamSignature(u,e);l?o[c]=d:m[c]=d}for(const u of i)o[u.option]={type:u.type,required:u.required,alias:u.alias,variadic:u.variadic??!1,description:u.description,default:u.default??null};return{command:n,options:o,arguments:m}}static parseParamSignature(t,e){let i=t,n=!1;const r={required:!0,type:"string",description:void 0,default:null,variadic:!1};if(i.includes(":")){const[o,m]=i.split(":");i=o.trim(),r.description=m.trim()}if(i.includes("=")){const[o,m]=i.split("=");i=o.trim(),r.default=m.trim(),r.required=!1,typeof r.default=="string"&&!r.default.length?r.default=null:r.default==="true"?(r.default=!0,r.type="boolean"):r.default==="false"&&(r.default=!1,r.type="boolean")}else i.startsWith("--")&&(r.required=!1,r.default=!1,r.type="boolean");if(i.includes("|")){const[o,...m]=i.split("|");i=o.trim(),r.alias=m.map(u=>u.trim())}return i.startsWith("--")&&(n=!0,i=i.slice(2)),r.default==="*"&&(r.default=[],r.type=["string"]),i.endsWith("?")&&(r.required=!1,i=i.slice(0,-1)),i.endsWith("*")&&(r.type=["string"],r.variadic=!0,r.default=[],i=i.slice(0,-1)),r.description=r.description??e[i]??e[`--${i}`],{name:i,isOption:n,definition:r}}}class _ extends v{helperDefinitions={};get command(){return this.parser?this.parser.command:this.signature.split(" ")[0]}newCommandParser(t){return new b({io:t.io,signature:this.signature,helperDefinitions:this.helperDefinitions,defaultOptions:this.defaultOptions()})}constructor(){super("")}option(t,e=null){return this.parser.option(t,e)}argument(t,e=null){return this.parser.argument(t,e)}async askForConfirmation(...t){return this.io.askForConfirmation(...t)}async askForInput(...t){return this.io.askForInput(...t)}async askForSelect(...t){return this.io.askForSelect(...t)}newLoader(...t){return this.io.newLoader(...t)}}class S{level;constructor(t={}){this.level=t.level??"info"}shouldLog(t){const e=["debug","info","warn","error"],i=e.indexOf(this.level);return e.indexOf(t)>=i}setLevel(t){this.level=t}getLevel(){return this.level}log(...t){console.log(...t)}info(...t){this.shouldLog("info")&&console.log(...t)}warn(...t){this.shouldLog("warn")&&console.warn(...t)}error(...t){this.shouldLog("error")&&console.error(...t)}debug(...t){this.shouldLog("debug")&&console.log(...t)}}class I{getBigrams(t){const e=[],i=t.toLowerCase();for(let n=0;n<i.length-1;n++)e.push(i.slice(n,n+2));return e}calculateSimilarity(t,e){if(t===e)return 1;if(t.length<2||e.length<2)return 0;const i=this.getBigrams(t),n=this.getBigrams(e),r=new Set(n);let o=0;for(const m of i)r.has(m)&&(o++,r.delete(m));return 2*o/(i.length+n.length)}findBestMatch(t,e){const i=e.map(o=>({target:o,rating:this.calculateSimilarity(t,o)}));let n=0,r=i[0]?.rating??0;for(let o=1;o<i.length;o++)i[o].rating>r&&(r=i[o].rating,n=o);return{ratings:i,bestMatch:i[n],bestMatchIndex:n}}}class V{commands={};io;logger;stringSimilarity;newCommandIO(t){return new R(t)}constructor(t){this.logger=t?.logger??new S,this.io=this.newCommandIO({logger:this.logger}),this.stringSimilarity=t?.stringSimilarity??new I}getAvailableCommands(){return Object.keys(this.commands)}getCommands(){return Object.values(this.commands)}importFile=async t=>(await import(t)).default;commandResolver=async t=>{let e=await this.importFile(t);if(!e)throw new Error(`The command at path ${t} does not have a default export.`);return e&&typeof e=="object"&&"default"in e&&(e=e.default),typeof e=="function"?new e:e instanceof v?e:null};withCommandResolver(t){return this.commandResolver=t,this}withFileImporter(t){return this.importFile=t,this}registerCommand(t,e=!1){const i=t.command;if(!i)throw new Error("Command signature is invalid, it must have a command name.");if(!e&&this.commands[i])throw new Error(`Command ${i} already registered.`);this.commands[i]=t}async loadCommandsPath(t){for await(const e of this.listCommandsFiles(t))try{const i=await this.commandResolver(e);i instanceof v&&this.registerCommand(i)}catch(i){throw new Error(`Command ${e} failed to load. ${i}`,{cause:i})}}async runCommand(t,e,...i){const n=typeof e=="string"?this.commands[e]:e,r=typeof e=="string"?e:n.command;if(!n){const o=await this.suggestCommand(r);return o?await this.runCommand(t,o,...i):1}return await n.run({ctx:t,logger:this.logger,args:i})??0}async suggestCommand(t){const e=this.getAvailableCommands(),{bestMatch:i,bestMatchIndex:n,ratings:r}=this.stringSimilarity.findBestMatch(t,e),o=r.filter(m=>m.rating>.3).map(m=>m.target);if(i.rating>0&&o.length<=1||i.rating>.7&&o.length>1){const m=e[n];return await this.askRunSimilarCommand(t,m)?m:null}if(o.length){this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
10
+ `);const m=await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"),o);if(m)return m}throw new P(t)}async askRunSimilarCommand(t,e){return this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
11
+ `),this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `)}async*listCommandsFiles(t){const e=W.readdirSync(t,{withFileTypes:!0});for(const i of e){const n=F.resolve(t,i.name);if(i.isDirectory())yield*this.listCommandsFiles(F.resolve(t,i.name));else{if(!n.endsWith(".ts")&&!n.endsWith(".js")&&!n.endsWith(".mjs")&&!n.endsWith(".cjs"))continue;yield n}}}}class D{logger;constructor(t){this.logger=t}handle(t){if(t instanceof f)return t.pretty(this.logger),-1;throw t}}class G extends v{constructor(t){super("help",{description:a.bold("Show help information about the CLI and its commands")}),this.opts=t}async handle(){const t=this.opts.commandRegistry.getCommands(),e=this.opts.cliName??"Bob CLI",i=this.opts.cliVersion??"0.0.0",n=(await Promise.resolve().then(()=>require("./package-o2EGFSAP.cjs")))?.default?.version??"0.0.0";this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(n)})
12
12
 
13
13
  ${a.yellow("Usage")}:
14
14
  command [options] [arguments]
15
15
 
16
16
  ${a.yellow("Available commands")}:
17
- `);const s=Math.max(...e.map(m=>m.command.length))??0,o={};for(const m of e){const c=m.group??m.command.split(":")[0];o[c]||(o[c]=[]),o[c].push(m)}const u=Object.entries(o).sort(([m],[c])=>m.toLowerCase().localeCompare(c.toLowerCase())).sort(([,m],[,c])=>m.length-c.length);for(const[m,c]of u){const l=c.length>1;l&&this.io.log(a.yellow(`${m}:`));const d=c.sort((p,h)=>p.command.toLowerCase().localeCompare(h.command.toLowerCase()));for(const p of d){let h=A(s-p.command.length);l&&(h=h.slice(2)),this.io.log(`${l?" ":""}${a.green(p.command)} ${h} ${p.description}`)}}}}class z{ctx;logger;commandRegistry;exceptionHandler;helpCommand;newCommandRegistry(e){return new I(e.logger)}newHelpCommand(e){return new U(e)}newExceptionHandler(e){return new D(e.logger)}constructor(e={}){this.ctx=e.ctx,this.logger=e.logger??new E,this.commandRegistry=this.newCommandRegistry({logger:this.logger}),this.exceptionHandler=this.newExceptionHandler({logger:this.logger}),this.helpCommand=this.newHelpCommand({cliName:e.name,cliVersion:e.version,commandRegistry:this.commandRegistry})}withCommandResolver(e){return this.commandRegistry.withCommandResolver(e),this}withFileImporter(e){return this.commandRegistry.withFileImporter(e),this}async withCommands(...e){for(const t of e)typeof t=="string"?await this.commandRegistry.loadCommandsPath(t):typeof t=="function"?this.registerCommand(new t):this.registerCommand(t)}async runCommand(e,...t){return e?await this.commandRegistry.runCommand(this.ctx??{},e,...t).catch(this.exceptionHandler.handle.bind(this.exceptionHandler)):await this.runHelpCommand()}async runHelpCommand(){return await this.runCommand(this.helpCommand)}registerCommand(e){this.commandRegistry.registerCommand(e)}}exports.BadCommandOption=C;exports.BadCommandParameter=B;exports.BobError=f;exports.Cli=z;exports.Command=v;exports.CommandIO=R;exports.CommandNotFoundError=L;exports.CommandParser=x;exports.CommandRegistry=I;exports.CommandSignatureParser=$;exports.CommandWithSignature=G;exports.ExceptionHandler=D;exports.HelpOption=V;exports.InvalidOption=b;exports.Logger=E;exports.MissingRequiredArgumentValue=O;exports.MissingRequiredOptionValue=P;
17
+ `);const r=Math.max(...t.map(u=>u.command.length))??0,o={};for(const u of t){const c=u.group??u.command.split(":")[0];o[c]||(o[c]=[]),o[c].push(u)}const m=Object.entries(o).sort(([u],[c])=>u.toLowerCase().localeCompare(c.toLowerCase())).sort(([,u],[,c])=>u.length-c.length);for(const[u,c]of m){const l=c.length>1;l&&this.io.log(a.yellow(`${u}:`));const d=c.sort((h,p)=>h.command.toLowerCase().localeCompare(p.command.toLowerCase()));for(const h of d){let p=A(r-h.command.length);l&&(p=p.slice(2)),this.io.log(`${l?" ":""}${a.green(h.command)} ${p} ${h.description}`)}}}}class z{ctx;logger;commandRegistry;exceptionHandler;helpCommand;newCommandRegistry(t){return new V(t)}newHelpCommand(t){return new G(t)}newExceptionHandler(t){return new D(t.logger)}constructor(t={}){this.ctx=t.ctx,this.logger=t.logger??new S,this.commandRegistry=this.newCommandRegistry({logger:this.logger}),this.exceptionHandler=this.newExceptionHandler({logger:this.logger}),this.helpCommand=this.newHelpCommand({cliName:t.name,cliVersion:t.version,commandRegistry:this.commandRegistry})}withCommandResolver(t){return this.commandRegistry.withCommandResolver(t),this}withFileImporter(t){return this.commandRegistry.withFileImporter(t),this}async withCommands(...t){for(const e of t)typeof e=="string"?await this.commandRegistry.loadCommandsPath(e):typeof e=="function"?this.registerCommand(new e):this.registerCommand(e)}async runCommand(t,...e){return t?await this.commandRegistry.runCommand(this.ctx??{},t,...e).catch(this.exceptionHandler.handle.bind(this.exceptionHandler)):await this.runHelpCommand()}async runHelpCommand(){return await this.runCommand(this.helpCommand)}registerCommand(t){this.commandRegistry.registerCommand(t)}}exports.BadCommandOption=C;exports.BadCommandParameter=B;exports.BobError=f;exports.Cli=z;exports.Command=v;exports.CommandIO=R;exports.CommandNotFoundError=P;exports.CommandParser=x;exports.CommandRegistry=V;exports.CommandSignatureParser=b;exports.CommandWithSignature=_;exports.ExceptionHandler=D;exports.HelpOption=L;exports.InvalidOption=$;exports.Logger=S;exports.MissingRequiredArgumentValue=O;exports.MissingRequiredOptionValue=N;exports.StringSimilarity=I;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="bob-core",t="2.0.0-beta.14",s="BOB Core",i="module",n=!1,r=["dist/**"],o={".":{import:"./dist/esm/index.js",require:"./dist/cjs/index.js"}},c={"*":{"*":["./dist/cjs/*.d.ts"]}},p={start:"node -r @swc-node/register debug/main.ts",build:"rimraf ./dist && vite build",typecheck:"tsc --noEmit",prepack:"npm run build",test:"vitest run",lint:"eslint .","lint:fix":"eslint . --fix"},d="Léo Hubert",l="ISC",a={"@eslint/js":"^9.37.0","@faker-js/faker":"^10.0.0","@swc-node/register":"^1.11.1","@trivago/prettier-plugin-sort-imports":"^5.2.2","@types/minimist":"^1.2.5","@types/node":"^20.14.5","@types/prompts":"^2.4.9","@types/string-similarity":"^4.0.2","@vitest/coverage-v8":"^3.2.4",eslint:"^9.37.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4",prettier:"^3.6.2",rimraf:"^6.0.1",tsx:"^4.20.6",typescript:"^5.7.3","typescript-eslint":"^8.46.0",vite:"^7.1.6","vite-plugin-dts":"^4.5.4",vitest:"^3.2.4"},m={chalk:"^5.6.2",minimist:"^1.2.8",prompts:"^2.4.2","string-similarity":"^4.0.4"},u={name:e,version:t,description:s,type:i,sideEffects:n,files:r,exports:o,typesVersions:c,scripts:p,author:d,license:l,devDependencies:a,dependencies:m};exports.author=d;exports.default=u;exports.dependencies=m;exports.description=s;exports.devDependencies=a;exports.exports=o;exports.files=r;exports.license=l;exports.name=e;exports.scripts=p;exports.sideEffects=n;exports.type=i;exports.typesVersions=c;exports.version=t;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="bob-core",t="2.0.0-beta.15",s="BOB Core",i="module",n=!1,r=["dist/**"],o={".":{import:"./dist/esm/index.js",require:"./dist/cjs/index.js"}},c={"*":{"*":["./dist/cjs/*.d.ts"]}},p={start:"node -r @swc-node/register debug/main.ts",build:"rimraf ./dist && vite build",typecheck:"tsc --noEmit",prepack:"npm run build",test:"vitest run",lint:"eslint .","lint:fix":"eslint . --fix"},d="Léo Hubert",l="ISC",a={"@eslint/js":"^9.37.0","@faker-js/faker":"^10.0.0","@swc-node/register":"^1.11.1","@trivago/prettier-plugin-sort-imports":"^5.2.2","@types/minimist":"^1.2.5","@types/node":"^20.14.5","@types/prompts":"^2.4.9","@types/string-similarity":"^4.0.2","@vitest/coverage-v8":"^3.2.4",eslint:"^9.37.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4",prettier:"^3.6.2",rimraf:"^6.0.1",tsx:"^4.20.6",typescript:"^5.7.3","typescript-eslint":"^8.46.0",vite:"^7.1.6","vite-plugin-dts":"^4.5.4",vitest:"^3.2.4"},m={chalk:"^5.6.2",minimist:"^1.2.8",prompts:"^2.4.2"},u={name:e,version:t,description:s,type:i,sideEffects:n,files:r,exports:o,typesVersions:c,scripts:p,author:d,license:l,devDependencies:a,dependencies:m};exports.author=d;exports.default=u;exports.dependencies=m;exports.description=s;exports.devDependencies=a;exports.exports=o;exports.files=r;exports.license=l;exports.name=e;exports.scripts=p;exports.sideEffects=n;exports.type=i;exports.typesVersions=c;exports.version=t;
@@ -1,5 +1,5 @@
1
1
  import { Command } from './Command.js';
2
- import { CommandRegistry, CommandResolver, FileImporter } from './CommandRegistry.js';
2
+ import { CommandRegistry, CommandRegistryOptions, CommandResolver, FileImporter } from './CommandRegistry.js';
3
3
  import { ExceptionHandler } from './ExceptionHandler.js';
4
4
  import { Logger } from './Logger.js';
5
5
  import { default as HelpCommand, HelpCommandOptions } from './commands/HelpCommand.js';
@@ -16,9 +16,7 @@ export declare class Cli<C extends ContextDefinition = ContextDefinition> {
16
16
  readonly commandRegistry: CommandRegistry;
17
17
  private readonly exceptionHandler;
18
18
  private readonly helpCommand;
19
- protected newCommandRegistry(opts: {
20
- logger: Logger;
21
- }): CommandRegistry;
19
+ protected newCommandRegistry(opts: CommandRegistryOptions): CommandRegistry;
22
20
  protected newHelpCommand(opts: HelpCommandOptions): HelpCommand;
23
21
  protected newExceptionHandler(opts: {
24
22
  logger: Logger;
@@ -1,4 +1,4 @@
1
- import { CommandIO } from './CommandIO.js';
1
+ import { CommandIO, CommandIOOptions } from './CommandIO.js';
2
2
  import { CommandParser } from './CommandParser.js';
3
3
  import { Logger } from './Logger.js';
4
4
  import { CommandOption } from './contracts/index.js';
@@ -43,9 +43,7 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
43
43
  options: Options;
44
44
  arguments: Arguments;
45
45
  }): CommandParser<Options, Arguments>;
46
- protected newCommandIO(opts: {
47
- logger: Logger;
48
- }): CommandIO;
46
+ protected newCommandIO(opts: CommandIOOptions): CommandIO;
49
47
  constructor(command: string, opts?: {
50
48
  description?: string;
51
49
  group?: string;
@@ -6,9 +6,12 @@ export type SelectOption = {
6
6
  selected?: boolean | undefined;
7
7
  description?: string | undefined;
8
8
  };
9
+ export type CommandIOOptions = {
10
+ logger: Logger;
11
+ };
9
12
  export declare class CommandIO {
10
13
  private logger;
11
- constructor(logger: Logger);
14
+ constructor(opts: CommandIOOptions);
12
15
  /**
13
16
  * Logger methods
14
17
  */
@@ -1,15 +1,21 @@
1
1
  import { Command } from './Command.js';
2
- import { CommandIO } from './CommandIO.js';
2
+ import { CommandIO, CommandIOOptions } from './CommandIO.js';
3
3
  import { Logger } from './Logger.js';
4
+ import { StringSimilarity } from './StringSimilarity.js';
4
5
  import { ArgumentsSchema, ContextDefinition, OptionsSchema } from './lib/types.js';
5
6
  export type CommandResolver = (path: string) => Promise<Command | null>;
6
7
  export type FileImporter = (filePath: string) => Promise<unknown>;
8
+ export type CommandRegistryOptions = {
9
+ logger?: Logger;
10
+ stringSimilarity?: StringSimilarity;
11
+ };
7
12
  export declare class CommandRegistry {
8
13
  private readonly commands;
9
14
  protected readonly io: CommandIO;
10
15
  protected readonly logger: Logger;
11
- protected get CommandIOClass(): typeof CommandIO;
12
- constructor(logger?: Logger);
16
+ private readonly stringSimilarity;
17
+ protected newCommandIO(opts: CommandIOOptions): CommandIO;
18
+ constructor(opts?: CommandRegistryOptions);
13
19
  getAvailableCommands(): string[];
14
20
  getCommands(): Array<Command>;
15
21
  private importFile;
@@ -0,0 +1,26 @@
1
+ export interface SimilarityResult {
2
+ rating: number;
3
+ target: string;
4
+ }
5
+ export interface BestMatchResult {
6
+ bestMatch: SimilarityResult;
7
+ bestMatchIndex: number;
8
+ ratings: SimilarityResult[];
9
+ }
10
+ /**
11
+ * String similarity calculator using Dice's Coefficient algorithm
12
+ */
13
+ export declare class StringSimilarity {
14
+ /**
15
+ * Generate bigrams (character pairs) from a string
16
+ */
17
+ private getBigrams;
18
+ /**
19
+ * Calculate Dice's Coefficient similarity between two strings (0-1 scale)
20
+ */
21
+ calculateSimilarity(str1: string, str2: string): number;
22
+ /**
23
+ * Find best matching string and ratings for all candidates
24
+ */
25
+ findBestMatch(target: string, candidates: string[]): BestMatchResult;
26
+ }
@@ -7,6 +7,7 @@ export * from './CommandRegistry.js';
7
7
  export * from './Cli.js';
8
8
  export * from './Logger.js';
9
9
  export * from './ExceptionHandler.js';
10
+ export * from './StringSimilarity.js';
10
11
  export * from './lib/types.js';
11
12
  export * from './errors/index.js';
12
13
  export * from './options/index.js';
package/dist/esm/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  import y from "prompts";
2
2
  import a from "chalk";
3
- import L from "minimist";
4
- import * as V from "node:fs";
3
+ import q from "minimist";
4
+ import * as I from "node:fs";
5
5
  import R from "path";
6
- import * as I from "string-similarity";
7
- class k {
6
+ class S {
8
7
  logger;
9
8
  constructor(t) {
10
- this.logger = t;
9
+ this.logger = t.logger;
11
10
  }
12
11
  /**
13
12
  * Logger methods
@@ -77,31 +76,31 @@ class k {
77
76
  async askForSelect(t, e, i) {
78
77
  if (e.length === 0)
79
78
  throw new Error("No options provided");
80
- const s = [];
79
+ const n = [];
81
80
  for (const o of e)
82
- typeof o == "string" ? s.push({ title: o, value: o }) : s.push(o);
81
+ typeof o == "string" ? n.push({ title: o, value: o }) : n.push(o);
83
82
  return (await y({
84
83
  type: "select",
85
84
  name: "value",
86
85
  message: t,
87
- choices: s,
86
+ choices: n,
88
87
  ...i
89
88
  }))?.value ?? null;
90
89
  }
91
90
  newLoader(t = "", e = ["⠙", "⠘", "⠰", "⠴", "⠤", "⠦", "⠆", "⠃", "⠋", "⠉"], i = 100) {
92
- let s = t, n = null, o = 0;
93
- const u = setInterval(function() {
94
- n && (process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(n.length + 5) + "\r")), n = null), process.stdout.write(new TextEncoder().encode("\r" + e[o++] + " " + s)), o = o % e.length;
95
- }, i), m = () => {
96
- clearInterval(u), process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(s.length + 5) + "\r"));
91
+ let n = t, s = null, o = 0;
92
+ const m = setInterval(function() {
93
+ s && (process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(s.length + 5) + "\r")), s = null), process.stdout.write(new TextEncoder().encode("\r" + e[o++] + " " + n)), o = o % e.length;
94
+ }, i), u = () => {
95
+ clearInterval(m), process.stdout.write(new TextEncoder().encode("\r" + " ".repeat(n.length + 5) + "\r"));
97
96
  };
98
97
  return {
99
- [Symbol.dispose]: m,
100
- [Symbol.asyncDispose]: m,
101
- updateText: (p) => {
102
- n = s, s = p;
98
+ [Symbol.dispose]: u,
99
+ [Symbol.asyncDispose]: u,
100
+ updateText: (h) => {
101
+ s = n, n = h;
103
102
  },
104
- stop: m
103
+ stop: u
105
104
  };
106
105
  }
107
106
  }
@@ -147,9 +146,9 @@ class b extends f {
147
146
  if (e.length > 0) {
148
147
  t.log(`
149
148
  ${a.yellow("Available options")}:`);
150
- for (const [i, s] of e) {
151
- const n = g(s), o = typeof n.alias == "string" ? [n.alias] : n.alias, u = Array.isArray(n.type) ? `[${n.type[0]}]` : n.type, m = `--${i}${o.length > 0 ? o.map((l) => `, -${l}`).join("") : ""}`, p = " ".repeat(30 - m.length);
152
- t.log(` ${a.green(m)} ${p} ${n.description || "\b"} ${a.white(`(${u})`)}`);
149
+ for (const [i, n] of e) {
150
+ const s = g(n), o = typeof s.alias == "string" ? [s.alias] : s.alias, m = Array.isArray(s.type) ? `[${s.type[0]}]` : s.type, u = `--${i}${o.length > 0 ? o.map((l) => `, -${l}`).join("") : ""}`, h = " ".repeat(30 - u.length);
151
+ t.log(` ${a.green(u)} ${h} ${s.description || "\b"} ${a.white(`(${m})`)}`);
153
152
  }
154
153
  t.log("");
155
154
  }
@@ -164,7 +163,7 @@ class F extends f {
164
163
  t.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`);
165
164
  }
166
165
  }
167
- class P extends f {
166
+ class V extends f {
168
167
  constructor(t) {
169
168
  super(`Argument "${t}" is required.`), this.option = t;
170
169
  }
@@ -190,7 +189,7 @@ class C extends f {
190
189
  t.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `), (this.param.value || this.param.reason) && t.log(""), this.param.value && t.log(` ${a.blue("Value")}: ${this.param.value}`), this.param.reason && t.log(` ${a.yellow("Reason")}: ${this.param.reason}`);
191
190
  }
192
191
  }
193
- class D extends f {
192
+ class P extends f {
194
193
  constructor(t) {
195
194
  super(`Command "${t}" not found.`), this.command = t;
196
195
  }
@@ -204,34 +203,34 @@ function w(r, t, e, i) {
204
203
  if (t === "string")
205
204
  return String(r);
206
205
  if (t === "number") {
207
- const s = Number(r);
208
- if (isNaN(s))
206
+ const n = Number(r);
207
+ if (isNaN(n))
209
208
  throw new C({
210
209
  option: e,
211
210
  reason: `Expected a number, got "${r}"`
212
211
  });
213
- return s;
212
+ return n;
214
213
  }
215
214
  if (t === "boolean")
216
215
  return typeof r == "boolean" ? r : r === "true" || r === "1" ? !0 : r === "false" || r === "0" ? !1 : !!r;
217
216
  if (Array.isArray(t)) {
218
- const s = t[0], n = Array.isArray(r) ? r : [r];
219
- if (s === "string")
220
- return n.map((o) => String(o));
221
- if (s === "number")
222
- return n.map((o) => {
223
- const u = Number(o);
224
- if (isNaN(u))
217
+ const n = t[0], s = Array.isArray(r) ? r : [r];
218
+ if (n === "string")
219
+ return s.map((o) => String(o));
220
+ if (n === "number")
221
+ return s.map((o) => {
222
+ const m = Number(o);
223
+ if (isNaN(m))
225
224
  throw new C({
226
225
  option: e,
227
226
  reason: `Expected array of numbers, got "${o}" in array`
228
227
  });
229
- return u;
228
+ return m;
230
229
  });
231
230
  }
232
231
  return r;
233
232
  }
234
- class N {
233
+ class k {
235
234
  options;
236
235
  parsedOptions = null;
237
236
  arguments;
@@ -250,7 +249,7 @@ class N {
250
249
  * @throws {BadCommandOption} If a value cannot be converted to the expected type
251
250
  */
252
251
  init(t) {
253
- const { _: e, ...i } = L(t);
252
+ const { _: e, ...i } = q(t);
254
253
  return this.validateUnknownOptions(i), this.parsedOptions = this.handleOptions(i), this.parsedArguments = this.handleArguments(e), {
255
254
  options: this.parsedOptions,
256
255
  arguments: this.parsedArguments
@@ -263,14 +262,14 @@ class N {
263
262
  async validate() {
264
263
  for (const t in this.options)
265
264
  if (g(this.options[t]).required && (this.parsedOptions?.[t] === void 0 || this.parsedOptions?.[t] === null))
266
- throw new P(t);
265
+ throw new V(t);
267
266
  for (const t in this.arguments) {
268
267
  const e = g(this.arguments[t]), i = this.parsedArguments?.[t];
269
268
  if (e.required && i == null) {
270
269
  if (this.shouldPromptForMissingOptions) {
271
- const s = await this.promptForArgument(t, e);
272
- if (s && this.parsedArguments) {
273
- this.parsedArguments[t] = w(s, e.type, t);
270
+ const n = await this.promptForArgument(t, e);
271
+ if (n && this.parsedArguments) {
272
+ this.parsedArguments[t] = w(n, e.type, t);
274
273
  continue;
275
274
  }
276
275
  }
@@ -278,9 +277,9 @@ class N {
278
277
  }
279
278
  if (e.required && e.variadic && Array.isArray(i) && i.length === 0) {
280
279
  if (this.shouldPromptForMissingOptions) {
281
- const s = await this.promptForArgument(t, e);
282
- if (s && this.parsedArguments) {
283
- this.parsedArguments[t] = w(s, e.type, t);
280
+ const n = await this.promptForArgument(t, e);
281
+ if (n && this.parsedArguments) {
282
+ this.parsedArguments[t] = w(n, e.type, t);
284
283
  continue;
285
284
  }
286
285
  }
@@ -343,9 +342,9 @@ class N {
343
342
  const e = /* @__PURE__ */ new Set();
344
343
  for (const i in this.options) {
345
344
  e.add(i);
346
- const s = g(this.options[i]);
347
- for (const n of s.alias)
348
- e.add(n);
345
+ const n = g(this.options[i]);
346
+ for (const s of n.alias)
347
+ e.add(s);
349
348
  }
350
349
  for (const i in t)
351
350
  if (!e.has(i))
@@ -357,8 +356,8 @@ class N {
357
356
  handleOptions(t) {
358
357
  const e = {};
359
358
  for (const i in this.options) {
360
- const s = g(this.options[i]);
361
- e[i] = this.resolveOptionValue(i, s, t);
359
+ const n = g(this.options[i]);
360
+ e[i] = this.resolveOptionValue(i, n, t);
362
361
  }
363
362
  return e;
364
363
  }
@@ -367,13 +366,13 @@ class N {
367
366
  */
368
367
  handleArguments(t) {
369
368
  const e = {}, i = [...t];
370
- for (const s in this.arguments) {
371
- const n = g(this.arguments[s]);
372
- if (n.variadic) {
373
- e[s] = this.handleVariadicArgument(s, n, i);
369
+ for (const n in this.arguments) {
370
+ const s = g(this.arguments[n]);
371
+ if (s.variadic) {
372
+ e[n] = this.handleVariadicArgument(n, s, i);
374
373
  continue;
375
374
  }
376
- e[s] = this.resolveArgumentValue(s, n, i.shift());
375
+ e[n] = this.resolveArgumentValue(n, s, i.shift());
377
376
  }
378
377
  return e;
379
378
  }
@@ -395,14 +394,14 @@ class N {
395
394
  * Handles alias resolution, defaults, and type conversion
396
395
  */
397
396
  resolveOptionValue(t, e, i) {
398
- let s;
399
- const n = [t, ...e.alias];
400
- for (const o of n)
397
+ let n;
398
+ const s = [t, ...e.alias];
399
+ for (const o of s)
401
400
  if (o in i) {
402
- s = i[o];
401
+ n = i[o];
403
402
  break;
404
403
  }
405
- if (s === void 0) {
404
+ if (n === void 0) {
406
405
  if (e.required)
407
406
  throw new C({
408
407
  option: t,
@@ -410,7 +409,7 @@ class N {
410
409
  });
411
410
  return e.default;
412
411
  }
413
- return w(s, e.type, t, e.default);
412
+ return w(n, e.type, t, e.default);
414
413
  }
415
414
  optionDefinitions() {
416
415
  const t = {};
@@ -452,26 +451,26 @@ class N {
452
451
  `, Array.isArray(e.type) ? (i += `Please provide one or more values, separated by commas:
453
452
  `, await this.io.askForList(i, void 0, {
454
453
  separator: ",",
455
- validate: (s) => {
456
- if (!s.length)
454
+ validate: (n) => {
455
+ if (!n.length)
457
456
  return "Please enter at least one value";
458
457
  if (e.type[0] === "number") {
459
- for (const n of s.split(","))
460
- if (isNaN(Number(n)))
458
+ for (const s of n.split(","))
459
+ if (isNaN(Number(s)))
461
460
  return "Please enter only valid numbers";
462
461
  }
463
462
  return !0;
464
463
  }
465
464
  })) : await this.io.askForInput(i, void 0, {
466
465
  type: e.type === "number" ? "number" : e.secret ? "password" : "text",
467
- validate: (s) => {
468
- if (s == null || typeof s == "string" && !s.length)
466
+ validate: (n) => {
467
+ if (n == null || typeof n == "string" && !n.length)
469
468
  return "This value is required";
470
469
  if (e.type === "number") {
471
- const n = Number(s);
472
- if (isNaN(n))
470
+ const s = Number(n);
471
+ if (isNaN(s))
473
472
  return "Please enter a valid number";
474
- } else if (e.type === "string" && (typeof s != "string" || !s.length))
473
+ } else if (e.type === "string" && (typeof n != "string" || !n.length))
475
474
  return "Please enter a valid text";
476
475
  return !0;
477
476
  }
@@ -481,52 +480,52 @@ class N {
481
480
  function A(r) {
482
481
  return new Array(r + 5).join(" ");
483
482
  }
484
- class H {
483
+ class D {
485
484
  type = "boolean";
486
485
  option = "help";
487
486
  alias = ["h"];
488
487
  default = !1;
489
488
  description = `Display help for the given command. When no command is given display help for the ${a.green("list")} command`;
490
489
  async handler() {
491
- const t = this.parser.argumentDefinitions(), e = this.parser.optionDefinitions(), i = Object.entries(t), s = Object.entries(e), n = s.map(([l, d]) => {
492
- const h = Array.isArray(d.alias) ? d.alias : d.alias ? [d.alias] : [];
490
+ const t = this.parser.argumentDefinitions(), e = this.parser.optionDefinitions(), i = Object.entries(t), n = Object.entries(e), s = n.map(([l, d]) => {
491
+ const c = Array.isArray(d.alias) ? d.alias : d.alias ? [d.alias] : [];
493
492
  return {
494
493
  name: l,
495
494
  ...d,
496
- optionWithAlias: `--${l}${h.map((c) => `, -${c}`).join("")}`
495
+ optionWithAlias: `--${l}${c.map((p) => `, -${p}`).join("")}`
497
496
  };
498
497
  }), o = i.filter(([, l]) => l.required);
499
498
  this.io.log(a.yellow("Description:")), this.io.log(` ${this.description}
500
499
  `), this.io.log(a.yellow("Usage:")), this.io.log(` ${this.command} ${o.length > 0 ? o.map(([l]) => `<${l}>`).join(" ") : "\b"} [options]`);
501
- const u = Math.max(...n.map((l) => l.optionWithAlias.length), 0), m = Math.max(...i.map(([l]) => l.length), 0), p = m > u ? m : u;
500
+ const m = Math.max(...s.map((l) => l.optionWithAlias.length), 0), u = Math.max(...i.map(([l]) => l.length), 0), h = u > m ? u : m;
502
501
  if (i.length > 0) {
503
502
  this.io.log(`
504
503
  ${a.yellow("Arguments")}:`);
505
504
  for (const [l, d] of i) {
506
- const h = A(p - l.length);
507
- let c = ` ${a.green(l)} ${h} ${d.description ?? "\b"}`;
505
+ const c = A(h - l.length);
506
+ let p = ` ${a.green(l)} ${c} ${d.description ?? "\b"}`;
508
507
  if (d.default !== void 0 && !d.required) {
509
- const q = (Array.isArray(d.type) ? `[${d.type[0]}]` : d.type) === "array" || Array.isArray(d.type) ? JSON.stringify(d.default) : d.default;
510
- c += ` ${a.yellow(`[default: ${q}]`)}`;
508
+ const L = (Array.isArray(d.type) ? `[${d.type[0]}]` : d.type) === "array" || Array.isArray(d.type) ? JSON.stringify(d.default) : d.default;
509
+ p += ` ${a.yellow(`[default: ${L}]`)}`;
511
510
  }
512
- d.variadic && (c += ` ${a.white("(variadic)")}`), this.io.log(c);
511
+ d.variadic && (p += ` ${a.white("(variadic)")}`), this.io.log(p);
513
512
  }
514
513
  }
515
- if (s.length > 0) {
514
+ if (n.length > 0) {
516
515
  this.io.log(`
517
516
  ${a.yellow("Options")}:`);
518
- for (const l of n) {
519
- const d = A(p - l.optionWithAlias.length);
520
- let h = `${a.green(l.optionWithAlias)} ${d} ${l.description ?? "\b"}`;
517
+ for (const l of s) {
518
+ const d = A(h - l.optionWithAlias.length);
519
+ let c = `${a.green(l.optionWithAlias)} ${d} ${l.description ?? "\b"}`;
521
520
  if (l.type) {
522
- const c = Array.isArray(l.type) ? `[${l.type[0]}]` : l.type;
523
- h += ` ${a.white(`(${c})`)}`;
521
+ const p = Array.isArray(l.type) ? `[${l.type[0]}]` : l.type;
522
+ c += ` ${a.white(`(${p})`)}`;
524
523
  }
525
524
  if (l.default !== void 0 && !l.required) {
526
525
  const O = (Array.isArray(l.type) ? `[${l.type[0]}]` : l.type) === "array" || Array.isArray(l.type) ? JSON.stringify(l.default) : l.default;
527
- h += ` ${a.yellow(`[default: ${O}]`)}`;
526
+ c += ` ${a.yellow(`[default: ${O}]`)}`;
528
527
  }
529
- this.io.log(h);
528
+ this.io.log(c);
530
529
  }
531
530
  }
532
531
  if (this.commandsExamples.length > 0) {
@@ -534,9 +533,9 @@ ${a.yellow("Options")}:`);
534
533
  ${a.yellow("Examples")}:`);
535
534
  let l = process.argv[0].split("/").pop();
536
535
  l === "node" && (l += " " + process.argv[1].split("/").pop());
537
- for (const [d, h] of this.commandsExamples.entries())
538
- d > 0 && this.io.log(""), this.io.log(` ${h.description}
539
- `), this.io.log(` ${a.green(`${l} ${h.command}`)}`);
536
+ for (const [d, c] of this.commandsExamples.entries())
537
+ d > 0 && this.io.log(""), this.io.log(` ${c.description}
538
+ `), this.io.log(` ${a.green(`${l} ${c.command}`)}`);
540
539
  }
541
540
  return -1;
542
541
  }
@@ -558,17 +557,17 @@ class v {
558
557
  _handler;
559
558
  tmp;
560
559
  defaultOptions() {
561
- return [new H()];
560
+ return [new D()];
562
561
  }
563
562
  newCommandParser(t) {
564
- return new N({
563
+ return new k({
565
564
  io: t.io,
566
565
  options: t.options,
567
566
  arguments: t.arguments
568
567
  });
569
568
  }
570
569
  newCommandIO(t) {
571
- return new k(t.logger);
570
+ return new S(t);
572
571
  }
573
572
  constructor(t, e) {
574
573
  this._command = t, this.description = e?.description ?? "", this.group = e?.group, this.tmp = {
@@ -577,8 +576,8 @@ class v {
577
576
  };
578
577
  const i = this.defaultOptions();
579
578
  if (i.length > 0)
580
- for (const s of i)
581
- this.tmp.options[s.option] = s;
579
+ for (const n of i)
580
+ this.tmp.options[n.option] = n;
582
581
  }
583
582
  disablePrompting() {
584
583
  return this.disablePromptingFlag = !0, this;
@@ -614,19 +613,19 @@ class v {
614
613
  if (this.ctx = t.ctx, this.logger = t.logger, this.io = this.newCommandIO({
615
614
  logger: t.logger
616
615
  }), t && "args" in t) {
617
- const n = this.tmp?.options ?? {};
616
+ const s = this.tmp?.options ?? {};
618
617
  for (const o of this.defaultOptions())
619
- o.option in n || (n[o.option] = o);
618
+ o.option in s || (s[o.option] = o);
620
619
  this.parser = this.newCommandParser({
621
620
  io: this.io,
622
- options: n,
621
+ options: s,
623
622
  arguments: this.tmp?.arguments ?? {}
624
623
  }), e = this.parser.init(t.args);
625
624
  for (const o of this.defaultOptions())
626
625
  if (e.options[o.option] === !0) {
627
- const u = await o.handler.call(this);
628
- if (u && u !== 0)
629
- return u;
626
+ const m = await o.handler.call(this);
627
+ if (m && m !== 0)
628
+ return m;
630
629
  }
631
630
  this.disablePromptingFlag && this.parser.disablePrompting(), await this.parser.validate();
632
631
  } else
@@ -644,7 +643,7 @@ class v {
644
643
  return await this._handler(t.ctx, e) ?? 0;
645
644
  }
646
645
  }
647
- class $ extends N {
646
+ class $ extends k {
648
647
  command;
649
648
  constructor(t) {
650
649
  const e = $.parseSignature(t.signature, t.helperDefinitions, t.defaultOptions);
@@ -659,24 +658,24 @@ class $ extends N {
659
658
  * Example: "migrate {name} {--force}" -> { command: "migrate", arguments: {name: ...}, options: {force: ...} }
660
659
  */
661
660
  static parseSignature(t, e, i) {
662
- const [s, ...n] = t.split(/\{(.*?)\}/g).map((m) => m.trim()).filter(Boolean), o = {}, u = {};
663
- for (const m of n) {
664
- const { name: p, isOption: l, definition: d } = $.parseParamSignature(m, e);
665
- l ? o[p] = d : u[p] = d;
661
+ const [n, ...s] = t.split(/\{(.*?)\}/g).map((u) => u.trim()).filter(Boolean), o = {}, m = {};
662
+ for (const u of s) {
663
+ const { name: h, isOption: l, definition: d } = $.parseParamSignature(u, e);
664
+ l ? o[h] = d : m[h] = d;
666
665
  }
667
- for (const m of i)
668
- o[m.option] = {
669
- type: m.type,
670
- required: m.required,
671
- alias: m.alias,
672
- variadic: m.variadic ?? !1,
673
- description: m.description,
674
- default: m.default ?? null
666
+ for (const u of i)
667
+ o[u.option] = {
668
+ type: u.type,
669
+ required: u.required,
670
+ alias: u.alias,
671
+ variadic: u.variadic ?? !1,
672
+ description: u.description,
673
+ default: u.default ?? null
675
674
  };
676
675
  return {
677
- command: s,
676
+ command: n,
678
677
  options: o,
679
- arguments: u
678
+ arguments: m
680
679
  };
681
680
  }
682
681
  /**
@@ -694,8 +693,8 @@ class $ extends N {
694
693
  * - {--opt|o} -> option with alias
695
694
  */
696
695
  static parseParamSignature(t, e) {
697
- let i = t, s = !1;
698
- const n = {
696
+ let i = t, n = !1;
697
+ const s = {
699
698
  required: !0,
700
699
  type: "string",
701
700
  description: void 0,
@@ -703,18 +702,18 @@ class $ extends N {
703
702
  variadic: !1
704
703
  };
705
704
  if (i.includes(":")) {
706
- const [o, u] = i.split(":");
707
- i = o.trim(), n.description = u.trim();
705
+ const [o, m] = i.split(":");
706
+ i = o.trim(), s.description = m.trim();
708
707
  }
709
708
  if (i.includes("=")) {
710
- const [o, u] = i.split("=");
711
- i = o.trim(), n.default = u.trim(), n.required = !1, typeof n.default == "string" && !n.default.length ? n.default = null : n.default === "true" ? (n.default = !0, n.type = "boolean") : n.default === "false" && (n.default = !1, n.type = "boolean");
712
- } else i.startsWith("--") && (n.required = !1, n.default = !1, n.type = "boolean");
709
+ const [o, m] = i.split("=");
710
+ i = o.trim(), s.default = m.trim(), s.required = !1, typeof s.default == "string" && !s.default.length ? s.default = null : s.default === "true" ? (s.default = !0, s.type = "boolean") : s.default === "false" && (s.default = !1, s.type = "boolean");
711
+ } else i.startsWith("--") && (s.required = !1, s.default = !1, s.type = "boolean");
713
712
  if (i.includes("|")) {
714
- const [o, ...u] = i.split("|");
715
- i = o.trim(), n.alias = u.map((m) => m.trim());
713
+ const [o, ...m] = i.split("|");
714
+ i = o.trim(), s.alias = m.map((u) => u.trim());
716
715
  }
717
- return i.startsWith("--") && (s = !0, i = i.slice(2)), n.default === "*" && (n.default = [], n.type = ["string"]), i.endsWith("?") && (n.required = !1, i = i.slice(0, -1)), i.endsWith("*") && (n.type = ["string"], n.variadic = !0, n.default = [], i = i.slice(0, -1)), n.description = n.description ?? e[i] ?? e[`--${i}`], { name: i, isOption: s, definition: n };
716
+ return i.startsWith("--") && (n = !0, i = i.slice(2)), s.default === "*" && (s.default = [], s.type = ["string"]), i.endsWith("?") && (s.required = !1, i = i.slice(0, -1)), i.endsWith("*") && (s.type = ["string"], s.variadic = !0, s.default = [], i = i.slice(0, -1)), s.description = s.description ?? e[i] ?? e[`--${i}`], { name: i, isOption: n, definition: s };
718
717
  }
719
718
  }
720
719
  class z extends v {
@@ -753,7 +752,7 @@ class z extends v {
753
752
  return this.io.newLoader(...t);
754
753
  }
755
754
  }
756
- class S {
755
+ class N {
757
756
  level;
758
757
  constructor(t = {}) {
759
758
  this.level = t.level ?? "info";
@@ -784,15 +783,58 @@ class S {
784
783
  this.shouldLog("debug") && console.log(...t);
785
784
  }
786
785
  }
786
+ class H {
787
+ /**
788
+ * Generate bigrams (character pairs) from a string
789
+ */
790
+ getBigrams(t) {
791
+ const e = [], i = t.toLowerCase();
792
+ for (let n = 0; n < i.length - 1; n++)
793
+ e.push(i.slice(n, n + 2));
794
+ return e;
795
+ }
796
+ /**
797
+ * Calculate Dice's Coefficient similarity between two strings (0-1 scale)
798
+ */
799
+ calculateSimilarity(t, e) {
800
+ if (t === e) return 1;
801
+ if (t.length < 2 || e.length < 2) return 0;
802
+ const i = this.getBigrams(t), n = this.getBigrams(e), s = new Set(n);
803
+ let o = 0;
804
+ for (const m of i)
805
+ s.has(m) && (o++, s.delete(m));
806
+ return 2 * o / (i.length + n.length);
807
+ }
808
+ /**
809
+ * Find best matching string and ratings for all candidates
810
+ */
811
+ findBestMatch(t, e) {
812
+ const i = e.map((o) => ({
813
+ target: o,
814
+ rating: this.calculateSimilarity(t, o)
815
+ }));
816
+ let n = 0, s = i[0]?.rating ?? 0;
817
+ for (let o = 1; o < i.length; o++)
818
+ i[o].rating > s && (s = i[o].rating, n = o);
819
+ return {
820
+ ratings: i,
821
+ bestMatch: i[n],
822
+ bestMatchIndex: n
823
+ };
824
+ }
825
+ }
787
826
  class j {
788
827
  commands = {};
789
828
  io;
790
829
  logger;
791
- get CommandIOClass() {
792
- return k;
830
+ stringSimilarity;
831
+ newCommandIO(t) {
832
+ return new S(t);
793
833
  }
794
834
  constructor(t) {
795
- this.logger = t ?? new S(), this.io = new this.CommandIOClass(this.logger);
835
+ this.logger = t?.logger ?? new N(), this.io = this.newCommandIO({
836
+ logger: this.logger
837
+ }), this.stringSimilarity = t?.stringSimilarity ?? new H();
796
838
  }
797
839
  getAvailableCommands() {
798
840
  return Object.keys(this.commands);
@@ -833,46 +875,46 @@ class j {
833
875
  }
834
876
  }
835
877
  async runCommand(t, e, ...i) {
836
- const s = typeof e == "string" ? this.commands[e] : e, n = typeof e == "string" ? e : s.command;
837
- if (!s) {
838
- const o = await this.suggestCommand(n);
878
+ const n = typeof e == "string" ? this.commands[e] : e, s = typeof e == "string" ? e : n.command;
879
+ if (!n) {
880
+ const o = await this.suggestCommand(s);
839
881
  return o ? await this.runCommand(t, o, ...i) : 1;
840
882
  }
841
- return await s.run({
883
+ return await n.run({
842
884
  ctx: t,
843
885
  logger: this.logger,
844
886
  args: i
845
887
  }) ?? 0;
846
888
  }
847
889
  async suggestCommand(t) {
848
- const e = this.getAvailableCommands(), { bestMatch: i, bestMatchIndex: s, ratings: n } = I.findBestMatch(t, e), o = n.filter((u) => u.rating > 0.3).map((u) => u.target);
890
+ const e = this.getAvailableCommands(), { bestMatch: i, bestMatchIndex: n, ratings: s } = this.stringSimilarity.findBestMatch(t, e), o = s.filter((m) => m.rating > 0.3).map((m) => m.target);
849
891
  if (i.rating > 0 && o.length <= 1 || i.rating > 0.7 && o.length > 1) {
850
- const u = e[s];
851
- return await this.askRunSimilarCommand(t, u) ? u : null;
892
+ const m = e[n];
893
+ return await this.askRunSimilarCommand(t, m) ? m : null;
852
894
  }
853
895
  if (o.length) {
854
896
  this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
855
897
  `);
856
- const u = await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"), o);
857
- if (u)
858
- return u;
898
+ const m = await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"), o);
899
+ if (m)
900
+ return m;
859
901
  }
860
- throw new D(t);
902
+ throw new P(t);
861
903
  }
862
904
  async askRunSimilarCommand(t, e) {
863
905
  return this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
864
906
  `), this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `);
865
907
  }
866
908
  async *listCommandsFiles(t) {
867
- const e = V.readdirSync(t, { withFileTypes: !0 });
909
+ const e = I.readdirSync(t, { withFileTypes: !0 });
868
910
  for (const i of e) {
869
- const s = R.resolve(t, i.name);
911
+ const n = R.resolve(t, i.name);
870
912
  if (i.isDirectory())
871
913
  yield* this.listCommandsFiles(R.resolve(t, i.name));
872
914
  else {
873
- if (!s.endsWith(".ts") && !s.endsWith(".js") && !s.endsWith(".mjs") && !s.endsWith(".cjs"))
915
+ if (!n.endsWith(".ts") && !n.endsWith(".js") && !n.endsWith(".mjs") && !n.endsWith(".cjs"))
874
916
  continue;
875
- yield s;
917
+ yield n;
876
918
  }
877
919
  }
878
920
  }
@@ -888,34 +930,34 @@ class W {
888
930
  throw t;
889
931
  }
890
932
  }
891
- class T extends v {
933
+ class M extends v {
892
934
  constructor(t) {
893
935
  super("help", {
894
936
  description: a.bold("Show help information about the CLI and its commands")
895
937
  }), this.opts = t;
896
938
  }
897
939
  async handle() {
898
- const t = this.opts.commandRegistry.getCommands(), e = this.opts.cliName ?? "Bob CLI", i = this.opts.cliVersion ?? "0.0.0", s = (await import("./package-vtMT-dat.js"))?.default?.version ?? "0.0.0";
899
- this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(s)})
940
+ const t = this.opts.commandRegistry.getCommands(), e = this.opts.cliName ?? "Bob CLI", i = this.opts.cliVersion ?? "0.0.0", n = (await import("./package-C-2LYcDa.js"))?.default?.version ?? "0.0.0";
941
+ this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(n)})
900
942
 
901
943
  ${a.yellow("Usage")}:
902
944
  command [options] [arguments]
903
945
 
904
946
  ${a.yellow("Available commands")}:
905
947
  `);
906
- const n = Math.max(...t.map((m) => m.command.length)) ?? 0, o = {};
907
- for (const m of t) {
908
- const p = m.group ?? m.command.split(":")[0];
909
- o[p] || (o[p] = []), o[p].push(m);
948
+ const s = Math.max(...t.map((u) => u.command.length)) ?? 0, o = {};
949
+ for (const u of t) {
950
+ const h = u.group ?? u.command.split(":")[0];
951
+ o[h] || (o[h] = []), o[h].push(u);
910
952
  }
911
- const u = Object.entries(o).sort(([m], [p]) => m.toLowerCase().localeCompare(p.toLowerCase())).sort(([, m], [, p]) => m.length - p.length);
912
- for (const [m, p] of u) {
913
- const l = p.length > 1;
914
- l && this.io.log(a.yellow(`${m}:`));
915
- const d = p.sort((h, c) => h.command.toLowerCase().localeCompare(c.command.toLowerCase()));
916
- for (const h of d) {
917
- let c = A(n - h.command.length);
918
- l && (c = c.slice(2)), this.io.log(`${l ? " " : ""}${a.green(h.command)} ${c} ${h.description}`);
953
+ const m = Object.entries(o).sort(([u], [h]) => u.toLowerCase().localeCompare(h.toLowerCase())).sort(([, u], [, h]) => u.length - h.length);
954
+ for (const [u, h] of m) {
955
+ const l = h.length > 1;
956
+ l && this.io.log(a.yellow(`${u}:`));
957
+ const d = h.sort((c, p) => c.command.toLowerCase().localeCompare(p.command.toLowerCase()));
958
+ for (const c of d) {
959
+ let p = A(s - c.command.length);
960
+ l && (p = p.slice(2)), this.io.log(`${l ? " " : ""}${a.green(c.command)} ${p} ${c.description}`);
919
961
  }
920
962
  }
921
963
  }
@@ -927,16 +969,16 @@ class J {
927
969
  exceptionHandler;
928
970
  helpCommand;
929
971
  newCommandRegistry(t) {
930
- return new j(t.logger);
972
+ return new j(t);
931
973
  }
932
974
  newHelpCommand(t) {
933
- return new T(t);
975
+ return new M(t);
934
976
  }
935
977
  newExceptionHandler(t) {
936
978
  return new W(t.logger);
937
979
  }
938
980
  constructor(t = {}) {
939
- this.ctx = t.ctx, this.logger = t.logger ?? new S(), this.commandRegistry = this.newCommandRegistry({
981
+ this.ctx = t.ctx, this.logger = t.logger ?? new N(), this.commandRegistry = this.newCommandRegistry({
940
982
  logger: this.logger
941
983
  }), this.exceptionHandler = this.newExceptionHandler({
942
984
  logger: this.logger
@@ -972,16 +1014,17 @@ export {
972
1014
  f as BobError,
973
1015
  J as Cli,
974
1016
  v as Command,
975
- k as CommandIO,
976
- D as CommandNotFoundError,
977
- N as CommandParser,
1017
+ S as CommandIO,
1018
+ P as CommandNotFoundError,
1019
+ k as CommandParser,
978
1020
  j as CommandRegistry,
979
1021
  $ as CommandSignatureParser,
980
1022
  z as CommandWithSignature,
981
1023
  W as ExceptionHandler,
982
- H as HelpOption,
1024
+ D as HelpOption,
983
1025
  b as InvalidOption,
984
- S as Logger,
1026
+ N as Logger,
985
1027
  F as MissingRequiredArgumentValue,
986
- P as MissingRequiredOptionValue
1028
+ V as MissingRequiredOptionValue,
1029
+ H as StringSimilarity
987
1030
  };
@@ -1,4 +1,4 @@
1
- const t = "bob-core", e = "2.0.0-beta.14", s = "BOB Core", i = "module", m = !1, n = ["dist/**"], r = { ".": { import: "./dist/esm/index.js", require: "./dist/cjs/index.js" } }, o = { "*": { "*": ["./dist/cjs/*.d.ts"] } }, c = { start: "node -r @swc-node/register debug/main.ts", build: "rimraf ./dist && vite build", typecheck: "tsc --noEmit", prepack: "npm run build", test: "vitest run", lint: "eslint .", "lint:fix": "eslint . --fix" }, p = "Léo Hubert", d = "ISC", l = { "@eslint/js": "^9.37.0", "@faker-js/faker": "^10.0.0", "@swc-node/register": "^1.11.1", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/minimist": "^1.2.5", "@types/node": "^20.14.5", "@types/prompts": "^2.4.9", "@types/string-similarity": "^4.0.2", "@vitest/coverage-v8": "^3.2.4", eslint: "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", prettier: "^3.6.2", rimraf: "^6.0.1", tsx: "^4.20.6", typescript: "^5.7.3", "typescript-eslint": "^8.46.0", vite: "^7.1.6", "vite-plugin-dts": "^4.5.4", vitest: "^3.2.4" }, a = { chalk: "^5.6.2", minimist: "^1.2.8", prompts: "^2.4.2", "string-similarity": "^4.0.4" }, f = {
1
+ const t = "bob-core", e = "2.0.0-beta.15", s = "BOB Core", i = "module", m = !1, n = ["dist/**"], r = { ".": { import: "./dist/esm/index.js", require: "./dist/cjs/index.js" } }, o = { "*": { "*": ["./dist/cjs/*.d.ts"] } }, c = { start: "node -r @swc-node/register debug/main.ts", build: "rimraf ./dist && vite build", typecheck: "tsc --noEmit", prepack: "npm run build", test: "vitest run", lint: "eslint .", "lint:fix": "eslint . --fix" }, p = "Léo Hubert", d = "ISC", l = { "@eslint/js": "^9.37.0", "@faker-js/faker": "^10.0.0", "@swc-node/register": "^1.11.1", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/minimist": "^1.2.5", "@types/node": "^20.14.5", "@types/prompts": "^2.4.9", "@types/string-similarity": "^4.0.2", "@vitest/coverage-v8": "^3.2.4", eslint: "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", prettier: "^3.6.2", rimraf: "^6.0.1", tsx: "^4.20.6", typescript: "^5.7.3", "typescript-eslint": "^8.46.0", vite: "^7.1.6", "vite-plugin-dts": "^4.5.4", vitest: "^3.2.4" }, a = { chalk: "^5.6.2", minimist: "^1.2.8", prompts: "^2.4.2" }, f = {
2
2
  name: t,
3
3
  version: e,
4
4
  description: s,
@@ -1,5 +1,5 @@
1
1
  import { Command } from './Command.js';
2
- import { CommandRegistry, CommandResolver, FileImporter } from './CommandRegistry.js';
2
+ import { CommandRegistry, CommandRegistryOptions, CommandResolver, FileImporter } from './CommandRegistry.js';
3
3
  import { ExceptionHandler } from './ExceptionHandler.js';
4
4
  import { Logger } from './Logger.js';
5
5
  import { default as HelpCommand, HelpCommandOptions } from './commands/HelpCommand.js';
@@ -16,9 +16,7 @@ export declare class Cli<C extends ContextDefinition = ContextDefinition> {
16
16
  readonly commandRegistry: CommandRegistry;
17
17
  private readonly exceptionHandler;
18
18
  private readonly helpCommand;
19
- protected newCommandRegistry(opts: {
20
- logger: Logger;
21
- }): CommandRegistry;
19
+ protected newCommandRegistry(opts: CommandRegistryOptions): CommandRegistry;
22
20
  protected newHelpCommand(opts: HelpCommandOptions): HelpCommand;
23
21
  protected newExceptionHandler(opts: {
24
22
  logger: Logger;
@@ -1,4 +1,4 @@
1
- import { CommandIO } from './CommandIO.js';
1
+ import { CommandIO, CommandIOOptions } from './CommandIO.js';
2
2
  import { CommandParser } from './CommandParser.js';
3
3
  import { Logger } from './Logger.js';
4
4
  import { CommandOption } from './contracts/index.js';
@@ -43,9 +43,7 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
43
43
  options: Options;
44
44
  arguments: Arguments;
45
45
  }): CommandParser<Options, Arguments>;
46
- protected newCommandIO(opts: {
47
- logger: Logger;
48
- }): CommandIO;
46
+ protected newCommandIO(opts: CommandIOOptions): CommandIO;
49
47
  constructor(command: string, opts?: {
50
48
  description?: string;
51
49
  group?: string;
@@ -6,9 +6,12 @@ export type SelectOption = {
6
6
  selected?: boolean | undefined;
7
7
  description?: string | undefined;
8
8
  };
9
+ export type CommandIOOptions = {
10
+ logger: Logger;
11
+ };
9
12
  export declare class CommandIO {
10
13
  private logger;
11
- constructor(logger: Logger);
14
+ constructor(opts: CommandIOOptions);
12
15
  /**
13
16
  * Logger methods
14
17
  */
@@ -1,15 +1,21 @@
1
1
  import { Command } from './Command.js';
2
- import { CommandIO } from './CommandIO.js';
2
+ import { CommandIO, CommandIOOptions } from './CommandIO.js';
3
3
  import { Logger } from './Logger.js';
4
+ import { StringSimilarity } from './StringSimilarity.js';
4
5
  import { ArgumentsSchema, ContextDefinition, OptionsSchema } from './lib/types.js';
5
6
  export type CommandResolver = (path: string) => Promise<Command | null>;
6
7
  export type FileImporter = (filePath: string) => Promise<unknown>;
8
+ export type CommandRegistryOptions = {
9
+ logger?: Logger;
10
+ stringSimilarity?: StringSimilarity;
11
+ };
7
12
  export declare class CommandRegistry {
8
13
  private readonly commands;
9
14
  protected readonly io: CommandIO;
10
15
  protected readonly logger: Logger;
11
- protected get CommandIOClass(): typeof CommandIO;
12
- constructor(logger?: Logger);
16
+ private readonly stringSimilarity;
17
+ protected newCommandIO(opts: CommandIOOptions): CommandIO;
18
+ constructor(opts?: CommandRegistryOptions);
13
19
  getAvailableCommands(): string[];
14
20
  getCommands(): Array<Command>;
15
21
  private importFile;
@@ -0,0 +1,26 @@
1
+ export interface SimilarityResult {
2
+ rating: number;
3
+ target: string;
4
+ }
5
+ export interface BestMatchResult {
6
+ bestMatch: SimilarityResult;
7
+ bestMatchIndex: number;
8
+ ratings: SimilarityResult[];
9
+ }
10
+ /**
11
+ * String similarity calculator using Dice's Coefficient algorithm
12
+ */
13
+ export declare class StringSimilarity {
14
+ /**
15
+ * Generate bigrams (character pairs) from a string
16
+ */
17
+ private getBigrams;
18
+ /**
19
+ * Calculate Dice's Coefficient similarity between two strings (0-1 scale)
20
+ */
21
+ calculateSimilarity(str1: string, str2: string): number;
22
+ /**
23
+ * Find best matching string and ratings for all candidates
24
+ */
25
+ findBestMatch(target: string, candidates: string[]): BestMatchResult;
26
+ }
@@ -7,6 +7,7 @@ export * from './CommandRegistry.js';
7
7
  export * from './Cli.js';
8
8
  export * from './Logger.js';
9
9
  export * from './ExceptionHandler.js';
10
+ export * from './StringSimilarity.js';
10
11
  export * from './lib/types.js';
11
12
  export * from './errors/index.js';
12
13
  export * from './options/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bob-core",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.15",
4
4
  "description": "BOB Core",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -56,7 +56,6 @@
56
56
  "dependencies": {
57
57
  "chalk": "^5.6.2",
58
58
  "minimist": "^1.2.8",
59
- "prompts": "^2.4.2",
60
- "string-similarity": "^4.0.4"
59
+ "prompts": "^2.4.2"
61
60
  }
62
61
  }