pelulu-cli 1.0.0 โ 1.0.1
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/DailyNotesApp/app/build.gradle.kts +57 -0
- package/DailyNotesApp/app/src/main/java/com/dailynotes/app/MainActivity.kt +48 -0
- package/DailyNotesApp/app/src/main/java/com/dailynotes/app/Note.kt +13 -0
- package/DailyNotesApp/app/src/main/java/com/dailynotes/app/NoteDatabase.kt +9 -0
- package/DailyNotesApp/build.gradle.kts +5 -0
- package/DailyNotesApp/settings.gradle.kts +17 -0
- package/README.md +69 -82
- package/config.example.json +2 -1
- package/package.json +6 -3
- package/preview.jpg +0 -0
- package/src/core/auto-format.js +61 -12
- package/src/core/config.js +1 -1
- package/src/core/confirm.js +2 -2
- package/src/core/diff.js +4 -4
- package/src/core/doctor.js +2 -2
- package/src/core/error-handler.js +1 -1
- package/src/core/file-tracker.js +4 -4
- package/src/core/formatter.js +19 -19
- package/src/core/logger.js +19 -3
- package/src/core/spinner.js +2 -2
- package/src/core/stats.js +2 -2
- package/src/core/thinking.js +8 -8
- package/src/core/tool-help.js +2 -2
- package/src/core/tool-registry.js +6 -10
- package/src/core/update-checker.js +125 -0
- package/src/core/wizard.js +5 -5
- package/src/core/workspace.js +4 -4
- package/src/index.js +82 -37
- package/src/mcp/activation.js +3 -3
- package/src/mcp/message-sender.js +1 -1
- package/src/mcp/mqtt-client.js +4 -4
- package/src/repl-commands.js +2 -1
- package/src/repl.js +26 -6
- package/src/tools/config.js +1 -1
- package/src/tools/diff.js +6 -0
- package/src/tools/file.js +3 -3
- package/src/tools/git.js +1 -1
- package/src/tools/network.js +1 -1
- package/src/tools/project.js +2 -2
- package/src/tools/search.js +3 -3
- package/src/tools/shell.js +2 -2
- package/src/tools/template.js +1 -1
- package/src/tools/watch.js +1 -1
- package/src/tui/completable-input.js +127 -0
- package/src/tui/ink-app.js +324 -0
- package/src/tui/ink-components.js +157 -0
- package/src/tui/ink-entry.js +86 -0
- package/src/tui/renderer.js +137 -19
- package/src/tui/status-bar.js +7 -7
package/src/tui/renderer.js
CHANGED
|
@@ -23,19 +23,38 @@ function center(text, width) {
|
|
|
23
23
|
return ' '.repeat(left) + text + ' '.repeat(width - left - text.length);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Render the Pelulu ASCII art banner (displayed once at startup)
|
|
28
|
+
*/
|
|
29
|
+
export function renderAsciiBanner(version) {
|
|
30
|
+
const v = version ? `v.${version}` : '';
|
|
31
|
+
const lines = [
|
|
32
|
+
chalk.cyan(' /\\_/\\ '),
|
|
33
|
+
chalk.cyan(' ( o.o ) ') + chalk.bold.cyan(` P E L U L U - C L I`),
|
|
34
|
+
chalk.cyan(' > ^ < ') + chalk.gray(` ${v} | powered by XiaoZhi`),
|
|
35
|
+
chalk.cyan(' /| |\\ '),
|
|
36
|
+
chalk.cyan('(_| |_)') + chalk.dim(' the tiny coding companion'),
|
|
37
|
+
];
|
|
38
|
+
console.log('');
|
|
39
|
+
for (const line of lines) console.log(line);
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function renderBanner(config, tools, connected, meta = {}) {
|
|
27
44
|
const w = 48;
|
|
28
45
|
const actions = tools.reduce((s, t) => s + (t.actions?.length || 0), 0);
|
|
29
46
|
const cwd = process.cwd();
|
|
30
47
|
const dirName = cwd.split('/').pop() || cwd;
|
|
48
|
+
const session = meta.session || '-';
|
|
49
|
+
const version = config.agent?.version || '';
|
|
31
50
|
|
|
32
51
|
console.log('');
|
|
33
52
|
console.log(chalk.cyan(`${box.tl}${horizontal(w, box.h)}${box.tr}`));
|
|
34
|
-
console.log(chalk.cyan(`${box.v}`) + chalk.bold.white(center(
|
|
53
|
+
console.log(chalk.cyan(`${box.v}`) + chalk.bold.white(center(`${config.agent?.name || 'Pelulu CLI'}${version ? ' v' + version : ''}`, w)) + chalk.cyan(`${box.v}`));
|
|
35
54
|
console.log(chalk.cyan(`${box.ml}${horizontal(w, box.h)}${box.mr}`));
|
|
36
|
-
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(`
|
|
37
|
-
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(`
|
|
38
|
-
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${connected ? '
|
|
55
|
+
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${dirName}`, w)) + chalk.cyan(`${box.v}`));
|
|
56
|
+
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${tools.length} tools / ${actions} actions`, w)) + chalk.cyan(`${box.v}`));
|
|
57
|
+
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${connected ? 'MQTT: on' : 'MQTT: off'} | Session: ${session}`, w)) + chalk.cyan(`${box.v}`));
|
|
39
58
|
console.log(chalk.cyan(`${box.bl}${horizontal(w, box.h)}${box.br}`));
|
|
40
59
|
console.log('');
|
|
41
60
|
}
|
|
@@ -44,7 +63,7 @@ export function renderStatus(status) {
|
|
|
44
63
|
const w = 48;
|
|
45
64
|
console.log('');
|
|
46
65
|
console.log(chalk.cyan(`${box.tl}${horizontal(w, box.h)}${box.tr}`));
|
|
47
|
-
console.log(chalk.cyan(`${box.v}`) + chalk.bold.white(pad('
|
|
66
|
+
console.log(chalk.cyan(`${box.v}`) + chalk.bold.white(pad(' [STATS] Status', w)) + chalk.cyan(`${box.v}`));
|
|
48
67
|
console.log(chalk.cyan(`${box.ml}${horizontal(w, box.h)}${box.mr}`));
|
|
49
68
|
for (const [key, value] of Object.entries(status)) {
|
|
50
69
|
console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${key}: ${value}`, w)) + chalk.cyan(`${box.v}`));
|
|
@@ -55,7 +74,7 @@ export function renderStatus(status) {
|
|
|
55
74
|
|
|
56
75
|
export function renderTools(tools) {
|
|
57
76
|
console.log('');
|
|
58
|
-
console.log(chalk.bold.white('
|
|
77
|
+
console.log(chalk.bold.white('Available Tools:'));
|
|
59
78
|
console.log('');
|
|
60
79
|
for (const t of tools) {
|
|
61
80
|
const actions = t.actions?.map(a => a.name || a).join(', ') || '';
|
|
@@ -67,29 +86,73 @@ export function renderTools(tools) {
|
|
|
67
86
|
|
|
68
87
|
export function renderToolCall(name, action, args) {
|
|
69
88
|
const ts = new Date().toLocaleTimeString();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
const actionStr = action ? `.${action}` : '';
|
|
90
|
+
const detail = args?.path || args?.command || args?.pattern || args?.url || '';
|
|
91
|
+
const detailStr = detail ? chalk.dim(` ${detail}`) : '';
|
|
92
|
+
console.log(` ${chalk.dim(ts)} ${chalk.cyan(name)}${chalk.white(actionStr)}${detailStr}`);
|
|
74
93
|
}
|
|
75
94
|
|
|
76
95
|
export function renderToolResult(success, data) {
|
|
77
96
|
if (success) {
|
|
78
|
-
console.log(chalk.green(`
|
|
97
|
+
console.log(chalk.green(` [OK] OK`));
|
|
79
98
|
} else {
|
|
80
|
-
console.log(chalk.red(`
|
|
99
|
+
console.log(chalk.red(` [ERR] ${data || 'error'}`));
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Strip emoji and decorative unicode from text.
|
|
105
|
+
* Keeps ASCII, CJK, common punctuation.
|
|
106
|
+
*/
|
|
107
|
+
function stripEmojis(text) {
|
|
108
|
+
// Remove emoji (Unicode ranges), box-drawing decorative symbols, and common decorative chars
|
|
109
|
+
return text
|
|
110
|
+
.replace(/\p{Emoji_Presentation}/gu, '')
|
|
111
|
+
.replace(/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?/gu, '')
|
|
112
|
+
.replace(/\p{Extended_Pictographic}/gu, '')
|
|
113
|
+
.replace(/[\u2600-\u27BF]/g, '')
|
|
114
|
+
.replace(/[\u{1F000}-\u{1FFFF}]/gu, '')
|
|
115
|
+
.replace(/[\u200D\uFE0F]/g, '')
|
|
116
|
+
.replace(/[ \t]{2,}/g, ' ')
|
|
117
|
+
.replace(/ ([.,;:!?])/g, '$1')
|
|
118
|
+
.trim();
|
|
119
|
+
}
|
|
120
|
+
|
|
84
121
|
export function renderAiResponse(text) {
|
|
122
|
+
const clean = stripEmojis(text);
|
|
123
|
+
if (!clean) return;
|
|
85
124
|
console.log('');
|
|
86
|
-
|
|
125
|
+
// Wrap long lines at 80 chars
|
|
126
|
+
const lines = wrapText(clean, 80);
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
console.log(chalk.white(` ${line}`));
|
|
129
|
+
}
|
|
87
130
|
console.log('');
|
|
88
131
|
}
|
|
89
132
|
|
|
133
|
+
function wrapText(text, maxWidth) {
|
|
134
|
+
const paragraphs = text.split('\n');
|
|
135
|
+
const result = [];
|
|
136
|
+
for (const para of paragraphs) {
|
|
137
|
+
if (!para.trim()) { result.push(''); continue; }
|
|
138
|
+
const words = para.split(/\s+/);
|
|
139
|
+
let line = '';
|
|
140
|
+
for (const word of words) {
|
|
141
|
+
if (line.length + word.length + 1 > maxWidth && line.length > 0) {
|
|
142
|
+
result.push(line);
|
|
143
|
+
line = word;
|
|
144
|
+
} else {
|
|
145
|
+
line = line ? `${line} ${word}` : word;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (line) result.push(line);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
90
153
|
export function renderUserInput(text) {
|
|
91
154
|
const ts = new Date().toLocaleTimeString();
|
|
92
|
-
console.log(chalk.dim(` ${ts} `) + chalk.blue('
|
|
155
|
+
console.log(chalk.dim(` ${ts} `) + chalk.blue('> ') + chalk.white(text));
|
|
93
156
|
}
|
|
94
157
|
|
|
95
158
|
export function renderHelp() {
|
|
@@ -103,17 +166,72 @@ export function renderHelp() {
|
|
|
103
166
|
console.log(chalk.cyan(' /files') + chalk.gray(' File changes'));
|
|
104
167
|
console.log(chalk.cyan(' /call <tool>') + chalk.gray(' Call tool directly'));
|
|
105
168
|
console.log(chalk.cyan(' /doctor') + chalk.gray(' Health check'));
|
|
169
|
+
console.log(chalk.cyan(' /keys') + chalk.gray(' Keyboard shortcuts'));
|
|
106
170
|
console.log(chalk.cyan(' /clear') + chalk.gray(' Clear screen'));
|
|
107
171
|
console.log(chalk.cyan(' /quit') + chalk.gray(' Exit'));
|
|
108
172
|
console.log('');
|
|
109
173
|
console.log(chalk.bold.white(' Shortcuts:'));
|
|
110
|
-
console.log(chalk.cyan(' read
|
|
111
|
-
console.log(chalk.cyan(' run
|
|
112
|
-
console.log(chalk.cyan(' git
|
|
113
|
-
console.log(chalk.cyan(' build') + chalk.gray('
|
|
174
|
+
console.log(chalk.cyan(' read <file>') + chalk.gray(' file read'));
|
|
175
|
+
console.log(chalk.cyan(' run <cmd>') + chalk.gray(' shell exec'));
|
|
176
|
+
console.log(chalk.cyan(' git <cmd>') + chalk.gray(' git operation'));
|
|
177
|
+
console.log(chalk.cyan(' build / test / lint') + chalk.gray(' project actions'));
|
|
178
|
+
console.log(chalk.cyan(' Tab') + chalk.gray(' auto-complete'));
|
|
114
179
|
console.log('');
|
|
115
180
|
}
|
|
116
181
|
|
|
117
182
|
export function createPrompt(dirName) {
|
|
118
183
|
return chalk.cyan(`${dirName} `) + chalk.white('โฏ ');
|
|
119
184
|
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Render update notification banner
|
|
188
|
+
* @param {object} update - { local, remote, release: { tag, name, url, body } }
|
|
189
|
+
*/
|
|
190
|
+
export function renderUpdateNotification(update) {
|
|
191
|
+
const w = 56;
|
|
192
|
+
const { local, remote, release } = update;
|
|
193
|
+
|
|
194
|
+
console.log('');
|
|
195
|
+
console.log(chalk.yellow(`${box.tl}${horizontal(w, box.h)}${box.tr}`));
|
|
196
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.bold.yellow(center(' UPDATE TERSEDIA!', w)) + chalk.yellow(`${box.v}`));
|
|
197
|
+
console.log(chalk.yellow(`${box.ml}${horizontal(w, box.h)}${box.mr}`));
|
|
198
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.white(pad(` Versi lokal : ${local}`, w)) + chalk.yellow(`${box.v}`));
|
|
199
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.green(pad(` Versi terbaru : ${remote}`, w)) + chalk.yellow(`${box.v}`));
|
|
200
|
+
|
|
201
|
+
if (release?.name && release.name !== release.tag) {
|
|
202
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.gray(pad(` Release : ${release.name}`, w)) + chalk.yellow(`${box.v}`));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (release?.url) {
|
|
206
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.cyan(pad(` ${release.url}`, w)) + chalk.yellow(`${box.v}`));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(chalk.yellow(`${box.ml}${horizontal(w, box.h)}${box.mr}`));
|
|
210
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.bold.white(pad(' Jalankan perintah berikut untuk update:', w)) + chalk.yellow(`${box.v}`));
|
|
211
|
+
console.log(chalk.yellow(`${box.v}`) + chalk.green(pad(' npm install pelulu-cli@latest', w)) + chalk.yellow(`${box.v}`));
|
|
212
|
+
console.log(chalk.yellow(`${box.bl}${horizontal(w, box.h)}${box.br}`));
|
|
213
|
+
console.log('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Render update check failure (silent, dim)
|
|
218
|
+
*/
|
|
219
|
+
export function renderUpdateError(message) {
|
|
220
|
+
console.log(chalk.dim(` [WARN] Update check failed: ${message}`));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Render a clean init status line (replaces verbose per-tool logging)
|
|
225
|
+
*/
|
|
226
|
+
export function renderInitLine(icon, text, detail = '') {
|
|
227
|
+
const detailStr = detail ? chalk.dim(` (${detail})`) : '';
|
|
228
|
+
console.log(chalk.gray(` ${icon} ${text}`) + detailStr);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Render ready line with session info
|
|
233
|
+
*/
|
|
234
|
+
export function renderReady(sessionId) {
|
|
235
|
+
console.log(chalk.green(` โ Ready`) + chalk.dim(` session: ${sessionId || '-'}`));
|
|
236
|
+
console.log('');
|
|
237
|
+
}
|
package/src/tui/status-bar.js
CHANGED
|
@@ -7,7 +7,7 @@ import chalk from 'chalk';
|
|
|
7
7
|
export class StatusBar {
|
|
8
8
|
constructor() {
|
|
9
9
|
this.items = {
|
|
10
|
-
mqtt: '
|
|
10
|
+
mqtt: '...',
|
|
11
11
|
session: '-',
|
|
12
12
|
tools: 0,
|
|
13
13
|
calls: 0,
|
|
@@ -22,12 +22,12 @@ export class StatusBar {
|
|
|
22
22
|
const { mqtt, session, tools, calls } = this.items;
|
|
23
23
|
const time = new Date().toLocaleTimeString();
|
|
24
24
|
const line = [
|
|
25
|
-
chalk.dim('
|
|
26
|
-
chalk.gray(` ${mqtt === '
|
|
27
|
-
chalk.gray(`
|
|
28
|
-
chalk.gray(`
|
|
29
|
-
chalk.gray(`
|
|
30
|
-
chalk.gray(`
|
|
25
|
+
chalk.dim('\u2500'.repeat(process.stdout.columns || 60)),
|
|
26
|
+
chalk.gray(` ${mqtt === '[OK]' ? '[on]' : '[off]'} MQTT: ${mqtt}`),
|
|
27
|
+
chalk.gray(` Session: ${session}`),
|
|
28
|
+
chalk.gray(` Tools: ${tools}`),
|
|
29
|
+
chalk.gray(` Calls: ${calls}`),
|
|
30
|
+
chalk.gray(` ${time}`),
|
|
31
31
|
].join(' ');
|
|
32
32
|
console.log(line);
|
|
33
33
|
}
|