@pentoshi/clai 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +287 -0
  2. package/bin/clai.mjs +2 -0
  3. package/dist/agent/runner.d.ts +12 -0
  4. package/dist/agent/runner.js +249 -0
  5. package/dist/agent/runner.js.map +1 -0
  6. package/dist/commands/doctor.d.ts +1 -0
  7. package/dist/commands/doctor.js +29 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/providers.d.ts +13 -0
  10. package/dist/commands/providers.js +137 -0
  11. package/dist/commands/providers.js.map +1 -0
  12. package/dist/commands/update.d.ts +5 -0
  13. package/dist/commands/update.js +123 -0
  14. package/dist/commands/update.js.map +1 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +172 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/llm/anthropic.d.ts +2 -0
  19. package/dist/llm/anthropic.js +127 -0
  20. package/dist/llm/anthropic.js.map +1 -0
  21. package/dist/llm/gemini.d.ts +2 -0
  22. package/dist/llm/gemini.js +109 -0
  23. package/dist/llm/gemini.js.map +1 -0
  24. package/dist/llm/groq.d.ts +2 -0
  25. package/dist/llm/groq.js +49 -0
  26. package/dist/llm/groq.js.map +1 -0
  27. package/dist/llm/http.d.ts +35 -0
  28. package/dist/llm/http.js +112 -0
  29. package/dist/llm/http.js.map +1 -0
  30. package/dist/llm/ollama.d.ts +2 -0
  31. package/dist/llm/ollama.js +95 -0
  32. package/dist/llm/ollama.js.map +1 -0
  33. package/dist/llm/openai.d.ts +2 -0
  34. package/dist/llm/openai.js +49 -0
  35. package/dist/llm/openai.js.map +1 -0
  36. package/dist/llm/openrouter.d.ts +2 -0
  37. package/dist/llm/openrouter.js +55 -0
  38. package/dist/llm/openrouter.js.map +1 -0
  39. package/dist/llm/provider.d.ts +23 -0
  40. package/dist/llm/provider.js +58 -0
  41. package/dist/llm/provider.js.map +1 -0
  42. package/dist/llm/router.d.ts +8 -0
  43. package/dist/llm/router.js +103 -0
  44. package/dist/llm/router.js.map +1 -0
  45. package/dist/modes/agent.d.ts +17 -0
  46. package/dist/modes/agent.js +6 -0
  47. package/dist/modes/agent.js.map +1 -0
  48. package/dist/modes/ask.d.ts +8 -0
  49. package/dist/modes/ask.js +46 -0
  50. package/dist/modes/ask.js.map +1 -0
  51. package/dist/os/detect.d.ts +10 -0
  52. package/dist/os/detect.js +17 -0
  53. package/dist/os/detect.js.map +1 -0
  54. package/dist/os/pkgmgr.d.ts +6 -0
  55. package/dist/os/pkgmgr.js +32 -0
  56. package/dist/os/pkgmgr.js.map +1 -0
  57. package/dist/prompts/index.d.ts +2 -0
  58. package/dist/prompts/index.js +60 -0
  59. package/dist/prompts/index.js.map +1 -0
  60. package/dist/repl.d.ts +7 -0
  61. package/dist/repl.js +216 -0
  62. package/dist/repl.js.map +1 -0
  63. package/dist/safety/classifier.d.ts +8 -0
  64. package/dist/safety/classifier.js +118 -0
  65. package/dist/safety/classifier.js.map +1 -0
  66. package/dist/safety/patterns.d.ts +5 -0
  67. package/dist/safety/patterns.js +45 -0
  68. package/dist/safety/patterns.js.map +1 -0
  69. package/dist/store/config.d.ts +20 -0
  70. package/dist/store/config.js +46 -0
  71. package/dist/store/config.js.map +1 -0
  72. package/dist/store/history.d.ts +24 -0
  73. package/dist/store/history.js +145 -0
  74. package/dist/store/history.js.map +1 -0
  75. package/dist/store/keys.d.ts +10 -0
  76. package/dist/store/keys.js +115 -0
  77. package/dist/store/keys.js.map +1 -0
  78. package/dist/store/logs.d.ts +2 -0
  79. package/dist/store/logs.js +31 -0
  80. package/dist/store/logs.js.map +1 -0
  81. package/dist/store/project.d.ts +2 -0
  82. package/dist/store/project.js +14 -0
  83. package/dist/store/project.js.map +1 -0
  84. package/dist/tools/fs.d.ts +5 -0
  85. package/dist/tools/fs.js +82 -0
  86. package/dist/tools/fs.js.map +1 -0
  87. package/dist/tools/http.d.ts +6 -0
  88. package/dist/tools/http.js +14 -0
  89. package/dist/tools/http.js.map +1 -0
  90. package/dist/tools/registry.d.ts +5 -0
  91. package/dist/tools/registry.js +79 -0
  92. package/dist/tools/registry.js.map +1 -0
  93. package/dist/tools/shell.d.ts +7 -0
  94. package/dist/tools/shell.js +16 -0
  95. package/dist/tools/shell.js.map +1 -0
  96. package/dist/types.d.ts +40 -0
  97. package/dist/types.js +9 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/ui/banner.d.ts +12 -0
  100. package/dist/ui/banner.js +55 -0
  101. package/dist/ui/banner.js.map +1 -0
  102. package/package.json +66 -0
@@ -0,0 +1,82 @@
1
+ import { readFile, readdir, writeFile } from "node:fs/promises";
2
+ import { relative, resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { execa } from "execa";
5
+ import { getConfig } from "../store/config.js";
6
+ function expandHome(path) {
7
+ if (path === "~")
8
+ return homedir();
9
+ if (path.startsWith("~/") || path.startsWith("~\\")) {
10
+ return resolve(homedir(), path.slice(2));
11
+ }
12
+ return path;
13
+ }
14
+ /** Resolve path with tilde expansion — no sandbox check (read-only callers) */
15
+ function resolvePath(path) {
16
+ return resolve(expandHome(path));
17
+ }
18
+ /** Resolve + sandbox check for write operations only */
19
+ function ensureWriteAllowed(path) {
20
+ const resolved = resolvePath(path);
21
+ const roots = [
22
+ ...getConfig().sandboxRoots.map((root) => resolve(expandHome(root))),
23
+ resolve(homedir()),
24
+ ];
25
+ const allowed = roots.some((root) => {
26
+ const rel = relative(root, resolved);
27
+ return (rel === "" || (!rel.startsWith("..") && !resolve(rel).startsWith("..")));
28
+ });
29
+ if (!allowed) {
30
+ throw new Error(`Write blocked — path is outside approved roots: ${path}`);
31
+ }
32
+ return resolved;
33
+ }
34
+ export async function fsRead(path) {
35
+ const resolved = resolvePath(path);
36
+ const content = await readFile(resolved, "utf8");
37
+ return { ok: true, output: content };
38
+ }
39
+ export async function fsWrite(path, content) {
40
+ const resolved = ensureWriteAllowed(path);
41
+ await writeFile(resolved, content, "utf8");
42
+ return { ok: true, output: `Wrote ${resolved}` };
43
+ }
44
+ export async function fsList(path) {
45
+ const resolved = resolvePath(path);
46
+ const entries = await readdir(resolved, { withFileTypes: true });
47
+ return {
48
+ ok: true,
49
+ output: entries
50
+ .map((entry) => `${entry.isDirectory() ? "dir " : "file"} ${entry.name}`)
51
+ .join("\n"),
52
+ };
53
+ }
54
+ export async function fsSearch(pattern, path = process.cwd()) {
55
+ const resolved = resolvePath(path);
56
+ const maxLines = 50;
57
+ try {
58
+ const result = await execa("rg", ["--max-count", "5", "--max-filesize", "1M", "-l", pattern, resolved], {
59
+ reject: false,
60
+ all: true,
61
+ timeout: 15_000,
62
+ });
63
+ return {
64
+ ok: result.exitCode === 0,
65
+ output: result.all ?? "",
66
+ exitCode: result.exitCode,
67
+ };
68
+ }
69
+ catch {
70
+ const result = await execa("grep", ["-R", "-l", "-m", String(maxLines), pattern, resolved], {
71
+ reject: false,
72
+ all: true,
73
+ timeout: 15_000,
74
+ });
75
+ return {
76
+ ok: result.exitCode === 0,
77
+ output: result.all ?? "",
78
+ exitCode: result.exitCode,
79
+ };
80
+ }
81
+ }
82
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/tools/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ToolResult } from "../types.js";
2
+ export declare function httpFetch(url: string, options?: {
3
+ method?: string | undefined;
4
+ body?: string | undefined;
5
+ maxBytes?: number | undefined;
6
+ }): Promise<ToolResult>;
@@ -0,0 +1,14 @@
1
+ export async function httpFetch(url, options = {}) {
2
+ const response = await fetch(url, {
3
+ method: options.method ?? "GET",
4
+ body: options.body ?? null,
5
+ });
6
+ const limit = options.maxBytes ?? 1_000_000;
7
+ const text = await response.text();
8
+ return {
9
+ ok: response.ok,
10
+ output: text.slice(0, limit),
11
+ exitCode: response.status,
12
+ };
13
+ }
14
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/tools/http.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,UAII,EAAE;IAEN,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;QAC/B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;KAC3B,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;QAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM;KAC1B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ToolCall, ToolResult } from '../types.js';
2
+ export type ToolHandler = (args: Record<string, unknown>) => Promise<ToolResult>;
3
+ export declare const toolRegistry: Record<string, ToolHandler>;
4
+ export declare function availableToolNames(): string[];
5
+ export declare function runToolCall(call: ToolCall): Promise<ToolResult>;
@@ -0,0 +1,79 @@
1
+ import { detectSystem } from '../os/detect.js';
2
+ import { detectPackageManager } from '../os/pkgmgr.js';
3
+ import { fsList, fsRead, fsSearch, fsWrite } from './fs.js';
4
+ import { httpFetch } from './http.js';
5
+ import { shellExec } from './shell.js';
6
+ function requireString(args, key) {
7
+ const value = args[key];
8
+ if (typeof value !== 'string' || value.length === 0) {
9
+ throw new Error(`Tool argument "${key}" must be a non-empty string`);
10
+ }
11
+ return value;
12
+ }
13
+ function optionalString(args, key) {
14
+ const value = args[key];
15
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
16
+ }
17
+ function optionalNumber(args, key) {
18
+ const value = args[key];
19
+ return typeof value === 'number' ? value : undefined;
20
+ }
21
+ export const toolRegistry = {
22
+ async 'shell.exec'(args) {
23
+ return shellExec({ command: requireString(args, 'command'), cwd: optionalString(args, 'cwd'), timeoutMs: optionalNumber(args, 'timeoutMs') });
24
+ },
25
+ async 'fs.read'(args) {
26
+ return fsRead(requireString(args, 'path'));
27
+ },
28
+ async 'fs.write'(args) {
29
+ return fsWrite(requireString(args, 'path'), requireString(args, 'content'));
30
+ },
31
+ async 'fs.list'(args) {
32
+ return fsList(optionalString(args, 'path') ?? process.cwd());
33
+ },
34
+ async 'fs.search'(args) {
35
+ return fsSearch(requireString(args, 'pattern'), optionalString(args, 'path'));
36
+ },
37
+ async 'pkg.install'(args) {
38
+ const tool = requireString(args, 'tool');
39
+ const pkgmgr = await detectPackageManager();
40
+ return shellExec({ command: pkgmgr.installCommand(tool) });
41
+ },
42
+ async 'net.scan'(args) {
43
+ const target = requireString(args, 'target');
44
+ const ports = optionalString(args, 'ports');
45
+ const command = ports ? `nmap -p ${ports} ${target}` : `nmap ${target}`;
46
+ return shellExec({ command, timeoutMs: 120_000 });
47
+ },
48
+ async 'http.fetch'(args) {
49
+ return httpFetch(requireString(args, 'url'), {
50
+ method: optionalString(args, 'method'),
51
+ body: optionalString(args, 'body'),
52
+ maxBytes: optionalNumber(args, 'maxBytes'),
53
+ });
54
+ },
55
+ async sysinfo() {
56
+ return { ok: true, output: JSON.stringify(detectSystem(), null, 2) };
57
+ },
58
+ async 'pentest.recon'(args) {
59
+ const target = requireString(args, 'target');
60
+ const commands = [`whois ${target}`, `dig ${target}`, `nmap --top-ports 100 ${target}`];
61
+ const outputs = [];
62
+ for (const command of commands) {
63
+ const result = await shellExec({ command, timeoutMs: 120_000 });
64
+ outputs.push(`$ ${command}\n${result.output}`);
65
+ }
66
+ return { ok: true, output: outputs.join('\n\n') };
67
+ },
68
+ };
69
+ export function availableToolNames() {
70
+ return Object.keys(toolRegistry);
71
+ }
72
+ export async function runToolCall(call) {
73
+ const handler = toolRegistry[call.name];
74
+ if (!handler) {
75
+ throw new Error(`Unknown tool: ${call.name}`);
76
+ }
77
+ return handler(call.args);
78
+ }
79
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ToolResult } from "../types.js";
2
+ export interface ShellExecArgs {
3
+ command: string;
4
+ cwd?: string | undefined;
5
+ timeoutMs?: number | undefined;
6
+ }
7
+ export declare function shellExec(args: ShellExecArgs): Promise<ToolResult>;
@@ -0,0 +1,16 @@
1
+ import { execaCommand } from "execa";
2
+ export async function shellExec(args) {
3
+ const subprocess = await execaCommand(args.command, {
4
+ cwd: args.cwd ?? process.cwd(),
5
+ timeout: args.timeoutMs ?? 120_000,
6
+ reject: false,
7
+ all: true,
8
+ shell: true,
9
+ });
10
+ return {
11
+ ok: subprocess.exitCode === 0,
12
+ output: subprocess.all ?? `${subprocess.stdout}\n${subprocess.stderr}`.trim(),
13
+ exitCode: subprocess.exitCode,
14
+ };
15
+ }
16
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AASrC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAmB;IACjD,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE;QAClD,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QAC9B,OAAO,EAAE,IAAI,CAAC,SAAS,IAAI,OAAO;QAClC,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IACH,OAAO;QACL,EAAE,EAAE,UAAU,CAAC,QAAQ,KAAK,CAAC;QAC7B,MAAM,EACJ,UAAU,CAAC,GAAG,IAAI,GAAG,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;QACvE,QAAQ,EAAE,UAAU,CAAC,QAAQ;KAC9B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ export declare const providerIds: readonly ["groq", "gemini", "openrouter", "openai", "anthropic", "ollama"];
2
+ export type ProviderId = (typeof providerIds)[number];
3
+ export type Mode = "ask" | "agent";
4
+ export type RiskLevel = "safe" | "confirm" | "block";
5
+ export interface ChatMessage {
6
+ role: "system" | "user" | "assistant" | "tool";
7
+ content: string;
8
+ }
9
+ export interface CompletionRequest {
10
+ provider?: ProviderId | undefined;
11
+ model?: string | undefined;
12
+ messages: ChatMessage[];
13
+ temperature?: number | undefined;
14
+ maxTokens?: number | undefined;
15
+ signal?: AbortSignal | undefined;
16
+ }
17
+ export interface CompletionResult {
18
+ text: string;
19
+ provider: ProviderId;
20
+ model: string;
21
+ }
22
+ export interface ProviderStatus {
23
+ provider: ProviderId;
24
+ label: string;
25
+ active: boolean;
26
+ configured: boolean;
27
+ source: "env" | "keychain" | "fallback" | "local" | "missing";
28
+ maskedKey?: string | undefined;
29
+ model: string;
30
+ note?: string | undefined;
31
+ }
32
+ export interface ToolCall {
33
+ name: string;
34
+ args: Record<string, unknown>;
35
+ }
36
+ export interface ToolResult {
37
+ ok: boolean;
38
+ output: string;
39
+ exitCode?: number | undefined;
40
+ }
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ export const providerIds = [
2
+ "groq",
3
+ "gemini",
4
+ "openrouter",
5
+ "openai",
6
+ "anthropic",
7
+ "ollama",
8
+ ];
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,QAAQ;CACA,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare function renderBanner(version: string): string;
2
+ export declare function renderSessionInfo(opts: {
3
+ workdir: string;
4
+ model: string;
5
+ provider: string;
6
+ mode: string;
7
+ }): string;
8
+ export declare function renderSuggestions(): string;
9
+ export declare function renderModeSwitch(mode: string): string;
10
+ export declare function renderProviderSwitch(provider: string, model: string): string;
11
+ export declare const PROMPT: string;
12
+ export declare const PROMPT_SECONDARY: string;
@@ -0,0 +1,55 @@
1
+ import chalk from "chalk";
2
+ import { homedir } from "node:os";
3
+ // ── Box drawing helpers ─────────────────────────────────────────────────────
4
+ function stripAnsi(str) {
5
+ // biome-ignore lint: escape sequences are intentional
6
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
7
+ }
8
+ function box(lines, opts = {}) {
9
+ const color = opts.color ?? chalk.gray;
10
+ const contentWidth = Math.max(opts.minWidth ?? 60, ...lines.map((l) => stripAnsi(l).length));
11
+ const top = color(`╭${"─".repeat(contentWidth + 2)}╮`);
12
+ const bottom = color(`╰${"─".repeat(contentWidth + 2)}╯`);
13
+ const padded = lines.map((l) => {
14
+ const pad = contentWidth - stripAnsi(l).length;
15
+ return `${color("│")} ${l}${" ".repeat(Math.max(0, pad))} ${color("│")}`;
16
+ });
17
+ return [top, ...padded, bottom].join("\n");
18
+ }
19
+ // ── Public rendering functions ──────────────────────────────────────────────
20
+ export function renderBanner(version) {
21
+ return box([`${chalk.magenta("●")} ${chalk.bold.white("clai")} ${chalk.dim(`v${version}`)}`], { minWidth: 58 });
22
+ }
23
+ export function renderSessionInfo(opts) {
24
+ const home = homedir();
25
+ const workdir = opts.workdir.startsWith(home)
26
+ ? `~${opts.workdir.slice(home.length)}`
27
+ : opts.workdir;
28
+ return box([
29
+ `${chalk.dim("↳ workdir:")} ${workdir}`,
30
+ `${chalk.dim("↳ model:")} ${chalk.cyan(opts.model)}`,
31
+ `${chalk.dim("↳ provider:")} ${chalk.green(opts.provider)}`,
32
+ `${chalk.dim("↳ mode:")} ${chalk.yellow(opts.mode)}`,
33
+ ], { minWidth: 58 });
34
+ }
35
+ export function renderSuggestions() {
36
+ const suggestions = [
37
+ "scan my network",
38
+ "find open ports on 192.168.1.1",
39
+ "what is my IP?",
40
+ "read my .zshrc",
41
+ ];
42
+ return chalk.dim(" try: ") + chalk.dim.italic(suggestions.join(" │ "));
43
+ }
44
+ export function renderModeSwitch(mode) {
45
+ return box([`${chalk.dim("mode →")} ${chalk.yellow(mode)}`], { minWidth: 30 });
46
+ }
47
+ export function renderProviderSwitch(provider, model) {
48
+ return box([
49
+ `${chalk.dim("provider →")} ${chalk.green(provider)}`,
50
+ `${chalk.dim("model →")} ${chalk.cyan(model)}`,
51
+ ], { minWidth: 30 });
52
+ }
53
+ export const PROMPT = `${chalk.magenta("❯")} `;
54
+ export const PROMPT_SECONDARY = `${chalk.gray("…")} `;
55
+ //# sourceMappingURL=banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@pentoshi/clai",
3
+ "version": "0.2.0",
4
+ "description": "A fast, cross-platform AI CLI assistant with ask and agent modes for shell tasks, file operations, and cybersecurity workflows.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "pentoshi007",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/pentoshi007/clai.git"
11
+ },
12
+ "homepage": "https://github.com/pentoshi007/clai",
13
+ "keywords": [
14
+ "cli",
15
+ "ai",
16
+ "agent",
17
+ "terminal",
18
+ "cybersecurity",
19
+ "pentesting",
20
+ "nmap",
21
+ "llm",
22
+ "groq",
23
+ "gemini",
24
+ "ollama"
25
+ ],
26
+ "bin": {
27
+ "clai": "bin/clai.mjs"
28
+ },
29
+ "files": [
30
+ "bin",
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "scripts": {
35
+ "dev": "tsx src/index.ts",
36
+ "start": "node dist/index.js",
37
+ "build": "tsc -p tsconfig.json",
38
+ "prepublishOnly": "npm run build",
39
+ "compile": "bun run scripts/build.ts",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "test": "vitest run",
42
+ "doctor": "tsx src/index.ts doctor"
43
+ },
44
+ "dependencies": {
45
+ "@inquirer/prompts": "^7.2.3",
46
+ "chalk": "^5.4.1",
47
+ "commander": "^12.1.0",
48
+ "conf": "^13.1.0",
49
+ "execa": "^9.5.2",
50
+ "ora": "^8.1.1",
51
+ "zod": "^3.24.1"
52
+ },
53
+ "optionalDependencies": {
54
+ "better-sqlite3": "^11.7.0",
55
+ "keytar": "^7.9.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^22.10.2",
59
+ "tsx": "^4.19.2",
60
+ "typescript": "^5.7.2",
61
+ "vitest": "^2.1.8"
62
+ },
63
+ "engines": {
64
+ "node": ">=20"
65
+ }
66
+ }