multacd 1.0.0__tar.gz

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 (187) hide show
  1. multacd-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. multacd-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md +12 -0
  3. multacd-1.0.0/.github/pull_request_template.md +22 -0
  4. multacd-1.0.0/.github/workflows/release.yml +106 -0
  5. multacd-1.0.0/.github/workflows/test.yml +29 -0
  6. multacd-1.0.0/.gitignore +37 -0
  7. multacd-1.0.0/CHANGELOG.md +43 -0
  8. multacd-1.0.0/CONTRIBUTING.md +36 -0
  9. multacd-1.0.0/DESIGN.md +992 -0
  10. multacd-1.0.0/LICENSE +21 -0
  11. multacd-1.0.0/PKG-INFO +94 -0
  12. multacd-1.0.0/README.md +57 -0
  13. multacd-1.0.0/briefing/__init__.py +6 -0
  14. multacd-1.0.0/briefing/generator.py +125 -0
  15. multacd-1.0.0/briefing/privacy.py +112 -0
  16. multacd-1.0.0/briefing/sources/__init__.py +3 -0
  17. multacd-1.0.0/briefing/sources/git_source.py +77 -0
  18. multacd-1.0.0/briefing/sources/news_source.py +76 -0
  19. multacd-1.0.0/briefing/sources/todo_source.py +69 -0
  20. multacd-1.0.0/core/__init__.py +0 -0
  21. multacd-1.0.0/core/agent_loop.py +410 -0
  22. multacd-1.0.0/core/codebase.py +318 -0
  23. multacd-1.0.0/core/config.py +340 -0
  24. multacd-1.0.0/core/llm_client.py +347 -0
  25. multacd-1.0.0/core/mode_manager.py +342 -0
  26. multacd-1.0.0/core/permissions.py +284 -0
  27. multacd-1.0.0/core/plugin_loader.py +194 -0
  28. multacd-1.0.0/core/prompt_composer.py +126 -0
  29. multacd-1.0.0/core/research/__init__.py +0 -0
  30. multacd-1.0.0/core/research/bus.py +41 -0
  31. multacd-1.0.0/core/research/orchestrator.py +638 -0
  32. multacd-1.0.0/core/research/query_generator.py +199 -0
  33. multacd-1.0.0/core/updater.py +187 -0
  34. multacd-1.0.0/core/version.py +20 -0
  35. multacd-1.0.0/daemon/__init__.py +3 -0
  36. multacd-1.0.0/daemon/ipc.py +127 -0
  37. multacd-1.0.0/daemon/process.py +205 -0
  38. multacd-1.0.0/docs/configuration.md +43 -0
  39. multacd-1.0.0/docs/index.md +10 -0
  40. multacd-1.0.0/docs/install.md +35 -0
  41. multacd-1.0.0/docs/mkdocs.yml +34 -0
  42. multacd-1.0.0/docs/modes/coding.md +10 -0
  43. multacd-1.0.0/docs/modes/personal.md +12 -0
  44. multacd-1.0.0/docs/modes/research.md +9 -0
  45. multacd-1.0.0/docs/plugins.md +25 -0
  46. multacd-1.0.0/docs/quickstart.md +24 -0
  47. multacd-1.0.0/main.py +195 -0
  48. multacd-1.0.0/memory/__init__.py +0 -0
  49. multacd-1.0.0/memory/context.py +117 -0
  50. multacd-1.0.0/memory/store.py +78 -0
  51. multacd-1.0.0/multacd.spec +70 -0
  52. multacd-1.0.0/plan/PLAN-phase1.md +806 -0
  53. multacd-1.0.0/plan/PLAN-phase2.md +1058 -0
  54. multacd-1.0.0/plan/PLAN-phase3.md +1103 -0
  55. multacd-1.0.0/plan/PLAN-phase4.md +1326 -0
  56. multacd-1.0.0/plan/PLAN-phase5.md +1334 -0
  57. multacd-1.0.0/plugins/README.md +75 -0
  58. multacd-1.0.0/plugins/example_plugin.py +67 -0
  59. multacd-1.0.0/pyproject.toml +112 -0
  60. multacd-1.0.0/requirements.txt +13 -0
  61. multacd-1.0.0/roadmap/ROADMAP.md +201 -0
  62. multacd-1.0.0/scheduler/__init__.py +3 -0
  63. multacd-1.0.0/scheduler/engine.py +227 -0
  64. multacd-1.0.0/scheduler/jobs/__init__.py +37 -0
  65. multacd-1.0.0/scheduler/jobs/briefing_job.py +36 -0
  66. multacd-1.0.0/scheduler/jobs/research_job.py +43 -0
  67. multacd-1.0.0/scripts/install.ps1 +49 -0
  68. multacd-1.0.0/scripts/install.sh +104 -0
  69. multacd-1.0.0/search_providers/__init__.py +41 -0
  70. multacd-1.0.0/search_providers/__main__.py +43 -0
  71. multacd-1.0.0/search_providers/base.py +137 -0
  72. multacd-1.0.0/search_providers/brave.py +87 -0
  73. multacd-1.0.0/search_providers/duckduckgo.py +114 -0
  74. multacd-1.0.0/search_providers/exa.py +99 -0
  75. multacd-1.0.0/search_providers/serpapi.py +85 -0
  76. multacd-1.0.0/search_providers/tavily.py +79 -0
  77. multacd-1.0.0/soul.md +30 -0
  78. multacd-1.0.0/tests/__init__.py +0 -0
  79. multacd-1.0.0/tests/conftest.py +70 -0
  80. multacd-1.0.0/tests/integration/__init__.py +0 -0
  81. multacd-1.0.0/tests/integration/test_agent_loop.py +64 -0
  82. multacd-1.0.0/tests/test_selftests.py +116 -0
  83. multacd-1.0.0/tests/unit/__init__.py +0 -0
  84. multacd-1.0.0/tests/unit/test_binary.py +42 -0
  85. multacd-1.0.0/tests/unit/test_config.py +57 -0
  86. multacd-1.0.0/tests/unit/test_docs.py +34 -0
  87. multacd-1.0.0/tests/unit/test_entrypoint.py +51 -0
  88. multacd-1.0.0/tests/unit/test_github.py +20 -0
  89. multacd-1.0.0/tests/unit/test_installer.py +109 -0
  90. multacd-1.0.0/tests/unit/test_permissions.py +56 -0
  91. multacd-1.0.0/tests/unit/test_plugin_loader.py +111 -0
  92. multacd-1.0.0/tests/unit/test_privacy_filter.py +38 -0
  93. multacd-1.0.0/tests/unit/test_search_providers/__init__.py +0 -0
  94. multacd-1.0.0/tests/unit/test_search_providers/test_tavily.py +25 -0
  95. multacd-1.0.0/tests/unit/test_tools/__init__.py +0 -0
  96. multacd-1.0.0/tests/unit/test_tools/test_bash.py +20 -0
  97. multacd-1.0.0/tests/unit/test_tools/test_read_file.py +24 -0
  98. multacd-1.0.0/tests/unit/test_tools/test_web_scrape.py +39 -0
  99. multacd-1.0.0/tests/unit/test_tools/test_write_file.py +22 -0
  100. multacd-1.0.0/tests/unit/test_updater.py +63 -0
  101. multacd-1.0.0/tg/__init__.py +7 -0
  102. multacd-1.0.0/tg/access_control.py +131 -0
  103. multacd-1.0.0/tg/agent.py +268 -0
  104. multacd-1.0.0/tg/bot.py +79 -0
  105. multacd-1.0.0/tg/file_handler.py +152 -0
  106. multacd-1.0.0/tg/formatter.py +94 -0
  107. multacd-1.0.0/tg/handlers.py +358 -0
  108. multacd-1.0.0/tools/__init__.py +0 -0
  109. multacd-1.0.0/tools/agent/__init__.py +0 -0
  110. multacd-1.0.0/tools/agent/ask.py +35 -0
  111. multacd-1.0.0/tools/agent/skill.py +67 -0
  112. multacd-1.0.0/tools/agent/task.py +33 -0
  113. multacd-1.0.0/tools/agent/todo_write.py +54 -0
  114. multacd-1.0.0/tools/code/__init__.py +0 -0
  115. multacd-1.0.0/tools/code/lint_python.py +144 -0
  116. multacd-1.0.0/tools/code/run_python.py +169 -0
  117. multacd-1.0.0/tools/code/run_tests.py +156 -0
  118. multacd-1.0.0/tools/codebase/__init__.py +0 -0
  119. multacd-1.0.0/tools/codebase/scan_codebase.py +28 -0
  120. multacd-1.0.0/tools/common.py +18 -0
  121. multacd-1.0.0/tools/filesystem/__init__.py +0 -0
  122. multacd-1.0.0/tools/filesystem/apply_patch.py +89 -0
  123. multacd-1.0.0/tools/filesystem/delete_file.py +36 -0
  124. multacd-1.0.0/tools/filesystem/edit_file.py +42 -0
  125. multacd-1.0.0/tools/filesystem/glob.py +35 -0
  126. multacd-1.0.0/tools/filesystem/grep.py +78 -0
  127. multacd-1.0.0/tools/filesystem/list_dir.py +46 -0
  128. multacd-1.0.0/tools/filesystem/move_file.py +38 -0
  129. multacd-1.0.0/tools/filesystem/multi_edit.py +46 -0
  130. multacd-1.0.0/tools/filesystem/read_file.py +42 -0
  131. multacd-1.0.0/tools/filesystem/read_many_files.py +35 -0
  132. multacd-1.0.0/tools/filesystem/write_file.py +37 -0
  133. multacd-1.0.0/tools/git/__init__.py +0 -0
  134. multacd-1.0.0/tools/git/_helper.py +44 -0
  135. multacd-1.0.0/tools/git/git_add.py +21 -0
  136. multacd-1.0.0/tools/git/git_branch.py +13 -0
  137. multacd-1.0.0/tools/git/git_checkout.py +25 -0
  138. multacd-1.0.0/tools/git/git_commit.py +21 -0
  139. multacd-1.0.0/tools/git/git_diff.py +18 -0
  140. multacd-1.0.0/tools/git/git_log.py +17 -0
  141. multacd-1.0.0/tools/git/git_merge.py +140 -0
  142. multacd-1.0.0/tools/git/git_pull.py +17 -0
  143. multacd-1.0.0/tools/git/git_push.py +92 -0
  144. multacd-1.0.0/tools/git/git_status.py +13 -0
  145. multacd-1.0.0/tools/memory/__init__.py +0 -0
  146. multacd-1.0.0/tools/memory/forget.py +33 -0
  147. multacd-1.0.0/tools/memory/recall.py +35 -0
  148. multacd-1.0.0/tools/memory/remember.py +34 -0
  149. multacd-1.0.0/tools/personal/__init__.py +3 -0
  150. multacd-1.0.0/tools/personal/cancel_job.py +55 -0
  151. multacd-1.0.0/tools/personal/daemon_status.py +64 -0
  152. multacd-1.0.0/tools/personal/generate_briefing.py +93 -0
  153. multacd-1.0.0/tools/personal/get_jobs.py +52 -0
  154. multacd-1.0.0/tools/personal/schedule_job.py +77 -0
  155. multacd-1.0.0/tools/personal/send_telegram.py +171 -0
  156. multacd-1.0.0/tools/personal/user_manager.py +103 -0
  157. multacd-1.0.0/tools/registry.py +286 -0
  158. multacd-1.0.0/tools/research/__init__.py +0 -0
  159. multacd-1.0.0/tools/research/deep_research.py +148 -0
  160. multacd-1.0.0/tools/research/export_research.py +146 -0
  161. multacd-1.0.0/tools/research/quick_research.py +159 -0
  162. multacd-1.0.0/tools/research/web_scrape.py +172 -0
  163. multacd-1.0.0/tools/research/web_search.py +176 -0
  164. multacd-1.0.0/tools/shell/__init__.py +0 -0
  165. multacd-1.0.0/tools/shell/bash.py +60 -0
  166. multacd-1.0.0/tools/web/__init__.py +0 -0
  167. multacd-1.0.0/tools/web/web_fetch.py +66 -0
  168. multacd-1.0.0/tui/__init__.py +0 -0
  169. multacd-1.0.0/tui/app.py +113 -0
  170. multacd-1.0.0/tui/icons.py +114 -0
  171. multacd-1.0.0/tui/screens/__init__.py +0 -0
  172. multacd-1.0.0/tui/screens/main_screen.py +424 -0
  173. multacd-1.0.0/tui/screens/setup_wizard.py +483 -0
  174. multacd-1.0.0/tui/themes/__init__.py +133 -0
  175. multacd-1.0.0/tui/themes/__main__.py +17 -0
  176. multacd-1.0.0/tui/widgets/__init__.py +0 -0
  177. multacd-1.0.0/tui/widgets/chat_panel.py +83 -0
  178. multacd-1.0.0/tui/widgets/confirm_dialog.py +52 -0
  179. multacd-1.0.0/tui/widgets/diff_viewer.py +68 -0
  180. multacd-1.0.0/tui/widgets/file_tree.py +121 -0
  181. multacd-1.0.0/tui/widgets/input_bar.py +92 -0
  182. multacd-1.0.0/tui/widgets/permission_popup.py +172 -0
  183. multacd-1.0.0/tui/widgets/slash_palette.py +131 -0
  184. multacd-1.0.0/tui/widgets/sources_panel.py +201 -0
  185. multacd-1.0.0/tui/widgets/status_bar.py +70 -0
  186. multacd-1.0.0/tui/widgets/thinking_bar.py +106 -0
  187. multacd-1.0.0/tui/widgets/tool_activity.py +163 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Laporkan sesuatu yang tidak berjalan dengan benar
4
+ ---
5
+
6
+ **Deskripsi bug:**
7
+
8
+ **Langkah reproduce:**
9
+
10
+ 1.
11
+ 2.
12
+ 3.
13
+
14
+ **Expected:**
15
+
16
+ **Actual:**
17
+
18
+ **Environment:**
19
+
20
+ - OS:
21
+ - multacd version (`multacd --version`):
22
+ - Python version:
23
+ - LLM provider:
24
+
25
+ **Log / error output:**
26
+
27
+ ```text
28
+
29
+ ```
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Usulkan fitur baru
4
+ ---
5
+
6
+ **Fitur yang diusulkan:**
7
+
8
+ **Masalah yang diselesaikan:**
9
+
10
+ **Solusi yang kamu bayangkan:**
11
+
12
+ **Alternatif lain yang dipertimbangkan:**
@@ -0,0 +1,22 @@
1
+ ## Deskripsi
2
+
3
+ [Jelaskan apa yang diubah dan kenapa]
4
+
5
+ ## Jenis perubahan
6
+
7
+ - [ ] Bug fix
8
+ - [ ] Fitur baru
9
+ - [ ] Breaking change
10
+ - [ ] Dokumentasi
11
+
12
+ ## Testing
13
+
14
+ - [ ] `pytest tests/` hijau
15
+ - [ ] `ruff check .` bersih
16
+ - [ ] Test manual di [platform]
17
+
18
+ ## Checklist
19
+
20
+ - [ ] Commit message `<EMOJI> <TYPE>: Pesan` (maks 50 karakter)
21
+ - [ ] Dokumentasi diupdate (kalau perlu)
22
+ - [ ] `CHANGELOG.md` ([Unreleased]) diupdate
@@ -0,0 +1,106 @@
1
+ # release.yml — tag v* → test → build binary → PyPI + GitHub Release.
2
+ # Sengaja satu file (bukan test/build/release terpisah seperti sketsa awal):
3
+ # `needs` antar-workflow tidak valid di GitHub Actions, jadi gating
4
+ # test → build → publish diekspresikan sebagai job dalam workflow ini.
5
+ # test.yml terpisah tetap ada buat CI tiap push/PR.
6
+ #
7
+ # Prasyarat manual sebelum Step 10: tambah secret PYPI_TOKEN
8
+ # (https://pypi.org → API token) di repo settings.
9
+
10
+ name: Release
11
+
12
+ on:
13
+ push:
14
+ tags:
15
+ - "v*"
16
+
17
+ jobs:
18
+ test:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+ - run: pip install -e ".[dev]"
26
+ - run: pytest tests/unit/ -v
27
+
28
+ build:
29
+ needs: [test]
30
+ strategy:
31
+ fail-fast: false
32
+ matrix:
33
+ include:
34
+ - os: ubuntu-latest
35
+ output: multacd-linux-x86_64
36
+ - os: ubuntu-24.04-arm
37
+ output: multacd-linux-aarch64
38
+ - os: macos-latest
39
+ output: multacd-macos-arm64
40
+ - os: windows-latest
41
+ output: multacd-windows-x86_64.exe
42
+
43
+ runs-on: ${{ matrix.os }}
44
+
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ - uses: actions/setup-python@v5
48
+ with:
49
+ python-version: "3.12"
50
+ - run: pip install pyinstaller -e .
51
+ - run: pyinstaller multacd.spec --noconfirm
52
+ - run: mv dist/multacd* dist/${{ matrix.output }}
53
+ if: runner.os != 'Windows'
54
+ - run: Move-Item dist/multacd.exe dist/${{ matrix.output }}
55
+ if: runner.os == 'Windows'
56
+ shell: pwsh
57
+ - run: dist/${{ matrix.output }} --version
58
+ - uses: actions/upload-artifact@v4
59
+ with:
60
+ name: ${{ matrix.output }}
61
+ path: dist/${{ matrix.output }}
62
+
63
+ publish:
64
+ needs: [test, build]
65
+ runs-on: ubuntu-latest
66
+ permissions:
67
+ contents: write
68
+
69
+ steps:
70
+ - uses: actions/checkout@v4
71
+ - uses: actions/setup-python@v5
72
+ with:
73
+ python-version: "3.12"
74
+ - uses: actions/download-artifact@v4
75
+ with:
76
+ path: dist_bin/
77
+ - run: pip install build twine
78
+ - run: python -m build --outdir dist_pkg/
79
+ - run: twine upload dist_pkg/*
80
+ env:
81
+ TWINE_USERNAME: __token__
82
+ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
83
+ - uses: softprops/action-gh-release@v2
84
+ with:
85
+ files: dist_bin/**/*
86
+ generate_release_notes: true
87
+ body: |
88
+ ## Install
89
+
90
+ Linux / macOS / Termux:
91
+
92
+ ```bash
93
+ curl -fsSL https://raw.githubusercontent.com/prototypeall850-creator/multacd/main/scripts/install.sh | bash
94
+ ```
95
+
96
+ Windows (PowerShell):
97
+
98
+ ```powershell
99
+ irm https://raw.githubusercontent.com/prototypeall850-creator/multacd/main/scripts/install.ps1 | iex
100
+ ```
101
+
102
+ Via pip:
103
+
104
+ ```bash
105
+ pip install multacd
106
+ ```
@@ -0,0 +1,29 @@
1
+ # test.yml — unit test tiap push/PR (Phase 5 Step 9).
2
+ # Integration/self-test modul (tests/test_selftests.py) sengaja tidak ikut:
3
+ # lambat (~1 mnt) dan butuh LLM key untuk sebagian flow — jalan lokal.
4
+
5
+ name: Tests
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ pull_request:
11
+ branches: [main]
12
+
13
+ jobs:
14
+ test:
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, macos-latest, windows-latest]
19
+ python-version: ["3.11", "3.12", "3.13"]
20
+
21
+ runs-on: ${{ matrix.os }}
22
+
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ - run: pip install -e ".[dev]"
29
+ - run: pytest tests/unit/ -v
@@ -0,0 +1,37 @@
1
+ # Python
2
+ .venv/
3
+ *.pyc
4
+ __pycache__/
5
+ *.pyo
6
+ *.pyd
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+
11
+ # Env & secrets
12
+ .env
13
+ .env.*
14
+ *.key
15
+ *.pem
16
+
17
+ # multacd local data (personal, jangan di-commit)
18
+ # NOTE: data user tersimpan di ~/.multacd/, bukan di repo.
19
+ # Baris di bawah ini jaga-jaga kalau ada yang nyasar ke repo.
20
+ .memory.db
21
+ todo.md
22
+
23
+ # OS / editor
24
+ .DS_Store
25
+ Thumbs.db
26
+ .vscode/
27
+ .idea/
28
+ *.swp
29
+ *.swo
30
+
31
+ # pytest / coverage
32
+ .pytest_cache/
33
+ .coverage
34
+ htmlcov/
35
+
36
+ # mkdocs build output
37
+ site/
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ Format: Keep a Changelog. Versi: Semantic Versioning.
4
+
5
+ ## [1.0.0] - 2026-09-11
6
+
7
+ Rilis stabil pertama: coding + research + personal agent dalam satu TUI,
8
+ install via binary, pip, atau source. Docs: https://prototypeall850-creator.github.io/multacd/
9
+
10
+ ### Added (Phase 5 — Polish & Distribution)
11
+
12
+ - Test suite: unit + integration (`pytest tests/`), 121 tests
13
+ - Plugin system (`~/.multacd/plugins/`) + contoh + panduan
14
+ - Auto-update: cek PyPI harian, notif TUI, `multacd update`
15
+ - Entry point `multacd` via pip (`pip install -e .`), versi tunggal
16
+ - Binary PyInstaller onefile + `multacd.spec`
17
+ - Installer `scripts/install.sh` (Linux/macOS/Termux) + `install.ps1` (Windows)
18
+ - Docs MkDocs (GitHub Pages), README user-facing, CONTRIBUTING
19
+
20
+ ### Added (Phase 1 — Foundation)
21
+
22
+ - Config BYOK, ReAct loop, permission auto/ask, tool dasar,
23
+ memory SQLite, TUI Textual, cross-platform
24
+
25
+ ### Added (Phase 2 — Coding Agent)
26
+
27
+ - soul.md, codebase scan, mode switching, run/lint/test,
28
+ smart git flow, TUI widgets (tree, diff, palette, permission bar)
29
+
30
+ ### Added (Phase 3 — Research Agent)
31
+
32
+ - 5 search provider BYOK, web_scrape, quick/deep research,
33
+ sources panel (Ctrl+R), export `.md`
34
+
35
+ ### Added (Phase 4 — Personal Agent)
36
+
37
+ - Bot Telegram (whitelist, konfirmasi Y/N, file up/down),
38
+ scheduler cron persist, daily briefing + privacy filter,
39
+ daemon mode, `/personal` di TUI
40
+
41
+ ## [0.9.0] - 2026-09-11
42
+
43
+ - Pre-1.0 dev: entry point `multacd` + versi tunggal (internal, tak dirilis).
@@ -0,0 +1,36 @@
1
+ # Contributing to multacd
2
+
3
+ ## Setup development
4
+
5
+ ```bash
6
+ git clone https://github.com/prototypeall850-creator/multacd && cd multacd
7
+ python -m venv .venv && source .venv/bin/activate
8
+ pip install -e ".[dev]"
9
+ ```
10
+
11
+ ## Menjalankan tests
12
+
13
+ ```bash
14
+ pytest tests/ # semua (butuh ~1 menit)
15
+ pytest tests/unit/ # cepat, tanpa self-test modul
16
+ ./.venv/bin/ruff check . # lint wajib hijau sebelum commit
17
+ ```
18
+
19
+ Setiap modul inti punya self-test (`python -m <modul>`) yang ikut
20
+ dijalankan `tests/test_selftests.py` — pola ini jangan dihapus.
21
+
22
+ ## Membuat plugin
23
+
24
+ Lihat `plugins/README.md` + `docs/plugins.md`. Test plugin:
25
+
26
+ ```bash
27
+ cp pluginmu.py ~/.multacd/plugins/ && multacd
28
+ ```
29
+
30
+ ## Pull request
31
+
32
+ - Fork → branch `feature/nama-fitur` → PR ke `main`
33
+ - Commit: `<EMOJI> <TYPE>: Pesan` (maks 50 karakter — cth `🔌 NEW: ...`)
34
+ - Wajib: `ruff` bersih + `pytest tests/` hijau
35
+ - Update `CHANGELOG.md` ([Unreleased]) kalau ubah perilaku user
36
+ - Issue kecil/bug? Buka issue dulu biar tercatat