kiosapi 0.1.19 → 0.1.22

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/agent/run.js CHANGED
@@ -251,11 +251,15 @@ async function runTool(call, otomatis, model) {
251
251
  const str = (k) => (typeof args[k] === 'string' ? args[k] : '');
252
252
  try {
253
253
  switch (call.function.name) {
254
- case 'baca_file':
255
- console.log(dim(` baca ${str('path')}`));
256
- return { output: bacaFile(str('path')) };
254
+ case 'baca_file': {
255
+ const mulai = typeof args.mulai === 'number' ? Math.floor(args.mulai) : undefined;
256
+ const sampai = typeof args.sampai === 'number' ? Math.floor(args.sampai) : undefined;
257
+ const rangeStr = mulai != null ? ` baris ${mulai}${sampai != null ? `–${sampai}` : '+'}` : '';
258
+ console.log(dim(` baca ${str('path')}${rangeStr}`));
259
+ return { output: bacaFile(str('path'), mulai, sampai) };
260
+ }
257
261
  case 'daftar_file': {
258
- const ked = typeof args['kedalaman'] === 'number' ? args['kedalaman'] : undefined;
262
+ const ked = typeof args.kedalaman === 'number' ? args.kedalaman : undefined;
259
263
  const dPath = str('path') || '.';
260
264
  console.log(dim(` daftar ${dPath}${ked != null ? ` (kedalaman ${ked})` : ''}`));
261
265
  return { output: daftarFile(dPath, ked) };
@@ -406,7 +410,9 @@ export async function runTurn(s, userText) {
406
410
  try {
407
411
  snippets.push(`<root-directory>\n${daftarFile('.', 2)}\n</root-directory>`);
408
412
  }
409
- catch { /* ignore */ }
413
+ catch {
414
+ /* ignore */
415
+ }
410
416
  for (const f of ['package.json', 'pyproject.toml', 'Cargo.toml', 'go.mod']) {
411
417
  const abs = join(process.cwd(), f);
412
418
  if (existsSync(abs)) {
@@ -415,7 +421,9 @@ export async function runTurn(s, userText) {
415
421
  // 1500 chars: enough for name/scripts/deps, not the full lockfile prose
416
422
  snippets.push(`<file path="${f}">\n${content.slice(0, 1_500)}\n</file>`);
417
423
  }
418
- catch { /* unreadable — skip */ }
424
+ catch {
425
+ /* unreadable — skip */
426
+ }
419
427
  }
420
428
  }
421
429
  if (snippets.length > 0) {
@@ -523,9 +531,7 @@ export async function runTurn(s, userText) {
523
531
  const total = callCounts.get(loopSig) ?? 0;
524
532
  console.log(red(`\n⚠ Loop terdeteksi: "${loopName}" dipanggil ${total}× — agen terjebak.`));
525
533
  console.log(yellow('Berikan instruksi baru, atau ketik /bersih untuk mulai ulang.'));
526
- const loopMsg = `⚠ LOOP: "${loopName}" sudah dipanggil ${total}× dengan argumen yang sama. ` +
527
- `JANGAN panggil lagi. Gunakan tool "selesai" dan jelaskan kendalanya, ` +
528
- `atau minta klarifikasi dari pengguna.`;
534
+ const loopMsg = `⚠ LOOP: "${loopName}" sudah dipanggil ${total}× dengan argumen yang sama. JANGAN panggil lagi. Gunakan tool "selesai" dan jelaskan kendalanya, atau minta klarifikasi dari pengguna.`;
529
535
  for (const call of calls) {
530
536
  s.messages.push({ role: 'tool', content: loopMsg, tool_call_id: call.id });
531
537
  }
@@ -545,11 +551,8 @@ export async function runTurn(s, userText) {
545
551
  const toolName = call.function.name;
546
552
  const cachedOut = toolCache.get(sig) ?? '';
547
553
  const warn = count >= COUNT_LIMIT - 1
548
- ? `⚠ STOP: "${toolName}" sudah dipanggil ${count}× dengan argumen yang sama — ` +
549
- `hasilnya tidak berubah. WAJIB gunakan tool "selesai" atau eksplorasi path BERBEDA.`
550
- : `[Cache — identik dengan panggilan sebelumnya]\n${cachedOut}\n\n` +
551
- `⚠ Kamu sudah memanggil "${toolName}" dengan argumen yang sama ${count}×. ` +
552
- `Jangan ulangi — masuk ke subfolder spesifik atau gunakan "selesai".`;
554
+ ? `⚠ STOP: "${toolName}" sudah dipanggil ${count}× dengan argumen yang sama — hasilnya tidak berubah. WAJIB gunakan tool "selesai" atau eksplorasi path BERBEDA.`
555
+ : `[Cache identik dengan panggilan sebelumnya]\n${cachedOut}\n\n⚠ Kamu sudah memanggil "${toolName}" dengan argumen yang sama ${count}×. Jangan ulangi — masuk ke subfolder spesifik atau gunakan "selesai".`;
553
556
  console.log(dim(` ↩ ${toolName} (cache ke-${count})`));
554
557
  s.messages.push({ role: 'tool', content: warn, tool_call_id: call.id });
555
558
  continue;
@@ -559,9 +562,9 @@ export async function runTurn(s, userText) {
559
562
  // listing or 100 KB of file content, which quickly overflows small-context-window models
560
563
  // (Workers AI llama has ~8K token limit). The full result is already returned by runTool;
561
564
  // truncating only the *stored* copy keeps the API payload manageable without losing info.
562
- const MAX_STORED_RESULT = 5_000;
565
+ const MAX_STORED_RESULT = 10_000;
563
566
  const stored = result.output.length > MAX_STORED_RESULT
564
- ? `${result.output.slice(0, MAX_STORED_RESULT)}\n…[dipotong — gunakan path/range spesifik jika perlu lebih]`
567
+ ? `${result.output.slice(0, MAX_STORED_RESULT)}\n…[dipotong — gunakan baca_file(path, baris_lanjutan) untuk membaca sisa file]`
565
568
  : result.output;
566
569
  s.messages.push({ role: 'tool', content: stored, tool_call_id: call.id });
567
570
  if (result.modifiedPath)
@@ -4,10 +4,14 @@ const TOOLS = {
4
4
  type: 'function',
5
5
  function: {
6
6
  name: 'baca_file',
7
- description: 'Baca isi sebuah file (path relatif ke direktori kerja).',
7
+ description: 'Baca isi file. Untuk file besar yang terpotong, gunakan mulai (nomor baris 1-based) untuk membaca bagian selanjutnya.',
8
8
  parameters: {
9
9
  type: 'object',
10
- properties: { path: { type: 'string', description: 'Path file' } },
10
+ properties: {
11
+ path: { type: 'string', description: 'Path file' },
12
+ mulai: { type: 'number', description: 'Baris awal, 1-based (default: 1)' },
13
+ sampai: { type: 'number', description: 'Baris akhir (default: akhir file)' },
14
+ },
11
15
  required: ['path'],
12
16
  },
13
17
  },
@@ -188,9 +192,21 @@ const MODE_BRIEF = {
188
192
  export function systemPrompt(mode) {
189
193
  const platform = process.platform;
190
194
  const osName = platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'macOS' : 'Linux';
191
- const shellNote = platform === 'win32'
192
- ? 'Shell: cmd.exe — gunakan sintaks Windows: mkdir folder (bukan mkdir -p), del file (bukan rm), move src dst (bukan mv), copy src dst (bukan cp). JANGAN pakai perintah bash/unix.'
193
- : 'Shell: bash';
195
+ const isGitBash = platform === 'win32' && !!process.env.MSYSTEM;
196
+ const shellNote = isGitBash
197
+ ? `Shell: bash (Git Bash on Windows) — gunakan sintaks bash:
198
+ · mkdir -p · rm file · mv src dst · cp src dst · ls · wc -l · grep · cat
199
+ · Path: gunakan forward slash / (bukan backslash)
200
+ · Path dengan [ ] atau spasi: kutip tunggal — wc -l 'src/routes/hub/[slug]/file.svelte'
201
+ · Hitung baris: wc -l 'path/file'`
202
+ : platform === 'win32'
203
+ ? `Shell: cmd.exe — sintaks Windows WAJIB:
204
+ · mkdir folder · del file · move src dst · copy src dst · type file
205
+ · DILARANG: wc, grep, cat, ls, find -name, atau perintah bash/unix APAPUN
206
+ · Hitung baris: type "file" | find /c /v "" (BUKAN wc -l)
207
+ · Path berisi [ ] atau spasi WAJIB dikutip ganda: type "path\\[slug]\\file.svelte"
208
+ · Gunakan baca_file/cari/daftar_file — JANGAN shell untuk membaca/mencari konten`
209
+ : 'Shell: bash';
194
210
  return `Kamu adalah asisten coding Kiosapi yang bekerja di terminal developer Indonesia.
195
211
  ${MODE_BRIEF[mode]}
196
212
 
@@ -199,8 +215,9 @@ OS: ${osName} · ${shellNote}
199
215
  Aturan:
200
216
  - Bekerja langkah demi langkah: pakai tool untuk membaca sebelum mengubah.
201
217
  - Strategi eksplorasi: struktur root tersedia di konteks awal — identifikasi subfolder relevan LANGSUNG, lalu baca dengan baca_file. Jangan ulangi daftar_file pada path yang sama.
218
+ - baca_file menampilkan "[path · N baris · M char]" — jika ada "TERPOTONG", panggil baca_file(path, baris_lanjutan) BUKAN dengan argumen identik. Gunakan mulai/sampai untuk baca bagian tertentu.
202
219
  - Kedalaman daftar_file: gunakan kedalaman=2 (default) untuk subfolder besar. Kedalaman=3 hanya jika kamu sudah tahu folder itu kecil. JANGAN gunakan kedalaman=4+ kecuali diminta eksplisit.
203
- - JANGAN memanggil tool APAPUN dengan argumen identik lebih dari 1× dalam satu sesi. Jika tool mengembalikan peringatan cache "⚠", langsung ganti ke path atau argumen BERBEDA.
220
+ - JANGAN memanggil tool APAPUN dengan argumen identik lebih dari 1× dalam satu sesi. Jika tool mengembalikan peringatan cache "⚠", langsung ganti ke path atau argumen BERBEDA — untuk baca_file: gunakan mulai= berbeda atau gunakan cari.
204
221
  - Path selalu relatif ke direktori kerja; akses ke luar ditolak.
205
222
  - Gunakan hapus_file/pindah_file untuk menghapus/memindahkan file (lebih aman dari jalankan del/rm).
206
223
  - Buat perubahan kecil dan jelas. Setelah tugas beres, panggil tool "selesai" dengan ringkasan.
@@ -42,15 +42,30 @@ export function safePath(p) {
42
42
  }
43
43
  return { abs, rel: rel || '.' };
44
44
  }
45
- /** baca_file — read a text file. */
46
- export function bacaFile(path) {
45
+ /** baca_file — read a text file, optionally a specific line range (1-based). */
46
+ export function bacaFile(path, mulai, sampai) {
47
47
  const { abs } = safePath(path);
48
48
  if (!existsSync(abs))
49
49
  return `Error: file tidak ada: ${path}`;
50
50
  const text = readFileSync(abs, 'utf8');
51
- return text.length > MAX_READ
52
- ? `${text.slice(0, MAX_READ)}\n…[dipotong, ${text.length} char total]`
53
- : text;
51
+ const allLines = text.split('\n');
52
+ const total = allLines.length;
53
+ if (mulai != null || sampai != null) {
54
+ const from = Math.max(1, Math.floor(mulai ?? 1));
55
+ const to = Math.min(total, Math.floor(sampai ?? total));
56
+ const content = allLines.slice(from - 1, to).join('\n');
57
+ const hint = to < total
58
+ ? `\n\n…[baris ${to + 1}–${total} belum dibaca — panggil baca_file("${path}", ${to + 1}) untuk lanjut]`
59
+ : '';
60
+ return `[${path} · baris ${from}–${to} dari ${total}]\n${content}${hint}`;
61
+ }
62
+ if (text.length > MAX_READ) {
63
+ const sliced = text.slice(0, MAX_READ);
64
+ const linesShown = sliced.split('\n').length;
65
+ return (`[${path} · ${total} baris · ${text.length} char — menampilkan baris 1–${linesShown}]\n` +
66
+ `${sliced}\n\n…[TERPOTONG — panggil baca_file("${path}", ${linesShown + 1}) untuk melanjutkan]`);
67
+ }
68
+ return `[${path} · ${total} baris · ${text.length} char]\n${text}`;
54
69
  }
55
70
  /** daftar_file — list a directory (ignored entries hidden).
56
71
  * kedalaman controls tree depth (1–5). Defaults: 3 for root ".", 1 for subdirs.
@@ -306,11 +321,15 @@ export async function jalankan(perintah) {
306
321
  let output = '';
307
322
  let limitHit = false;
308
323
  process.stdout.write('\n');
309
- // On Windows, explicitly use cmd.exe so Windows commands (findstr, dir, etc.) work correctly
310
- // even when the CLI is launched from a Git Bash or similar POSIX-shell terminal.
311
- const proc = process.platform === 'win32'
312
- ? spawn('cmd.exe', ['/c', perintah], { cwd: ROOT })
313
- : spawn(perintah, { cwd: ROOT, shell: true });
324
+ // Detect Git Bash on Windows: MSYSTEM is set by Git Bash (MINGW64/MINGW32).
325
+ // When in Git Bash, use bash -c so bash commands (ls, grep, wc, etc.) work correctly.
326
+ // Otherwise on plain Windows, use cmd.exe /c explicitly.
327
+ const isGitBash = process.platform === 'win32' && !!process.env.MSYSTEM;
328
+ const proc = isGitBash
329
+ ? spawn('bash', ['-c', perintah], { cwd: ROOT })
330
+ : process.platform === 'win32'
331
+ ? spawn('cmd.exe', ['/c', perintah], { cwd: ROOT })
332
+ : spawn(perintah, { cwd: ROOT, shell: true });
314
333
  const onData = (chunk) => {
315
334
  const text = chunk.toString();
316
335
  output += text;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.19",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [