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/README.md +324 -166
- package/bin/icopilot.js +0 -0
- package/dist/commands/suggest-cmd.js +148 -8
- package/dist/index.js +25 -2
- package/dist/modes/interactive.js +4 -1
- package/dist/modes/tui.js +22 -6
- package/dist/tools/shell.js +133 -25
- package/dist/ui/box.js +79 -0
- package/dist/ui/prompt.js +174 -12
- package/dist/ui/render.js +32 -3
- package/dist/ui/theme.js +71 -13
- package/package.json +77 -77
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
|
-
|
|
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
|
-
|
|
120
|
+
completer: isTTY ? slashCompleter : undefined,
|
|
12
121
|
historySize: 500,
|
|
13
122
|
});
|
|
14
|
-
//
|
|
15
|
-
|
|
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(
|
|
157
|
+
read(promptStr) {
|
|
20
158
|
return new Promise((resolve) => {
|
|
21
|
-
|
|
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
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
//
|
|
46
|
-
|
|
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
|
-
|
|
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,
|
|
34
|
-
user: style((c,
|
|
35
|
-
assistant: style((c,
|
|
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,
|
|
36
|
+
warn: style((c, n) => (n === 'light' ? c.hex('#92400E') : c.yellow)),
|
|
38
37
|
err: style((c) => c.red.bold),
|
|
39
|
-
ok: style((c,
|
|
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,
|
|
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 `
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
}
|