icopilot 2.2.0 → 2.3.2

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/ui/prompt.js CHANGED
@@ -1,27 +1,186 @@
1
1
  import readline from 'node:readline';
2
- import { theme } from './theme.js';
2
+ import { theme, safeUnicode } from './theme.js';
3
+ import { defaultContext } from '../util/completion.js';
3
4
  import { attachKeybindings, applyKeybindingConfig, } from '../util/keybindings.js';
4
- /** Minimal readline-based prompt (history-enabled, with optional keybindings). */
5
+ // ─── Slash command completer ───────────────────────────────────────────────
6
+ function slashCompleter(line) {
7
+ const ctx = defaultContext();
8
+ if (/^\/[\w-]*$/.test(line)) {
9
+ const partial = line.slice(1).toLowerCase();
10
+ const hits = ctx.slashCommands.filter((cmd) => cmd.startsWith(partial)).map((cmd) => `/${cmd}`);
11
+ return [hits, line];
12
+ }
13
+ const subMatch = line.match(/^(\/[\w-]+)\s+(\S*)$/);
14
+ if (subMatch) {
15
+ const cmd = subMatch[1].slice(1);
16
+ const partial = subMatch[2];
17
+ const subs = ctx.slashSubcommands[cmd] ?? [];
18
+ const hits = subs.filter((s) => s.startsWith(partial)).map((h) => `${subMatch[1]} ${h}`);
19
+ if (hits.length)
20
+ return [hits, line];
21
+ }
22
+ return [[], line];
23
+ }
24
+ // ─── Input box helpers ─────────────────────────────────────────────────────
25
+ const PLACEHOLDER = 'Enter @ to mention files or / for commands...';
26
+ function boxWidth() {
27
+ return Math.max(60, (process.stdout.columns || 80) - 6);
28
+ }
29
+ function drawBoxTop() {
30
+ const w = boxWidth();
31
+ const colorEnabled = theme.dim('') !== ''; // cheap color-enabled check
32
+ const line = colorEnabled ? theme.dim(` ╭${'─'.repeat(w)}╮`) : ` ╭${'─'.repeat(w)}╮`;
33
+ process.stdout.write(line + '\n');
34
+ }
35
+ function drawBoxBottom() {
36
+ const w = boxWidth();
37
+ const line = theme.dim(` ╰${'─'.repeat(w)}╯`);
38
+ process.stdout.write('\n' + line + '\n');
39
+ }
40
+ // ─── Persistent footer (scroll-region docked) ──────────────────────────────
41
+ const FOOTER_KEYS = safeUnicode
42
+ ? ' Ctrl+C Exit │ Tab Autocomplete │ @file Context │ Ctrl+R Clear'
43
+ : ' Ctrl+C Exit | Tab Autocomplete | @file Context | Ctrl+R Clear';
44
+ let footerInstalled = false;
45
+ function footerLine(cols) {
46
+ const text = FOOTER_KEYS;
47
+ // Dim the key names and leave separators brighter
48
+ const formatted = text
49
+ .replace(/Ctrl\+[A-Z]/g, (m) => `\x1b[1m${m}\x1b[0m\x1b[2m`)
50
+ .replace(/Tab/g, '\x1b[1mTab\x1b[0m\x1b[2m')
51
+ .replace(/@file/g, '\x1b[1m@file\x1b[0m\x1b[2m');
52
+ const pad = Math.max(0, cols - text.length);
53
+ return `\x1b[2m${formatted}${' '.repeat(pad)}\x1b[0m`;
54
+ }
55
+ function installFooter() {
56
+ if (!process.stdout.isTTY)
57
+ return;
58
+ const rows = process.stdout.rows ?? 24;
59
+ const cols = process.stdout.columns ?? 80;
60
+ // Reserve bottom 2 rows: separator + hotkey bar
61
+ process.stdout.write(`\x1b[1;${rows - 2}r`); // set scroll region
62
+ process.stdout.write('\x1b7'); // save cursor (DEC)
63
+ process.stdout.write(`\x1b[${rows - 1};1H\x1b[2K`);
64
+ process.stdout.write(theme.dim('─'.repeat(cols)));
65
+ process.stdout.write(`\x1b[${rows};1H\x1b[2K`);
66
+ process.stdout.write(footerLine(cols));
67
+ process.stdout.write('\x1b8'); // restore cursor (DEC)
68
+ footerInstalled = true;
69
+ }
70
+ function removeFooter() {
71
+ if (!footerInstalled)
72
+ return;
73
+ process.stdout.write('\x1b[r'); // reset scroll region to full terminal
74
+ footerInstalled = false;
75
+ }
76
+ // ─── Main factory ──────────────────────────────────────────────────────────
5
77
  export function createPrompt(keybindingMode) {
6
78
  const mode = keybindingMode ?? applyKeybindingConfig();
79
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
80
+ let promptActive = false;
81
+ let activeGhost = '';
82
+ // Install the sticky footer + handle terminal resize
83
+ if (isTTY)
84
+ installFooter();
85
+ const onResize = () => {
86
+ if (isTTY && footerInstalled)
87
+ installFooter();
88
+ };
89
+ process.on('SIGWINCH', onResize);
90
+ // ── Ghost text helpers ────────────────────────────────────────────────
91
+ const clearGhost = () => {
92
+ if (!activeGhost || !isTTY)
93
+ return;
94
+ process.stdout.write(`${' '.repeat(activeGhost.length)}\x1b[${activeGhost.length}D`);
95
+ activeGhost = '';
96
+ };
97
+ const drawGhost = (suffix) => {
98
+ if (!suffix || !isTTY)
99
+ return;
100
+ activeGhost = suffix;
101
+ process.stdout.write(`\x1b[2m${suffix}\x1b[0m\x1b[${suffix.length}D`);
102
+ };
103
+ // STEP 1 — clear ghost BEFORE readline redraws (prependListener fires first)
104
+ const onKeypressClear = (_ch, key) => {
105
+ if (!promptActive)
106
+ return;
107
+ if (key?.name === 'return' || key?.name === 'enter')
108
+ return;
109
+ clearGhost();
110
+ };
111
+ if (isTTY) {
112
+ readline.emitKeypressEvents(process.stdin);
113
+ process.stdin.prependListener('keypress', onKeypressClear);
114
+ }
115
+ // STEP 2 — readline (registers its own keypress handler after ours)
7
116
  const rl = readline.createInterface({
8
117
  input: process.stdin,
9
118
  output: process.stdout,
10
119
  terminal: true,
11
- // Future DX: path completion can be added here without changing callers.
120
+ completer: isTTY ? slashCompleter : undefined,
12
121
  historySize: 500,
13
122
  });
14
- // Attach keybindings if configured
15
- if (mode !== 'default') {
123
+ // STEP 3 draw ghost AFTER readline finishes its redraw (setImmediate)
124
+ const onKeypressDraw = (_ch, key) => {
125
+ if (!promptActive || !isTTY)
126
+ return;
127
+ if (key?.ctrl || key?.meta)
128
+ return;
129
+ if (key?.name === 'return' || key?.name === 'enter' || key?.name === 'tab')
130
+ return;
131
+ if (key?.name === 'up' || key?.name === 'down')
132
+ return;
133
+ setImmediate(() => {
134
+ if (!promptActive)
135
+ return;
136
+ const line = rl.line ?? '';
137
+ if (line === '') {
138
+ drawGhost(PLACEHOLDER);
139
+ return;
140
+ }
141
+ if (!/^\/\w/.test(line))
142
+ return;
143
+ const [hits] = slashCompleter(line);
144
+ if (!hits.length)
145
+ return;
146
+ const ghost = hits[0].slice(line.length);
147
+ if (!ghost)
148
+ return;
149
+ drawGhost(ghost);
150
+ });
151
+ };
152
+ if (isTTY)
153
+ process.stdin.on('keypress', onKeypressDraw);
154
+ if (mode !== 'default')
16
155
  attachKeybindings(rl, mode);
17
- }
18
156
  return {
19
- read(prompt) {
157
+ read(promptStr) {
20
158
  return new Promise((resolve) => {
21
- rl.question(prompt, (answer) => resolve(answer));
159
+ promptActive = true;
160
+ drawBoxTop();
161
+ rl.question(promptStr, (answer) => {
162
+ promptActive = false;
163
+ clearGhost();
164
+ drawBoxBottom();
165
+ resolve(answer);
166
+ });
167
+ // Show placeholder after readline renders the empty prompt
168
+ setImmediate(() => {
169
+ if (promptActive && !rl.line) {
170
+ drawGhost(PLACEHOLDER);
171
+ }
172
+ });
22
173
  });
23
174
  },
24
175
  close() {
176
+ promptActive = false;
177
+ clearGhost();
178
+ removeFooter();
179
+ process.off('SIGWINCH', onResize);
180
+ if (isTTY) {
181
+ process.stdin.removeListener('keypress', onKeypressClear);
182
+ process.stdin.removeListener('keypress', onKeypressDraw);
183
+ }
25
184
  rl.close();
26
185
  },
27
186
  getKeybindingMode() {
@@ -29,9 +188,12 @@ export function createPrompt(keybindingMode) {
29
188
  },
30
189
  };
31
190
  }
191
+ // ─── Prompt prefix (left border of input box) ─────────────────────────────
32
192
  export function prefix(mode) {
33
- const safeUnicode = process.platform !== 'win32' || Boolean(process.env.WT_SESSION);
34
- const label = mode === 'plan' ? 'Plan' : 'Copilot';
35
- const tag = safeUnicode ? theme.badge(label) : `[${label}]`;
36
- return `${tag} ${theme.user(safeUnicode ? '' : '>')} `;
193
+ const arrow = safeUnicode ? '' : '>';
194
+ const border = theme.dim('');
195
+ if (mode === 'plan') {
196
+ return ` ${border} ${theme.badge('plan')} ${theme.user(arrow)} `;
197
+ }
198
+ return ` ${border} ${theme.user(arrow)} `;
37
199
  }
package/dist/ui/render.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { theme } from './theme.js';
2
+ import { box } from './box.js';
2
3
  import { lazy } from '../util/lazy.js';
3
4
  let markdownConfigured = false;
4
5
  const loadMarked = lazy(async () => import('marked'));
@@ -24,9 +25,11 @@ export async function ensureMarkdown() {
24
25
  * Tracks fenced ``` code blocks across token boundaries and renders the
25
26
  * inside of a fence in `theme.hl` so the user can visually distinguish code
26
27
  * from prose during streaming, without waiting for the full markdown render. */
28
+ import { syntaxHighlightShell } from '../tools/shell.js';
27
29
  export class StreamSink {
28
30
  buf = '';
29
31
  inCode = false;
32
+ codeLang = '';
30
33
  lineBuf = '';
31
34
  write(token) {
32
35
  this.buf += token;
@@ -42,13 +45,26 @@ export class StreamSink {
42
45
  this.lineBuf = '';
43
46
  const trimmed = line.trimStart();
44
47
  if (trimmed.startsWith('```')) {
45
- // toggle on the fence boundary; print fence dimmed
46
- this.inCode = !this.inCode;
48
+ // Toggle fence; capture the language tag
49
+ if (!this.inCode) {
50
+ this.codeLang = trimmed.slice(3).trim().toLowerCase();
51
+ this.inCode = true;
52
+ }
53
+ else {
54
+ this.inCode = false;
55
+ this.codeLang = '';
56
+ }
47
57
  process.stdout.write(theme.dim(line));
48
58
  return;
49
59
  }
50
60
  if (this.inCode) {
51
- process.stdout.write(theme.hl(line));
61
+ const isShell = ['sh', 'bash', 'zsh', 'shell', 'fish', ''].includes(this.codeLang);
62
+ if (isShell) {
63
+ process.stdout.write(syntaxHighlightShell(line));
64
+ }
65
+ else {
66
+ process.stdout.write(theme.hl(line));
67
+ }
52
68
  return;
53
69
  }
54
70
  process.stdout.write(line);
@@ -94,3 +110,16 @@ export async function renderMarkdownString(md) {
94
110
  return md;
95
111
  }
96
112
  }
113
+ /** Render an AI response string inside a GitHub Copilot CLI-style bordered panel. */
114
+ export async function boxResponse(md) {
115
+ if (!md.trim())
116
+ return;
117
+ try {
118
+ const markdown = await ensureMarkdown();
119
+ const rendered = markdown.parse(md).trimEnd();
120
+ process.stdout.write(box(rendered, { title: 'Copilot', style: 'response' }));
121
+ }
122
+ catch {
123
+ process.stdout.write(box(md.trimEnd(), { title: 'Copilot', style: 'response' }));
124
+ }
125
+ }
package/dist/ui/theme.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import chalk, { Chalk, supportsColor } from 'chalk';
2
2
  import { config } from '../config.js';
3
- const plain = (text) => text;
4
3
  function colorEnabled() {
5
4
  if (config.theme === 'none')
6
5
  return false;
@@ -30,27 +29,86 @@ export function selectTheme() {
30
29
  return config.theme;
31
30
  }
32
31
  export const theme = {
33
- brand: style((c, name) => (name === 'light' ? c.hex('#0F6CBD').bold : c.hex('#58A6FF').bold)),
34
- user: style((c, name) => (name === 'light' ? c.hex('#0F6CBD').bold : c.hex('#58A6FF').bold)),
35
- assistant: style((c, name) => (name === 'light' ? c.green.bold : c.green)),
32
+ brand: style((c, n) => (n === 'light' ? c.hex('#0F6CBD').bold : c.hex('#58A6FF').bold)),
33
+ user: style((c, n) => (n === 'light' ? c.hex('#0F6CBD').bold : c.hex('#58A6FF').bold)),
34
+ assistant: style((c, n) => (n === 'light' ? c.green.bold : c.green)),
36
35
  system: style((c) => c.gray.italic),
37
- warn: style((c, name) => (name === 'light' ? c.hex('#92400E') : c.yellow)),
36
+ warn: style((c, n) => (n === 'light' ? c.hex('#92400E') : c.yellow)),
38
37
  err: style((c) => c.red.bold),
39
- ok: style((c, name) => (name === 'light' ? c.hex('#166534').bold : c.green.bold)),
38
+ ok: style((c, n) => (n === 'light' ? c.hex('#166534').bold : c.green.bold)),
40
39
  dim: style((c) => c.gray),
41
- hl: style((c, name) => (name === 'light' ? c.hex('#0A5CA8') : c.hex('#79C0FF'))),
40
+ hl: style((c, n) => (n === 'light' ? c.hex('#0A5CA8') : c.hex('#79C0FF'))),
41
+ ghost: style((c) => c.gray.dim),
42
+ hint: style((c) => c.gray.italic),
42
43
  badge: (s) => {
43
44
  if (!colorEnabled())
44
- return ` ${s} `;
45
+ return `[${s}]`;
45
46
  const p = palette();
46
47
  return p.name === 'light'
47
48
  ? p.c.bgHex('#0F6CBD').white.bold(` ${s} `)
48
49
  : p.c.bgHex('#1F6FEB').white.bold(` ${s} `);
49
50
  },
50
51
  };
51
- export function banner(version, model) {
52
- const title = theme.brand('GitHub Copilot');
53
- const sep = colorEnabled() ? '•' : '-';
54
- const sub = theme.dim(`v${version} ${sep} model: ${theme.hl(model)}`);
55
- return `\n${title} ${sub}\n${theme.dim('Type /help for commands. @file to inject a file. Ctrl-C to interrupt.')}\n`;
52
+ export const safeUnicode = process.platform !== 'win32' || Boolean(process.env.WT_SESSION);
53
+ // ─── Pixel-art logo ────────────────────────────────────────────────────────
54
+ // 5-row "ICOPILOT" in full-block characters (2-space gaps between letters).
55
+ // I=2 C=4 O=4 P=4 I=2 L=4 O=4 T=4
56
+ const LOGO_ROWS = [
57
+ '██ ████ ████ ████ ██ █ ████ ████',
58
+ '██ ██ █ █ █ █ ██ █ █ █ ██ ',
59
+ '██ ██ █ █ ████ ██ █ █ █ ██ ',
60
+ '██ ██ █ █ █ ██ █ █ █ ██ ',
61
+ '██ ████ ████ █ ██ ████ ████ ██ ',
62
+ ];
63
+ // ─── Pilot mascot (5 rows) ─────────────────────────────────────────────────
64
+ // Purple frame (#A371F7), cyan accents (#39D2D2).
65
+ function buildMascot(c) {
66
+ const fr = (s) => c.hex('#A371F7')(s);
67
+ const cy = (s) => c.hex('#39D2D2')(s);
68
+ return [
69
+ fr(' ╭─────╮ '),
70
+ fr(' │') + cy('◉') + fr(' ') + cy('◉') + fr('│ '),
71
+ fr(' │') + c.hex('#A371F7')(' ─── ') + fr('│ '),
72
+ fr(' ╰──') + cy('┬') + fr('──╯ '),
73
+ cy(' ▶') + c.hex('#A371F7')(' pilot '),
74
+ ];
75
+ }
76
+ export function banner(version, model, sessionDir) {
77
+ if (!colorEnabled()) {
78
+ return [
79
+ '',
80
+ 'iCopilot v' + version + ' model: ' + model,
81
+ '/help for commands · @file to add context · Tab to autocomplete',
82
+ '',
83
+ ].join('\n');
84
+ }
85
+ const { c, name } = palette();
86
+ const green = name === 'light' ? '#166534' : '#3FB950';
87
+ // Render logo rows in light-blue (#58A6FF)
88
+ const logoRows = LOGO_ROWS.map((r) => c.hex('#58A6FF').bold(r));
89
+ const mascotRows = buildMascot(c);
90
+ // Side-by-side: mascot (10 visible chars) + logo
91
+ const combined = logoRows.map((lr, i) => ` ${mascotRows[i] ?? ' '} ${lr}`).join('\n');
92
+ const sessDir = sessionDir ?? '~/.icopilot/sessions/';
93
+ const diag1 = ` ${c.hex(green)('●')} ` +
94
+ `${c.gray('Connected to')} ${c.hex('#58A6FF').bold('GitHub Models')} ` +
95
+ `${c.gray('[' + model + ']')}`;
96
+ const diag2 = ` ${c.hex(green)('●')} ` +
97
+ `${c.gray('Session:')} ${c.hex('#58A6FF')('Active')} ` +
98
+ `${c.gray('(' + sessDir + ')')}`;
99
+ const hints = safeUnicode
100
+ ? `${c.gray('/help')} for commands ${c.gray('@file')} to add context ${c.gray('Tab')} to autocomplete`
101
+ : `/help for commands @file to add context Tab to autocomplete`;
102
+ return [
103
+ '',
104
+ combined,
105
+ '',
106
+ ` ${c.gray('v' + version)} ${c.gray('·')} ${c.hex('#58A6FF')(model)}`,
107
+ '',
108
+ diag1,
109
+ diag2,
110
+ '',
111
+ ` ${hints}`,
112
+ '',
113
+ ].join('\n');
56
114
  }
package/package.json CHANGED
@@ -1,79 +1,79 @@
1
1
  {
2
- "name": "icopilot",
3
- "version": "2.2.0",
4
- "description": "iCopilot — terminal-native agentic CLI powered by GitHub Models",
5
- "type": "module",
6
- "bin": {
7
- "icopilot": "bin/icopilot.js",
8
- "icli": "bin/icopilot.js"
9
- },
10
- "main": "dist/index.js",
11
- "scripts": {
12
- "build": "tsc -p .",
13
- "dev": "tsc -p . --watch",
14
- "start": "node bin/icopilot.js",
15
- "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
16
- "lint:fix": "eslint --fix \"src/**/*.ts\" \"tests/**/*.ts\"",
17
- "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"*.md\"",
18
- "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
19
- "test": "vitest run",
20
- "test:watch": "vitest",
21
- "coverage": "vitest run --coverage",
22
- "typecheck": "tsc -p . --noEmit",
23
- "prepare": "tsc -p .",
24
- "release": "node scripts/release.mjs",
25
- "release:patch": "node scripts/release.mjs patch",
26
- "release:minor": "node scripts/release.mjs minor",
27
- "release:major": "node scripts/release.mjs major",
28
- "changelog": "node scripts/changelog.mjs",
29
- "smoke": "node scripts/smoke.mjs",
30
- "test:smoke": "vitest run tests/smoke --config vitest.smoke.config.ts",
31
- "screenshots": "node scripts/screenshots.mjs",
32
- "perf:cold-start": "node scripts/cold-start.mjs"
33
- },
34
- "engines": {
35
- "node": ">=18.17.0"
36
- },
37
- "dependencies": {
38
- "@inquirer/prompts": "^5.3.8",
39
- "chalk": "^5.3.0",
40
- "commander": "^12.1.0",
41
- "diff": "^5.2.0",
42
- "dotenv": "^16.4.5",
43
- "fast-glob": "^3.3.2",
44
- "gpt-tokenizer": "^2.1.2",
45
- "marked": "^13.0.3",
46
- "marked-terminal": "^7.1.0",
47
- "openai": "^4.56.0",
48
- "ora": "^8.0.1",
49
- "proxy-agent": "^6.5.0",
50
- "simple-git": "^3.25.0",
51
- "yaml": "^2.9.0"
52
- },
53
- "devDependencies": {
54
- "@types/diff": "^5.2.1",
55
- "@types/marked-terminal": "^6.1.1",
56
- "@types/node": "^20.14.10",
57
- "@typescript-eslint/eslint-plugin": "^7.18.0",
58
- "@typescript-eslint/parser": "^7.18.0",
59
- "@vitest/coverage-v8": "^1.6.0",
60
- "eslint": "^8.57.0",
61
- "eslint-config-prettier": "^9.1.0",
62
- "eslint-plugin-prettier": "^5.2.1",
63
- "prettier": "^3.3.3",
64
- "typescript": "^5.5.4",
65
- "vitest": "^1.6.0"
66
- },
67
- "license": "MIT",
68
- "files": [
69
- "bin",
70
- "dist",
71
- "README.md",
72
- "CHANGELOG.md",
73
- "LICENSE"
74
- ],
75
- "repository": {
76
- "type": "git",
77
- "url": ""
78
- }
2
+ "name": "icopilot",
3
+ "version": "2.3.2",
4
+ "description": "iCopilot — terminal-native agentic CLI powered by GitHub Models",
5
+ "type": "module",
6
+ "bin": {
7
+ "icopilot": "bin/icopilot.js",
8
+ "icli": "bin/icopilot.js"
9
+ },
10
+ "main": "dist/index.js",
11
+ "scripts": {
12
+ "build": "tsc -p .",
13
+ "dev": "tsc -p . --watch",
14
+ "start": "node bin/icopilot.js",
15
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
16
+ "lint:fix": "eslint --fix \"src/**/*.ts\" \"tests/**/*.ts\"",
17
+ "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"*.md\"",
18
+ "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "coverage": "vitest run --coverage",
22
+ "typecheck": "tsc -p . --noEmit",
23
+ "prepare": "tsc -p .",
24
+ "release": "node scripts/release.mjs",
25
+ "release:patch": "node scripts/release.mjs patch",
26
+ "release:minor": "node scripts/release.mjs minor",
27
+ "release:major": "node scripts/release.mjs major",
28
+ "changelog": "node scripts/changelog.mjs",
29
+ "smoke": "node scripts/smoke.mjs",
30
+ "test:smoke": "vitest run tests/smoke --config vitest.smoke.config.ts",
31
+ "screenshots": "node scripts/screenshots.mjs",
32
+ "perf:cold-start": "node scripts/cold-start.mjs"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.17.0"
36
+ },
37
+ "dependencies": {
38
+ "@inquirer/prompts": "^5.3.8",
39
+ "chalk": "^5.3.0",
40
+ "commander": "^12.1.0",
41
+ "diff": "^5.2.0",
42
+ "dotenv": "^16.4.5",
43
+ "fast-glob": "^3.3.2",
44
+ "gpt-tokenizer": "^2.1.2",
45
+ "marked": "^13.0.3",
46
+ "marked-terminal": "^7.1.0",
47
+ "openai": "^4.56.0",
48
+ "ora": "^8.0.1",
49
+ "proxy-agent": "^6.5.0",
50
+ "simple-git": "^3.25.0",
51
+ "yaml": "^2.9.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/diff": "^5.2.1",
55
+ "@types/marked-terminal": "^6.1.1",
56
+ "@types/node": "^20.14.10",
57
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
58
+ "@typescript-eslint/parser": "^7.18.0",
59
+ "@vitest/coverage-v8": "^1.6.0",
60
+ "eslint": "^8.57.0",
61
+ "eslint-config-prettier": "^9.1.0",
62
+ "eslint-plugin-prettier": "^5.2.1",
63
+ "prettier": "^3.3.3",
64
+ "typescript": "^5.5.4",
65
+ "vitest": "^1.6.0"
66
+ },
67
+ "license": "MIT",
68
+ "files": [
69
+ "bin",
70
+ "dist",
71
+ "README.md",
72
+ "CHANGELOG.md",
73
+ "LICENSE"
74
+ ],
75
+ "repository": {
76
+ "type": "git",
77
+ "url": "https://github.com/i4Edu/icli"
78
+ }
79
79
  }