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.
Files changed (49) hide show
  1. package/DailyNotesApp/app/build.gradle.kts +57 -0
  2. package/DailyNotesApp/app/src/main/java/com/dailynotes/app/MainActivity.kt +48 -0
  3. package/DailyNotesApp/app/src/main/java/com/dailynotes/app/Note.kt +13 -0
  4. package/DailyNotesApp/app/src/main/java/com/dailynotes/app/NoteDatabase.kt +9 -0
  5. package/DailyNotesApp/build.gradle.kts +5 -0
  6. package/DailyNotesApp/settings.gradle.kts +17 -0
  7. package/README.md +69 -82
  8. package/config.example.json +2 -1
  9. package/package.json +6 -3
  10. package/preview.jpg +0 -0
  11. package/src/core/auto-format.js +61 -12
  12. package/src/core/config.js +1 -1
  13. package/src/core/confirm.js +2 -2
  14. package/src/core/diff.js +4 -4
  15. package/src/core/doctor.js +2 -2
  16. package/src/core/error-handler.js +1 -1
  17. package/src/core/file-tracker.js +4 -4
  18. package/src/core/formatter.js +19 -19
  19. package/src/core/logger.js +19 -3
  20. package/src/core/spinner.js +2 -2
  21. package/src/core/stats.js +2 -2
  22. package/src/core/thinking.js +8 -8
  23. package/src/core/tool-help.js +2 -2
  24. package/src/core/tool-registry.js +6 -10
  25. package/src/core/update-checker.js +125 -0
  26. package/src/core/wizard.js +5 -5
  27. package/src/core/workspace.js +4 -4
  28. package/src/index.js +82 -37
  29. package/src/mcp/activation.js +3 -3
  30. package/src/mcp/message-sender.js +1 -1
  31. package/src/mcp/mqtt-client.js +4 -4
  32. package/src/repl-commands.js +2 -1
  33. package/src/repl.js +26 -6
  34. package/src/tools/config.js +1 -1
  35. package/src/tools/diff.js +6 -0
  36. package/src/tools/file.js +3 -3
  37. package/src/tools/git.js +1 -1
  38. package/src/tools/network.js +1 -1
  39. package/src/tools/project.js +2 -2
  40. package/src/tools/search.js +3 -3
  41. package/src/tools/shell.js +2 -2
  42. package/src/tools/template.js +1 -1
  43. package/src/tools/watch.js +1 -1
  44. package/src/tui/completable-input.js +127 -0
  45. package/src/tui/ink-app.js +324 -0
  46. package/src/tui/ink-components.js +157 -0
  47. package/src/tui/ink-entry.js +86 -0
  48. package/src/tui/renderer.js +137 -19
  49. package/src/tui/status-bar.js +7 -7
@@ -23,19 +23,38 @@ function center(text, width) {
23
23
  return ' '.repeat(left) + text + ' '.repeat(width - left - text.length);
24
24
  }
25
25
 
26
- export function renderBanner(config, tools, connected) {
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(`๐Ÿพ ${config.agent?.name || 'Pelulu CLI'}`, w)) + chalk.cyan(`${box.v}`));
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(` ๐Ÿ“ ${dirName}`, w)) + chalk.cyan(`${box.v}`));
37
- console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ๐Ÿ”ง ${tools.length} tools ยท ${actions} actions ยท 15 MCP slots used`, w)) + chalk.cyan(`${box.v}`));
38
- console.log(chalk.cyan(`${box.v}`) + chalk.gray(pad(` ${connected ? '๐ŸŸข MQTT Connected' : '๐Ÿ”ด Disconnected'}`, w)) + chalk.cyan(`${box.v}`));
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(' ๐Ÿ“Š Status', w)) + chalk.cyan(`${box.v}`));
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('๐Ÿ”ง Available Tools:'));
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
- console.log('');
71
- console.log(chalk.dim(` ${ts} `) + chalk.cyan('โš™๏ธ ') + chalk.white(`${name}.${action}`));
72
- if (args?.path) console.log(chalk.dim(` ๐Ÿ“ ${args.path}`));
73
- if (args?.command) console.log(chalk.dim(` ๐Ÿ’ป ${args.command}`));
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(` โœ… OK`));
97
+ console.log(chalk.green(` [OK] OK`));
79
98
  } else {
80
- console.log(chalk.red(` โŒ ${data || 'error'}`));
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
- console.log(chalk.green(` ๐Ÿค– ${text}`));
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('๐Ÿ‘ค ') + chalk.white(text));
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 index.js') + chalk.gray(' โ†’ file read'));
111
- console.log(chalk.cyan(' run npm test') + chalk.gray(' โ†’ shell exec'));
112
- console.log(chalk.cyan(' git status') + chalk.gray(' โ†’ git status'));
113
- console.log(chalk.cyan(' build') + chalk.gray(' โ†’ project build'));
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
+ }
@@ -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('โ”€'.repeat(process.stdout.columns || 60)),
26
- chalk.gray(` ${mqtt === 'โœ…' ? '๐ŸŸข' : '๐Ÿ”ด'} MQTT: ${mqtt}`),
27
- chalk.gray(` ๐Ÿ“ก Session: ${session}`),
28
- chalk.gray(` ๐Ÿ”ง Tools: ${tools}`),
29
- chalk.gray(` ๐Ÿ“Š Calls: ${calls}`),
30
- chalk.gray(` ๐Ÿ• ${time}`),
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
  }