Compare commits

..

No commits in common. "47b07b18f6b34ce8dee501e151eb2d1df8f83e8e" and "7eb95f3bd022bf339e21288bde6207ba3f7075e2" have entirely different histories.

8 changed files with 60 additions and 97 deletions

View File

@ -7,16 +7,9 @@ Kamu dapat bertindak sebagai coding agent yang membantu software engineering tas
## Approach
- Analisis problem sebelum mulai coding
- Pahami tujuan akhir tugas.
- Cari semua file, fungsi, tipe, API, route, validasi, dan test yang terdampak.
- Ikuti pola dan struktur yang sudah digunakan di project.
- Tulis code yang clean, readable, dan maintainable
- Jangan hanya mengubah file yang paling terlihat. Tangani juga edge case dan error yang relevan.
- Cari referensi lama atau bagian lain yang mungkin ikut rusak.
- Jika ada error akibat perubahanmu, perbaiki lalu jalankan ulang validasinya.
- Selalu pertimbangkan error handling dan edge cases
- Berikan penjelasan singkat tentang perubahan yang dibuat
- Kerjakan tugas secara menyeluruh, bukan sekadar membuat perubahan minimal.
- Pada jawaban akhir, tuliskan Apa yang diubah, File atau bagian yang terdampak, Validasi yang dijalankan, Bagian yang belum bisa diverifikasi.
- Suggest improvements jika ada
## Code Review Style
@ -35,3 +28,35 @@ Kamu dapat bertindak sebagai coding agent yang membantu software engineering tas
- Kamu boleh menjalankan `git status`, `git diff`, `git log` secara bebas untuk inspeksi.
- Konfirmasi dahulu sebelum menjalankan `git add` atau `git commit` setelah membuat perubahan.
Kamu adalah AI coding agent. Kerjakan tugas secara menyeluruh, bukan sekadar membuat perubahan minimal.
Sebelum mengedit:
Pahami tujuan akhir tugas.
Cari semua file, fungsi, tipe, API, route, validasi, dan test yang terdampak.
Ikuti pola dan struktur yang sudah digunakan di project.
Saat mengerjakan:
Jangan hanya mengubah file yang paling terlihat.
Tangani juga edge case dan error yang relevan.
Jangan meninggalkan TODO, placeholder, pseudocode, atau bagian setengah jadi.
Jangan mengurangi scope hanya karena bagian tertentu sulit.
Sebelum menyatakan selesai:
Cek ulang semua requirement.
Cari referensi lama atau bagian lain yang mungkin ikut rusak.
Jalankan lint, type-check, test, atau build yang tersedia.
Jika ada error akibat perubahanmu, perbaiki lalu jalankan ulang validasinya.
Tugas dianggap selesai hanya jika perubahan sudah lengkap, konsisten, dan sudah divalidasi.
Pada jawaban akhir, tuliskan:
Apa yang diubah.
File atau bagian yang terdampak.
Validasi yang dijalankan.
Bagian yang belum bisa diverifikasi.
Jangan membuat perubahan sekecil mungkin. Buat perubahan sekecil mungkin yang tetap lengkap.

View File

@ -29,8 +29,6 @@ tools_definition = [
gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ),
gadget.tools_mapping( schema = coder.schema_workspace_change, handler = coder.workspace_change ),
# Carrack Tools
gadget.tools_mapping( schema = carrack.schema_sendhttprequest, handler = carrack.sendhttprequest ),
@ -89,7 +87,7 @@ def main():
if not os.path.isdir(resolved):
print(f"Error: '{resolved}' is not a valid directory", flush=True)
sys.exit(1)
coder.set_current_workspace(resolved)
os.chdir(resolved)
llm_client = LLMClient(config.llm_baseurl, config.llm_model, config.llm_api_key, config.llm_timeout)

View File

@ -6,7 +6,6 @@ import curses
import os
import config
from .agent import submit, log
from tools.coder import set_current_workspace
def _build_visual(buffer, max_chars):
@ -174,7 +173,7 @@ def workspace_popup(app, stdscr):
if ws:
resolved = os.path.abspath(ws)
if os.path.isdir(resolved):
set_current_workspace(resolved)
os.chdir(resolved)
log(app, "system", f"Workspace \u2192 {resolved}")
else:
log(app, "error", f"Invalid directory: {resolved}")

View File

@ -4,7 +4,7 @@
import curses
import json
from tools.coder import get_current_workspace
import os
# -- Color pair IDs (id 1-9, id 0 = default curses) --
C_HEADER = 1 # header bar: biru
@ -377,7 +377,7 @@ def draw_status(app, stdscr):
# Status bar di baris h-9: mode, workspace, session
h, w = app.h, app.w
y = h - 9
ws = get_current_workspace()
ws = os.getcwd()
session_tag = ""
if app.current_session:

View File

@ -193,7 +193,7 @@ class TelegramClient:
self._schedule_send(chat_id, 'Memulai sesi baru. Ada yang bisa dibantu?', reply_to_msg_id)
return
if 'roleplayer' in self._skill and chat_type == 'private':
if self._skill == 'roleplayer' and chat_type == 'private':
try:
results = ragroleplay.user_load(config.ragroleplay_db_path, unique_id=str(chat_id), character=personality.PERSONALITY.name)
if results:
@ -213,22 +213,20 @@ class TelegramClient:
)
session.add_message('system', context)
else:
tg_id_str = str(chat_id)
tg_user_str = tg_username or ''
tg_info = f'ID: {chat_id}'
if tg_username:
tg_info += f', @{tg_username}'
session.add_message('system',
f'[User Context: Pengguna baru — belum ada di database]\n'
f'[PENTING: Kamu BELUM mengenal user ini. WAJIB tanya nama sebagai pembuka. '
f'Setelah nama diketahui, simpan via users_store. '
f'Lanjutkan percakapan natural dan proaktif melengkapi data user lainnya di pesan berikutnya.]\n'
f'[Platform: Telegram]\n'
f'[PENTING: Saat memanggil users_store, gunakan nilai berikut:]\n'
f'[telegram_id: {tg_id_str}]\n'
f'[telegram_username: {tg_user_str}]')
f'[Platform: Telegram ({tg_info})]')
except Exception:
pass
session.add_message('user', body)
is_roleplay = 'roleplayer' in self._skill
is_roleplay = self._skill == 'roleplayer'
asyncio.run_coroutine_threadsafe(
self._app.bot.send_chat_action(chat_id=chat_id, action='typing'),
@ -287,7 +285,7 @@ class TelegramClient:
msg = 'Max iterations reached without final answer.'
self._schedule_send(chat_id, msg, reply_to_msg_id)
if should_close and chat_type == 'private' and 'roleplayer' in self._skill:
if should_close and chat_type == 'private' and self._skill == 'roleplayer':
print(f'[{_ts()}] Natural close triggered for {chat_id}', flush=True)
self._session_mgr.reset(str(chat_id))
else:

View File

@ -297,7 +297,7 @@ class XMPPClient(ClientXMPP):
self._schedule_send(jid, 'Memulai sesi baru. Ada yang bisa di bantu?')
return
if 'roleplayer' in self._skill:
if self._skill == 'roleplayer':
try:
results = ragroleplay.user_load(config.ragroleplay_db_path, unique_id=jid, character=personality.PERSONALITY.name)
if results:
@ -328,7 +328,7 @@ class XMPPClient(ClientXMPP):
session.add_message('user', body)
is_roleplay = 'roleplayer' in self._skill
is_roleplay = self._skill == 'roleplayer'
if not is_roleplay:
self._schedule_send(jid, f'> {body}\nThinking...')
@ -388,7 +388,7 @@ class XMPPClient(ClientXMPP):
self._schedule_send(jid, f'> {quote}\n{msg}', 'chat')
# Natural close: DM only, roleplayer only
if should_close and 'roleplayer' in self._skill:
if should_close and self._skill == 'roleplayer':
print(f'[{_ts()}] Natural close triggered for {jid}', flush=True)
self._session_mgr.reset(jid)
else:
@ -424,7 +424,7 @@ class XMPPClient(ClientXMPP):
my_name = personality.PERSONALITY.name
quote = f'[{nick}] {body}'
_is_roleplay = 'roleplayer' in self._skill
_is_roleplay = self._skill == 'roleplayer'
def on_tool_calls(tnames):
if not _is_roleplay:

View File

@ -3,19 +3,6 @@ import subprocess
import re
import glob as glob_module
# Global state to track the agent's current working directory
# This ensures that subsequent bash commands run in the correct directory
CURRENT_WORKSPACE = os.getcwd()
def get_current_workspace():
global CURRENT_WORKSPACE
return CURRENT_WORKSPACE
def set_current_workspace(path):
global CURRENT_WORKSPACE
CURRENT_WORKSPACE = path
os.chdir(path)
schema_read_file = {
"type": "function",
"function": {
@ -33,9 +20,7 @@ schema_read_file = {
def read_file(path):
try:
# Use absolute path if it's relative to CURRENT_WORKSPACE
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
with open(path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
return ''.join(f"{i+1}: {line}" for i, line in enumerate(lines))
except Exception as e:
@ -59,10 +44,9 @@ schema_write_file = {
def write_file(path, content):
try:
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
with open(full_path, 'w', encoding='utf-8') as f:
with open(path, 'w', encoding='utf-8') as f:
f.write(content)
return f"Successfully wrote to {full_path}"
return f"Successfully wrote to {path}"
except Exception as e:
return f"Error writing file: {str(e)}"
@ -85,15 +69,14 @@ schema_edit_file = {
def edit_file(path, old_string, new_string):
try:
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
with open(path, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
if old_string not in content:
return f"Error: old_string not found in {full_path}"
return f"Error: old_string not found in {path}"
new_content = content.replace(old_string, new_string)
with open(full_path, 'w', encoding='utf-8') as f:
with open(path, 'w', encoding='utf-8') as f:
f.write(new_content)
return f"Successfully edited {full_path}"
return f"Successfully edited {path}"
except Exception as e:
return f"Error editing file: {str(e)}"
@ -101,7 +84,7 @@ schema_run_bash = {
"type": "function",
"function": {
"name" : "run_bash",
"description" : "Run a bash command. Returns stdout, stderr, and return code. Commands are executed relative to the current workspace.",
"description" : "Run a bash command. Returns stdout, stderr, and return code.",
"parameters" : {
"type" : "object",
"properties" : {
@ -114,9 +97,7 @@ schema_run_bash = {
def run_bash(command):
try:
# Prepend 'cd' to the command to ensure it runs in the correct workspace
full_command = f"cd {get_current_workspace()} && {command}"
result = subprocess.run(full_command, shell=True, capture_output=True, text=True, timeout=30)
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
except Exception as e:
return f"Error running command: {str(e)}"
@ -140,15 +121,12 @@ schema_search_code = {
def search_code(pattern, search_type, path="."):
try:
# Resolve path relative to workspace
search_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
if search_type == "glob":
files = glob_module.glob(f"{search_path}/**/{pattern}", recursive=True)
files = glob_module.glob(f"{path}/**/{pattern}", recursive=True)
return "\n".join(files) if files else "No files found"
elif search_type == "content":
results = []
for root, dirs, files in os.walk(search_path):
for root, dirs, files in os.walk(path):
for file in files:
file_path = os.path.join(root, file)
try:
@ -184,39 +162,8 @@ schema_git_operation = {
def git_operation(args):
try:
# Execute git command from the current workspace
full_command = ["git", *args]
result = subprocess.run(full_command, cwd=get_current_workspace(), capture_output=True, text=True, timeout=10)
result = subprocess.run(["git", *args], capture_output=True, text=True, timeout=10)
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
except Exception as e:
return f"Error running git command: {str(e)}"
schema_workspace_change = {
"type": "function",
"function": {
"name": "workspace_change",
"description": "Change the current working directory of the agent session. Supports tilde (~) expansion for the user home directory (/home/aji/).",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "The target directory path to change to."}
},
"required": ["path"]
}
}
}
def workspace_change(path):
try:
if path.startswith("~"):
target_path = os.path.expanduser(path)
else:
target_path = os.path.abspath(path)
if os.path.exists(target_path) and os.path.isdir(target_path):
set_current_workspace(target_path)
return f"Successfully changed workspace to: {target_path}"
else:
return f"Error: The path {target_path} does not exist or is not a directory."
except Exception as e:
return f"Error changing workspace: {str(e)}"

View File

@ -684,10 +684,6 @@ def user_load(query_name=None, unique_id=None, persona_keyword=None, user_id=Non
f"Character: {row.get('character', '-') or '-'}\n"
f"Salutation: {row.get('salutation', '-') or '-'}\n"
f"Persona: {row['persona']}\n"
f"Telegram ID: {row.get('telegram_id', '-') or '-'}\n"
f"Telegram Username: {row.get('telegram_username', '-') or '-'}\n"
f"XAMPP Username: {row.get('xampp_username', '-') or '-'}\n"
f"Alias: {row.get('alias', '-') or '-'}\n"
)
output.append(user_info)