@duckmind/deepquark-darwin-arm64 0.9.83 → 0.9.88
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/.deepquark/skills/bundled/knowledge-graph/SKILL.md +385 -0
- package/.deepquark/skills/bundled/knowledge-graph/STANDARDS.md +461 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/cli.ts +588 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/config.ts +630 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/connection-profile.ts +629 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/container.ts +756 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/mcp-client.ts +1310 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/output-formatter.ts +997 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/token-metrics.ts +335 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/transformation-log.ts +137 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/wrapper-config.ts +113 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/.env.example +129 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/compare-embeddings.ts +175 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-falkordb.yaml +108 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-neo4j.yaml +111 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/diagnose.ts +483 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb-dev.yml +146 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb.yml +151 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev-local.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j.yml +169 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-production.yml +128 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-test.yml +10 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose.yml +84 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/entrypoint.sh +40 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/install.ts +2054 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-falkordb.yml +78 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-neo4j.yml +88 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose.yml +83 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-all-llms-mcp.ts +387 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-models.ts +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-providers.ts +641 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-graphiti-model.ts +217 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-correct.ts +141 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-llms-mcp.ts +386 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-models.ts +173 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-llama-extraction.ts +188 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-final.ts +240 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-live.ts +187 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-session.ts +127 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-model-combinations.ts +316 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-ollama-models.ts +228 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-openrouter-models.ts +460 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-real-life-mcp.ts +311 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-search-debug.ts +199 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/Install.md +104 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/README.md +120 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/knowledge-cli.ts +996 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/server-cli.ts +531 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/BulkImport.md +514 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/CaptureEpisode.md +242 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/ClearGraph.md +392 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetRecent.md +352 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetStatus.md +373 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/HealthReport.md +212 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/InvestigateEntity.md +142 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/OntologyManagement.md +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/RunMaintenance.md +302 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchByDate.md +255 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchFacts.md +382 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchKnowledge.md +374 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/StixImport.md +212 -0
- package/bin/deepquark +0 -0
- package/package.json +1 -1
- package/.deepquark/skills/bundled/ge-payroll/SKILL.md +0 -153
- package/.deepquark/skills/bundled/ge-payroll/evals/evals.json +0 -23
- package/.deepquark/skills/bundled/ge-payroll/references/pain-points-improvements.md +0 -106
- package/.deepquark/skills/bundled/ge-payroll/references/process-detail.md +0 -217
- package/.deepquark/skills/bundled/ge-payroll/references/raci-stakeholders.md +0 -85
- package/.deepquark/skills/bundled/ge-payroll/references/timeline-mandays.md +0 -64
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Utilities Library
|
|
3
|
+
*
|
|
4
|
+
* Provides colored output, interactive prompts, and formatting helpers
|
|
5
|
+
* for consistent command-line interface across all scripts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import type inquirer from 'inquirer';
|
|
10
|
+
|
|
11
|
+
// Prompt types
|
|
12
|
+
export type PromptChoice = string;
|
|
13
|
+
export type PromptChoices = PromptChoice[];
|
|
14
|
+
|
|
15
|
+
// Checkbox choice
|
|
16
|
+
export interface CheckboxChoice {
|
|
17
|
+
name: string;
|
|
18
|
+
value: string;
|
|
19
|
+
checked?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Select choice
|
|
23
|
+
export interface SelectChoice {
|
|
24
|
+
name: string;
|
|
25
|
+
value: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* CLI Output class for consistent formatting
|
|
30
|
+
*/
|
|
31
|
+
export class CLIOutput {
|
|
32
|
+
/**
|
|
33
|
+
* Print a header/section title
|
|
34
|
+
*/
|
|
35
|
+
static header(title: string, width = 50): void {
|
|
36
|
+
const line = '═'.repeat(width);
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(chalk.blue(line));
|
|
39
|
+
console.log(chalk.blue.bold(title));
|
|
40
|
+
console.log(chalk.blue(line));
|
|
41
|
+
console.log();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Print a step/section header
|
|
46
|
+
*/
|
|
47
|
+
static step(text: string): void {
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(chalk.green.bold(`▶ ${text}`));
|
|
50
|
+
console.log();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Print a sub-step
|
|
55
|
+
*/
|
|
56
|
+
static substep(text: string): void {
|
|
57
|
+
console.log(chalk.cyan(` → ${text}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Print a success message
|
|
62
|
+
*/
|
|
63
|
+
static success(text: string): void {
|
|
64
|
+
console.log(`${chalk.green.bold('✓')} ${text}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Print an error message
|
|
69
|
+
*/
|
|
70
|
+
static error(text: string): void {
|
|
71
|
+
console.error(`${chalk.red.bold('✗')} ${text}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Print a warning message
|
|
76
|
+
*/
|
|
77
|
+
static warning(text: string): void {
|
|
78
|
+
console.log(`${chalk.yellow.bold('⚠')} ${text}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Print an info message
|
|
83
|
+
*/
|
|
84
|
+
static info(text: string): void {
|
|
85
|
+
console.log(chalk.gray(text));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Print a dimmed/subtle message
|
|
90
|
+
*/
|
|
91
|
+
static dim(text: string): void {
|
|
92
|
+
console.log(chalk.dim(text));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Print a key-value pair
|
|
97
|
+
*/
|
|
98
|
+
static kv(key: string, value: string): void {
|
|
99
|
+
console.log(`${chalk.cyan(key)}: ${chalk.white(value)}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Print a blank line
|
|
104
|
+
*/
|
|
105
|
+
static blank(): void {
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Print a separator line
|
|
111
|
+
*/
|
|
112
|
+
static separator(char = '─', width = 50): void {
|
|
113
|
+
console.log(chalk.gray(char.repeat(width)));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Print a bulleted list
|
|
118
|
+
*/
|
|
119
|
+
static list(items: string[], indent = 0): void {
|
|
120
|
+
const prefix = ' '.repeat(indent);
|
|
121
|
+
items.forEach((item) => {
|
|
122
|
+
console.log(`${prefix}${chalk.gray('•')} ${item}`);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Print a numbered list
|
|
128
|
+
*/
|
|
129
|
+
static numberedList(items: string[], start = 1): void {
|
|
130
|
+
items.forEach((item, index) => {
|
|
131
|
+
const num = chalk.cyan(`${start + index})`);
|
|
132
|
+
console.log(` ${num} ${item}`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Print a table (simple 2-column)
|
|
138
|
+
*/
|
|
139
|
+
static table(rows: [string, string][]): void {
|
|
140
|
+
const maxKeyLength = Math.max(...rows.map(([key]) => key.length));
|
|
141
|
+
rows.forEach(([key, value]) => {
|
|
142
|
+
const paddedKey = key.padEnd(maxKeyLength);
|
|
143
|
+
console.log(` ${chalk.cyan(paddedKey)} ${chalk.white(value)}`);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Print a code block
|
|
149
|
+
*/
|
|
150
|
+
static code(text: string, language = ''): void {
|
|
151
|
+
console.log();
|
|
152
|
+
if (language) {
|
|
153
|
+
console.log(chalk.dim(`${language}:`));
|
|
154
|
+
}
|
|
155
|
+
console.log(chalk.gray(text));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Clear the terminal
|
|
160
|
+
*/
|
|
161
|
+
static clear(): void {
|
|
162
|
+
console.clear();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Print a box
|
|
167
|
+
*/
|
|
168
|
+
static box(lines: string[], title = ''): void {
|
|
169
|
+
const maxLength = Math.max(...lines.map((line) => line.length), title.length);
|
|
170
|
+
const horizontal = '─'.repeat(maxLength + 2);
|
|
171
|
+
|
|
172
|
+
console.log();
|
|
173
|
+
console.log(chalk.gray(`┌${horizontal}┐`));
|
|
174
|
+
|
|
175
|
+
if (title) {
|
|
176
|
+
const paddedTitle = title.padEnd(maxLength);
|
|
177
|
+
console.log(chalk.gray(`│ ${chalk.white.bold(paddedTitle)} │`));
|
|
178
|
+
console.log(chalk.gray(`├${horizontal}┤`));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
lines.forEach((line) => {
|
|
182
|
+
const paddedLine = line.padEnd(maxLength);
|
|
183
|
+
console.log(chalk.gray(`│ ${chalk.white(paddedLine)} │`));
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log(chalk.gray(`└${horizontal}┘`));
|
|
187
|
+
console.log();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Print a spinner placeholder (for async operations)
|
|
192
|
+
* Note: For actual spinners, use a library like ora or cli-spinners
|
|
193
|
+
*/
|
|
194
|
+
static spinner(text: string): void {
|
|
195
|
+
console.log(chalk.cyan(`⟳ ${text}`));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Print a URL
|
|
200
|
+
*/
|
|
201
|
+
static url(label: string, url: string): void {
|
|
202
|
+
console.log(` ${chalk.cyan(label)}: ${chalk.blue.underline(url)}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Print command hint
|
|
207
|
+
*/
|
|
208
|
+
static hint(command: string): void {
|
|
209
|
+
console.log(chalk.dim(` $ ${command}`));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Print multiple hints
|
|
214
|
+
*/
|
|
215
|
+
static hints(commands: string[]): void {
|
|
216
|
+
commands.forEach((cmd) => CLIOutput.hint(cmd));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Print emoji with text
|
|
221
|
+
*/
|
|
222
|
+
static emoji(emoji: string, text: string): void {
|
|
223
|
+
console.log(`${emoji} ${text}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Print progress (percentage)
|
|
228
|
+
*/
|
|
229
|
+
static progress(current: number, total: number, label = ''): void {
|
|
230
|
+
const percentage = Math.round((current / total) * 100);
|
|
231
|
+
const bar =
|
|
232
|
+
'█'.repeat(Math.floor(percentage / 5)) + '░'.repeat(20 - Math.floor(percentage / 5));
|
|
233
|
+
const text = label ? `${label} ` : '';
|
|
234
|
+
|
|
235
|
+
console.log(`${text}[${chalk.green(bar)}] ${chalk.cyan(percentage)}% (${current}/${total})`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Print JSON output (pretty-printed)
|
|
240
|
+
*/
|
|
241
|
+
static json(data: unknown, pretty = true): void {
|
|
242
|
+
if (pretty) {
|
|
243
|
+
console.log(JSON.stringify(data, null, 2));
|
|
244
|
+
} else {
|
|
245
|
+
console.log(JSON.stringify(data));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Print error details
|
|
251
|
+
*/
|
|
252
|
+
static errorDetails(error: Error | unknown): void {
|
|
253
|
+
if (error instanceof Error) {
|
|
254
|
+
console.error(chalk.red(error.message));
|
|
255
|
+
if (error.stack) {
|
|
256
|
+
console.error(chalk.dim(error.stack));
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
console.error(chalk.red(String(error)));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* CLI Prompts class for interactive input
|
|
266
|
+
*/
|
|
267
|
+
export class CLIPrompts {
|
|
268
|
+
private inquirer: typeof inquirer;
|
|
269
|
+
|
|
270
|
+
constructor(inquirerModule: typeof inquirer) {
|
|
271
|
+
this.inquirer = inquirerModule;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Prompt for text input
|
|
276
|
+
*/
|
|
277
|
+
async input(message: string, defaultValue = ''): Promise<string> {
|
|
278
|
+
const answers = await this.inquirer.prompt<{ value: string }>([
|
|
279
|
+
{
|
|
280
|
+
type: 'input',
|
|
281
|
+
name: 'value',
|
|
282
|
+
message,
|
|
283
|
+
default: defaultValue,
|
|
284
|
+
},
|
|
285
|
+
]);
|
|
286
|
+
return answers.value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Prompt for password (hidden input)
|
|
291
|
+
*/
|
|
292
|
+
async password(message: string): Promise<string> {
|
|
293
|
+
const answers = await this.inquirer.prompt<{ value: string }>([
|
|
294
|
+
{
|
|
295
|
+
type: 'password',
|
|
296
|
+
name: 'value',
|
|
297
|
+
message,
|
|
298
|
+
},
|
|
299
|
+
]);
|
|
300
|
+
return answers.value;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Prompt for confirmation (yes/no)
|
|
305
|
+
*/
|
|
306
|
+
async confirm(message: string, defaultValue = false): Promise<boolean> {
|
|
307
|
+
const answers = await this.inquirer.prompt<{ value: boolean }>([
|
|
308
|
+
{
|
|
309
|
+
type: 'confirm',
|
|
310
|
+
name: 'value',
|
|
311
|
+
message,
|
|
312
|
+
default: defaultValue,
|
|
313
|
+
},
|
|
314
|
+
]);
|
|
315
|
+
return answers.value;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Prompt for single choice selection
|
|
320
|
+
*/
|
|
321
|
+
async select(message: string, choices: SelectChoice[], defaultValue?: string): Promise<string> {
|
|
322
|
+
const answers = await this.inquirer.prompt<{ value: string }>([
|
|
323
|
+
{
|
|
324
|
+
type: 'list',
|
|
325
|
+
name: 'value',
|
|
326
|
+
message,
|
|
327
|
+
choices,
|
|
328
|
+
default: defaultValue,
|
|
329
|
+
},
|
|
330
|
+
]);
|
|
331
|
+
return answers.value;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Prompt for multiple choice selection (checkboxes)
|
|
336
|
+
*/
|
|
337
|
+
async multiselect(message: string, choices: CheckboxChoice[]): Promise<string[]> {
|
|
338
|
+
const answers = await this.inquirer.prompt<{ value: string[] }>([
|
|
339
|
+
{
|
|
340
|
+
type: 'checkbox',
|
|
341
|
+
name: 'value',
|
|
342
|
+
message,
|
|
343
|
+
choices,
|
|
344
|
+
},
|
|
345
|
+
]);
|
|
346
|
+
return answers.value;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Prompt for number input
|
|
351
|
+
*/
|
|
352
|
+
async number(message: string, defaultValue?: number): Promise<number> {
|
|
353
|
+
const answers = await this.inquirer.prompt<{ value: number }>([
|
|
354
|
+
{
|
|
355
|
+
type: 'number',
|
|
356
|
+
name: 'value',
|
|
357
|
+
message,
|
|
358
|
+
default: defaultValue,
|
|
359
|
+
},
|
|
360
|
+
]);
|
|
361
|
+
return answers.value;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Prompt for number selection from list
|
|
366
|
+
*/
|
|
367
|
+
async numberSelect(
|
|
368
|
+
message: string,
|
|
369
|
+
choices: { name: string; value: number }[],
|
|
370
|
+
defaultValue?: number
|
|
371
|
+
): Promise<number> {
|
|
372
|
+
const answers = await this.inquirer.prompt<{ value: number }>([
|
|
373
|
+
{
|
|
374
|
+
type: 'list',
|
|
375
|
+
name: 'value',
|
|
376
|
+
message,
|
|
377
|
+
choices,
|
|
378
|
+
default: defaultValue,
|
|
379
|
+
},
|
|
380
|
+
]);
|
|
381
|
+
return answers.value;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Validation helpers
|
|
387
|
+
*/
|
|
388
|
+
export class Validators {
|
|
389
|
+
/**
|
|
390
|
+
* Validate non-empty string
|
|
391
|
+
*/
|
|
392
|
+
static nonEmpty(value: string): boolean | string {
|
|
393
|
+
if (!value || value.trim().length === 0) {
|
|
394
|
+
return 'This field is required';
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Validate number is positive
|
|
401
|
+
*/
|
|
402
|
+
static positive(value: number): boolean | string {
|
|
403
|
+
if (value <= 0) {
|
|
404
|
+
return 'Must be a positive number';
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Validate number is in range
|
|
411
|
+
*/
|
|
412
|
+
static range(min: number, max: number) {
|
|
413
|
+
return (value: number): boolean | string => {
|
|
414
|
+
if (value < min || value > max) {
|
|
415
|
+
return `Must be between ${min} and ${max}`;
|
|
416
|
+
}
|
|
417
|
+
return true;
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Validate URL format
|
|
423
|
+
*/
|
|
424
|
+
static url(value: string): boolean | string {
|
|
425
|
+
try {
|
|
426
|
+
new URL(value);
|
|
427
|
+
return true;
|
|
428
|
+
} catch {
|
|
429
|
+
return 'Must be a valid URL';
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Validate API key format (basic check)
|
|
435
|
+
*/
|
|
436
|
+
static apiKey(value: string): boolean | string {
|
|
437
|
+
if (!value || value.trim().length === 0) {
|
|
438
|
+
return 'API key is required';
|
|
439
|
+
}
|
|
440
|
+
if (value.length < 10) {
|
|
441
|
+
return 'API key seems too short';
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Formatting helpers
|
|
449
|
+
*/
|
|
450
|
+
export class Formatters {
|
|
451
|
+
/**
|
|
452
|
+
* Format bytes to human-readable size
|
|
453
|
+
*/
|
|
454
|
+
static bytes(bytes: number): string {
|
|
455
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
456
|
+
let size = bytes;
|
|
457
|
+
let unitIndex = 0;
|
|
458
|
+
|
|
459
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
460
|
+
size /= 1024;
|
|
461
|
+
unitIndex++;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Format duration in seconds to human-readable time
|
|
469
|
+
*/
|
|
470
|
+
static duration(seconds: number): string {
|
|
471
|
+
const hours = Math.floor(seconds / 3600);
|
|
472
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
473
|
+
const secs = Math.floor(seconds % 60);
|
|
474
|
+
|
|
475
|
+
const parts = [];
|
|
476
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
477
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
478
|
+
if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
|
|
479
|
+
|
|
480
|
+
return parts.join(' ');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Format date to relative time (e.g., "2 hours ago")
|
|
485
|
+
*/
|
|
486
|
+
static relativeTime(date: Date): string {
|
|
487
|
+
const now = new Date();
|
|
488
|
+
const diff = now.getTime() - date.getTime();
|
|
489
|
+
const seconds = Math.floor(diff / 1000);
|
|
490
|
+
const minutes = Math.floor(seconds / 60);
|
|
491
|
+
const hours = Math.floor(minutes / 60);
|
|
492
|
+
const days = Math.floor(hours / 24);
|
|
493
|
+
|
|
494
|
+
if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago`;
|
|
495
|
+
if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
|
496
|
+
if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
|
497
|
+
return 'just now';
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Format percentage
|
|
502
|
+
*/
|
|
503
|
+
static percentage(value: number, total: number): string {
|
|
504
|
+
const pct = Math.round((value / total) * 100);
|
|
505
|
+
return `${pct}%`;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Pad text to width
|
|
510
|
+
*/
|
|
511
|
+
static pad(text: string, width: number, align: 'left' | 'right' | 'center' = 'left'): string {
|
|
512
|
+
if (text.length >= width) return text;
|
|
513
|
+
|
|
514
|
+
const padding = width - text.length;
|
|
515
|
+
|
|
516
|
+
switch (align) {
|
|
517
|
+
case 'left':
|
|
518
|
+
return text + ' '.repeat(padding);
|
|
519
|
+
case 'right':
|
|
520
|
+
return ' '.repeat(padding) + text;
|
|
521
|
+
case 'center': {
|
|
522
|
+
const left = Math.floor(padding / 2);
|
|
523
|
+
const right = padding - left;
|
|
524
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Truncate text to max length
|
|
531
|
+
*/
|
|
532
|
+
static truncate(text: string, maxLength: number, suffix = '...'): string {
|
|
533
|
+
if (text.length <= maxLength) return text;
|
|
534
|
+
return text.substring(0, maxLength - suffix.length) + suffix;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Mask sensitive text (e.g., API keys)
|
|
539
|
+
*/
|
|
540
|
+
static mask(text: string, visibleChars = 4, maskChar = '•'): string {
|
|
541
|
+
if (text.length <= visibleChars) return text;
|
|
542
|
+
const start = text.substring(0, visibleChars);
|
|
543
|
+
const end = text.substring(text.length - 4);
|
|
544
|
+
const masked = maskChar.repeat(Math.max(0, text.length - visibleChars - 4));
|
|
545
|
+
return `${start}${masked}${end}`;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Export shorthand functions for convenience
|
|
551
|
+
*/
|
|
552
|
+
export const cli = {
|
|
553
|
+
header: CLIOutput.header,
|
|
554
|
+
step: CLIOutput.step,
|
|
555
|
+
substep: CLIOutput.substep,
|
|
556
|
+
success: CLIOutput.success,
|
|
557
|
+
error: CLIOutput.error,
|
|
558
|
+
warning: CLIOutput.warning,
|
|
559
|
+
info: CLIOutput.info,
|
|
560
|
+
dim: CLIOutput.dim,
|
|
561
|
+
blank: CLIOutput.blank,
|
|
562
|
+
separator: CLIOutput.separator,
|
|
563
|
+
clear: CLIOutput.clear,
|
|
564
|
+
url: CLIOutput.url,
|
|
565
|
+
kv: CLIOutput.kv,
|
|
566
|
+
list: CLIOutput.list,
|
|
567
|
+
table: CLIOutput.table,
|
|
568
|
+
hint: CLIOutput.hint,
|
|
569
|
+
hints: CLIOutput.hints,
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
export const fmt = {
|
|
573
|
+
bytes: Formatters.bytes,
|
|
574
|
+
duration: Formatters.duration,
|
|
575
|
+
relativeTime: Formatters.relativeTime,
|
|
576
|
+
percentage: Formatters.percentage,
|
|
577
|
+
pad: Formatters.pad,
|
|
578
|
+
truncate: Formatters.truncate,
|
|
579
|
+
mask: Formatters.mask,
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
export const validate = {
|
|
583
|
+
nonEmpty: Validators.nonEmpty,
|
|
584
|
+
positive: Validators.positive,
|
|
585
|
+
range: Validators.range,
|
|
586
|
+
url: Validators.url,
|
|
587
|
+
apiKey: Validators.apiKey,
|
|
588
|
+
};
|