hanoman 0.1.26 → 0.1.28

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/cli.js CHANGED
@@ -16,165 +16,6 @@ var init_types = __esm({
16
16
  }
17
17
  });
18
18
 
19
- // ../runner/src/reverse-standard.ts
20
- var init_reverse_standard = __esm({
21
- "../runner/src/reverse-standard.ts"() {
22
- "use strict";
23
- }
24
- });
25
-
26
- // ../runner/src/verify-scope.ts
27
- var CHANGED;
28
- var init_verify_scope = __esm({
29
- "../runner/src/verify-scope.ts"() {
30
- "use strict";
31
- CHANGED = [
32
- "Scope verifikasi: HANYA yang berubah. Mesin ini menjalankan beberapa sesi sekaligus \u2014",
33
- "memverifikasi seluruh project menghabiskan RAM & CPU yang sedang dipakai sesi lain.",
34
- "",
35
- "Berkas yang berubah di worktree ini:",
36
- '`git diff --name-only "$HANOMAN_BASE_SHA"...HEAD` (yang sudah di-commit) dan',
37
- "`git status --porcelain` (yang belum).",
38
- "",
39
- "- Test: jalankan HANYA test yang berkaitan dengan berkas itu. Repo vitest:",
40
- ' `pnpm vitest --run --changed "$HANOMAN_BASE_SHA"` (vitest sendiri menurunkan berkas yang',
41
- " berubah dari git, termasuk yang belum di-commit), atau `pnpm vitest related --run <berkas\u2026>`,",
42
- " atau sebut path berkas test-nya langsung. Stack lain: `pytest <path>`, `go test ./paket/...`.",
43
- " JANGAN `pnpm test` atau `vitest run` polos \u2014 itu seluruh suite.",
44
- " Jebakan: `--changed` menyalakan `passWithNoTests`, jadi nol test TERLIHAT hijau. Pastikan",
45
- ' test-nya memang berjalan, jangan menerima "no test files" sebagai bukti.',
46
- "- Typecheck: hanya paket yang tersentuh (mis. `pnpm --filter ./server typecheck`).",
47
- " JANGAN `pnpm -r typecheck` \u2014 itu menyalakan satu proses tsc per paket sekaligus.",
48
- "- Lint: hanya berkas yang berubah, bukan seluruh repo.",
49
- "- Build penuh: hanya bila yang kamu ubah memang soal build/bundling.",
50
- "- Boot server + curl / smoke end-to-end: hanya bila task ini menyentuh endpoint atau perilaku",
51
- " runtime-nya, sekali di akhir \u2014 bukan rutin tiap task.",
52
- "- Membereskan proses nyangkut: JANGAN `pkill -f <pola>` maupun `killall`. Prompt setiap sesi",
53
- " hidup di ARGV proses agennya, jadi pola seperti `vitest`/`tsc`/`node` ikut mencocoki agen sesi",
54
- " LAIN di mesin ini dan mematikannya di tengah kerja \u2014 dan `pkill` mengecualikan leluhurnya",
55
- " sendiri, jadi yang mati selalu sesi tetangga, bukan sesimu. Bunuh per-PID: `ps`/`lsof -ti:<port>`",
56
- " \u2192 `kill <pid>`; atau sempitkan polanya sampai memuat path worktree-mu sendiri.",
57
- "",
58
- "Suite penuh, lint penuh, dan build penuh adalah tugas MANUSIA sebelum merge, bukan tugas sesi.",
59
- "Pengecualian yang kamu putuskan sendiri: bila perubahanmu memang berdampak luas (mengubah",
60
- "tipe/kontrak bersama, skema, atau berkas yang diimpor banyak modul), perluas scope seperlunya",
61
- "dan katakan alasannya. Ini panduan biaya, bukan larangan."
62
- ].join("\n");
63
- }
64
- });
65
-
66
- // ../runner/src/code-style.ts
67
- var CODE_STYLE_CLAUSE;
68
- var init_code_style = __esm({
69
- "../runner/src/code-style.ts"() {
70
- "use strict";
71
- CODE_STYLE_CLAUSE = [
72
- "Gaya kode \u2014 berlaku setiap kali kamu menulis atau mengubah kode:",
73
- "- Tulis kode yang rapi dan mengikuti idiom, penamaan, serta struktur kode di sekitarnya.",
74
- " Kodemu harus terbaca seperti kode yang sudah ada di berkas itu, bukan seperti tempelan.",
75
- "- Jangan menulis komentar yang cuma mengulang apa yang sudah dinyatakan kode.",
76
- "- Komentar hanya untuk hal yang TIDAK terbaca dari kode: alasan/why sebuah keputusan,",
77
- " trade-off yang diambil, workaround beserta rujukan SPEC/ADR-nya, atau invariant yang tak",
78
- " kelihatan. Komentar semacam itu justru berharga \u2014 jangan ikut dibuang.",
79
- "- Jangan menambahkan komentar pembatas seksi, header berhiasan, atau narasi langkah demi langkah.",
80
- "- Jangan meninggalkan kode mati atau kode yang dikomentari. Hapus saja; riwayat git yang menyimpannya."
81
- ].join("\n");
82
- }
83
- });
84
-
85
- // ../runner/src/goal-spec.ts
86
- var init_goal_spec = __esm({
87
- "../runner/src/goal-spec.ts"() {
88
- "use strict";
89
- }
90
- });
91
-
92
- // ../runner/src/prompt.ts
93
- var ESCALATION_CONTRACT, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
94
- var init_prompt = __esm({
95
- "../runner/src/prompt.ts"() {
96
- "use strict";
97
- init_reverse_standard();
98
- init_verify_scope();
99
- init_code_style();
100
- init_goal_spec();
101
- ESCALATION_CONTRACT = [
102
- "REKOMENDASI ESKALASI (wajib, terbaca mesin). Di bagian akhir dokumen audit, tulis bagian",
103
- "`## Rekomendasi eskalasi` berisi penjelasan singkat untuk manusia, LALU tepat SATU blok ```json",
104
- "berbentuk persis seperti ini (satu-satunya blok json di dokumen itu):",
105
- "",
106
- "```json",
107
- '{ "escalation": { "target": "none|qa|brief|prd", "reason": "<alasan singkat>",',
108
- ' "alternatives": ["<target lain yang masuk akal>"],',
109
- ' "prefill": { "title": "", "context": "", "outcome": "", "constraints": "", "severity": "", "steps": "" } } }',
110
- "```",
111
- "",
112
- "Pilih `target` dari hasil auditmu, bukan default:",
113
- '- "qa" \u2014 bug / regresi / perilaku salah yang perlu diperbaiki. Isi `prefill.severity`',
114
- " (critical|major|minor) dan `prefill.steps` (langkah reproduksi).",
115
- '- "brief" \u2014 kebutuhan/fitur yang bentuknya sudah jelas dan cakupannya satu backlog.',
116
- " Isi `prefill.title`, `prefill.context`, `prefill.outcome`.",
117
- '- "prd" \u2014 kebutuhan produk yang besar, ambigu, atau lintas modul sehingga perlu dokumen PRD',
118
- " lebih dulu. Isi `prefill.title`, `prefill.context`, `prefill.outcome`.",
119
- '- "none" \u2014 pertanyaannya sudah terjawab; tak perlu perbaikan maupun fitur baru.',
120
- "",
121
- "`alternatives` boleh array kosong. Jangan menulis blok json lain di dokumen itu."
122
- ].join("\n");
123
- REVERSE_PHASE_GUIDE = [
124
- "- Scan: baca source code \u2014 stack, arsitektur, data model, API surface, perilaku domain. Belum menulis docs.",
125
- "- Docs teknis: tulis kategori yang bisa diturunkan dari kode (architecture, requirements + EARS dari perilaku nyata, adr ber-Status accepted (reverse-engineered), operations, security, design-system/frontend bila relevan). Isi lengkap dan spesifik, bukan kerangka.",
126
- "- Wawancara: untuk product, business, brand, research, entrypoints \u2014 ajukan SATU pertanyaan per giliran ke manusia di terminal ini, tunggu jawabannya, isi docs dari jawaban. Jangan mengarang. Topik tanpa jawaban tandai: Status: draft \u2014 menunggu input.",
127
- "- Konvensi & index: tulis internal/docs/README.md (index bernomor lengkap), CLAUDE.md, AGENTS.md, .claude/settings.json + .claude/hooks/ensure-docs-updated.py persis seperti STANDAR DOCS.",
128
- "- Serah terima: pastikan setiap berkas docs terdaftar di index, lalu tulis ringkasan hasil + daftar pertanyaan yang belum terjawab ke terminal."
129
- ].join("\n");
130
- SCAFFOLD_PHASE_GUIDE = [
131
- "- Brainstorm: perdalam IDE project (di bawah) jadi masalah, pengguna, scope, dan metrik sukses. Ajukan SATU pertanyaan per giliran ke manusia di terminal ini, tunggu jawabannya. Jangan mengarang; topik yang belum dijawab tandai sebagai open question.",
132
- "- Objective: kunci SATU MVP objective yang terukur dari hasil brainstorm, tulis ringkas di docs.",
133
- "- Doc index: tulis SELURUH internal/docs/** dari ide+objective+jawaban, mengikuti STANDAR DOCS di bawah \u2014 entrypoints, product, business, requirements (+EARS dari perilaku yang diinginkan), research, architecture (stack/data-model/api-contract/nfr), adr awal (Status accepted), design-system/frontend bila ada UI, operations, security, plus README index bernomor + CLAUDE.md + AGENTS.md + Stop hook. Lengkap dan spesifik terhadap ide ini, BUKAN kerangka."
134
- ].join("\n");
135
- }
136
- });
137
-
138
- // ../runner/src/git.ts
139
- var init_git = __esm({
140
- "../runner/src/git.ts"() {
141
- "use strict";
142
- }
143
- });
144
-
145
- // ../runner/src/settings.ts
146
- var init_settings = __esm({
147
- "../runner/src/settings.ts"() {
148
- "use strict";
149
- }
150
- });
151
-
152
- // ../runner/src/goal.ts
153
- var init_goal = __esm({
154
- "../runner/src/goal.ts"() {
155
- "use strict";
156
- init_prompt();
157
- init_goal_spec();
158
- }
159
- });
160
-
161
- // ../runner/src/codex-settings.ts
162
- var init_codex_settings = __esm({
163
- "../runner/src/codex-settings.ts"() {
164
- "use strict";
165
- init_prompt();
166
- }
167
- });
168
-
169
- // ../runner/src/agent-cli.ts
170
- var init_agent_cli = __esm({
171
- "../runner/src/agent-cli.ts"() {
172
- "use strict";
173
- init_settings();
174
- init_codex_settings();
175
- }
176
- });
177
-
178
19
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
179
20
  var util, objectUtil, ZodParsedType, getParsedType;
180
21
  var init_util = __esm({
@@ -4319,6 +4160,120 @@ var init_spec_source = __esm({
4319
4160
  }
4320
4161
  });
4321
4162
 
4163
+ // ../shared/src/method-catalog.ts
4164
+ function resolveMethod(id) {
4165
+ return (id ? METHODS[id] : void 0) ?? METHODS[DEFAULT_METHOD];
4166
+ }
4167
+ var DEFAULT_METHOD, VERIFICATION_GATE, MATT_CLAUSE, METHODS, METHOD_IDS, uniq, PLAN_DIRS, SPEC_DIRS;
4168
+ var init_method_catalog = __esm({
4169
+ "../shared/src/method-catalog.ts"() {
4170
+ "use strict";
4171
+ DEFAULT_METHOD = "superpowers";
4172
+ VERIFICATION_GATE = "superpowers:verification-before-completion";
4173
+ MATT_CLAUSE = "Sesi ini TAK BERPENUNGGU \u2014 tak ada manusia yang menonton terminal untuk menjawab. Skill mattpocock yang kontraknya mewawancarai manusia (`/grill-me`, `/to-spec`, `/triage`) JANGAN dipakai: pakai primitif model-invoked-nya dan putuskan sendiri. `to-tickets` di fase Plan dipakai HANYA sebagai penghasil berkas plan berkotak `- [ ]` di `docs/matt/plans/`, BUKAN penerbit tiket \u2014 hanoman sendiri adalah issue tracker-nya, jadi jangan menulis ke tracker eksternal mana pun.";
4174
+ METHODS = {
4175
+ // Isi `PHASE_SKILLS` (SPEC-166) dipindah APA ADANYA. Objective & Spec adalah keluaran skill
4176
+ // brainstorming yang di-invoke di fase Brainstorm — sengaja tak punya entri sendiri. Fase reverse
4177
+ // dipandu standar docs di prompt-nya, bukan skill.
4178
+ superpowers: {
4179
+ id: "superpowers",
4180
+ label: "superpowers",
4181
+ planDir: "docs/superpowers/plans",
4182
+ specDir: "docs/superpowers/specs",
4183
+ phaseSkills: {
4184
+ Brainstorm: ["superpowers:brainstorming"],
4185
+ Audit: ["superpowers:systematic-debugging"],
4186
+ Plan: ["superpowers:writing-plans"],
4187
+ Execute: [
4188
+ "superpowers:executing-plans",
4189
+ "superpowers:test-driven-development",
4190
+ VERIFICATION_GATE
4191
+ ],
4192
+ // SPEC-407 · fase `Goal` sengaja TANPA skill: seluruh inti flow itu membebaskan sesi dari
4193
+ // proses kaku. Yang tetap dijaga cuma pintu keluarnya.
4194
+ Verifikasi: [VERIFICATION_GATE]
4195
+ },
4196
+ exitSkills: [VERIFICATION_GATE],
4197
+ requires: ["superpowers"],
4198
+ install: {
4199
+ claude: [
4200
+ "claude plugin marketplace add obra/superpowers-marketplace",
4201
+ "claude plugin install superpowers@superpowers-marketplace"
4202
+ ],
4203
+ codex: ["codex plugin add superpowers@openai-curated"]
4204
+ }
4205
+ },
4206
+ // Plugin `mattpocock-skills` (`/plugin install mattpocock-skills`); skill plugin di Claude Code
4207
+ // beralamat `plugin:skill`. `superpowers` tetap ikut di `requires` karena gerbang verifikasinya
4208
+ // dipinjam dari sana — mattpocock tak punya padanannya.
4209
+ matt: {
4210
+ id: "matt",
4211
+ label: "mattpocock",
4212
+ planDir: "docs/matt/plans",
4213
+ specDir: "docs/matt/specs",
4214
+ phaseSkills: {
4215
+ Brainstorm: ["mattpocock-skills:grilling"],
4216
+ Audit: ["mattpocock-skills:diagnosing-bugs"],
4217
+ Plan: ["mattpocock-skills:to-tickets"],
4218
+ Execute: [
4219
+ "mattpocock-skills:implement",
4220
+ "mattpocock-skills:tdd",
4221
+ "mattpocock-skills:code-review"
4222
+ ],
4223
+ Verifikasi: [VERIFICATION_GATE]
4224
+ },
4225
+ exitSkills: [VERIFICATION_GATE],
4226
+ extraClause: MATT_CLAUSE,
4227
+ requires: ["mattpocock-skills", "superpowers"],
4228
+ // `mattpocock-skills` ada di marketplace resmi Claude Code → tanpa `marketplace add`.
4229
+ // superpowers ikut dipasang karena `requires` menyebutnya: gerbang verifikasinya dipinjam
4230
+ // dari sana (INVARIAN 2). codex belum punya plugin native mattpocock — jalur resminya
4231
+ // `npx skills`, dan ia INTERAKTIF; itu justru alasan pemasangan hidup di pane yang ditonton.
4232
+ install: {
4233
+ claude: [
4234
+ "claude plugin install mattpocock-skills",
4235
+ "claude plugin marketplace add obra/superpowers-marketplace",
4236
+ "claude plugin install superpowers@superpowers-marketplace"
4237
+ ],
4238
+ codex: [
4239
+ "npx skills@latest add mattpocock/skills",
4240
+ "codex plugin add superpowers@openai-curated"
4241
+ ]
4242
+ }
4243
+ }
4244
+ };
4245
+ METHOD_IDS = Object.keys(METHODS);
4246
+ uniq = (xs) => [...new Set(xs)];
4247
+ PLAN_DIRS = uniq(METHOD_IDS.map((id) => METHODS[id].planDir));
4248
+ SPEC_DIRS = uniq(METHOD_IDS.map((id) => METHODS[id].specDir));
4249
+ }
4250
+ });
4251
+
4252
+ // ../shared/src/method-status.ts
4253
+ function methodSkills(m) {
4254
+ return [.../* @__PURE__ */ new Set([...Object.values(m.phaseSkills).flat(), ...m.exitSkills])];
4255
+ }
4256
+ function methodStatus(m, agent, installed) {
4257
+ const haveSkills = new Set(installed.skills);
4258
+ const havePackages = new Set(installed.packages);
4259
+ const missingPackages = m.requires.filter((p) => !havePackages.has(p));
4260
+ const missingSkills = methodSkills(m).filter((id) => !haveSkills.has(id));
4261
+ return {
4262
+ method: m.id,
4263
+ label: m.label,
4264
+ agent,
4265
+ ready: missingPackages.length === 0 && missingSkills.length === 0,
4266
+ missingPackages,
4267
+ missingSkills,
4268
+ install: [...m.install[agent]]
4269
+ };
4270
+ }
4271
+ var init_method_status = __esm({
4272
+ "../shared/src/method-status.ts"() {
4273
+ "use strict";
4274
+ }
4275
+ });
4276
+
4322
4277
  // ../shared/src/agent-engine.ts
4323
4278
  var zAgentEngine;
4324
4279
  var init_agent_engine = __esm({
@@ -4465,6 +4420,7 @@ var init_entities = __esm({
4465
4420
  init_telegram();
4466
4421
  init_agent_engine();
4467
4422
  init_auto_merge();
4423
+ init_method_catalog();
4468
4424
  zProject = external_exports.object({
4469
4425
  id: external_exports.string(),
4470
4426
  name: external_exports.string(),
@@ -4664,6 +4620,12 @@ var init_entities = __esm({
4664
4620
  // SPEC-338 · ADR-0074 · model/effort codex
4665
4621
  verifyScope: zVerifyScope.default("changed"),
4666
4622
  // SPEC-376 · ADR-0080 · scope verifikasi sesi
4623
+ // SPEC-734 · ADR-0113 · metode workflow default. LENIENT (`z.string()`, bukan `z.enum`):
4624
+ // instance yang di-sync dari hub bisa membawa id metode yang belum ada di build ini, dan itu
4625
+ // harus jadi fallback diam di titik pakai (`resolveMethod`) — bukan baris Setting yang gagal
4626
+ // parse lalu mengosongkan layar Settings. Alasan yang sama dengan model/effort di sini.
4627
+ method: external_exports.string().default(DEFAULT_METHOD),
4628
+ // SPEC-734 · ADR-0113 · metode workflow default
4667
4629
  conflict: zConflict.default(CONFLICT_DEFAULTS),
4668
4630
  // SPEC-383 · ADR-0081 · default sesi konflik rebase/merge
4669
4631
  lead: zLead.default(LEAD_DEFAULTS),
@@ -5378,7 +5340,15 @@ var init_dto = __esm({
5378
5340
  // SPEC-236 · terminal biasa NON-claude: shell mentah di repoDir project. Tanpa flow (bukan
5379
5341
  // pipeline claude). DIDAHULUKAN: z.object non-strict membuang key asing, jadi bila varian
5380
5342
  // longgar {project,flow?} lebih dulu, {project,shell:true} akan lolos sbg plain (shell dibuang).
5381
- external_exports.object({ project: external_exports.string(), shell: external_exports.literal(true) }),
5343
+ // SPEC-739 · ADR-0114 · pemasangan skill metode: klien mengirim METODE + AGEN, bukan teks
5344
+ // perintah — perintahnya diturunkan server dari katalog `METHODS`. Dengan begitu "perintah
5345
+ // instalasi adalah data katalog" berlaku ujung ke ujung, dan endpoint ini tak pernah menjadi
5346
+ // "jalankan shell arbitrer". Tanpa field ini, shell mentah persis seperti SPEC-236.
5347
+ external_exports.object({
5348
+ project: external_exports.string(),
5349
+ shell: external_exports.literal(true),
5350
+ install: external_exports.object({ method: external_exports.string(), agent: zAgent }).optional()
5351
+ }),
5382
5352
  // SPEC-166 · "reverse" = sesi project-level di worktree-nya sendiri, menyusun Source of Truth
5383
5353
  // dari kode. TANPA override runtime: sesi project-level mengikuti Setting.agent (ADR-0074).
5384
5354
  // Terminal biasa (tanpa flow) kini punya variannya SENDIRI di bawah — lihat SPEC-517.
@@ -5425,6 +5395,10 @@ var init_dto = __esm({
5425
5395
  goalCondition: external_exports.string().max(4e3).optional(),
5426
5396
  agent: zAgent.optional(),
5427
5397
  verifyScope: zVerifyScope.optional(),
5398
+ // SPEC-734 · ADR-0113 — metode workflow per SESI: undefined → Spec.payload.method →
5399
+ // Setting.method → "superpowers". LENIENT (`z.string()`, bukan `z.enum`): id yang tak dikenal
5400
+ // jatuh ke default di `resolveMethod`, bukan ditolak 400 — alasan yang sama dengan zSetting.
5401
+ method: external_exports.string().optional(),
5428
5402
  // SPEC-447 · ADR-0093 — lewati gerbang dependency. Hanya jalur manusia; UI hanya
5429
5403
  // mengirimkannya sesudah operator melihat daftar pemblokirnya.
5430
5404
  force: external_exports.boolean().optional()
@@ -5618,6 +5592,8 @@ var init_api = __esm({
5618
5592
  limits: `${API}/limits`,
5619
5593
  // SPEC-339 · versi codex CLI (peringatan lunak model GPT-5.6).
5620
5594
  codexVersion: `${API}/codex/version`,
5595
+ // SPEC-739 · ADR-0114 · kesiapan skill metode per agen. LOCAL-only, diturunkan live dari disk.
5596
+ methodStatus: `${API}/methods/status`,
5621
5597
  // SPEC-489 · panduan AI agent — markdown MENTAH & PUBLIC (tanpa auth). Satu definisi URL untuk
5622
5598
  // klien web dan untuk tautan yang disalin operator ke agennya.
5623
5599
  agentDoc: `${API}/agent-integration.md`,
@@ -7146,6 +7122,8 @@ var init_src = __esm({
7146
7122
  "use strict";
7147
7123
  init_enums();
7148
7124
  init_spec_source();
7125
+ init_method_catalog();
7126
+ init_method_status();
7149
7127
  init_agent_engine();
7150
7128
  init_entities();
7151
7129
  init_agent();
@@ -7172,6 +7150,169 @@ var init_src = __esm({
7172
7150
  }
7173
7151
  });
7174
7152
 
7153
+ // ../runner/src/reverse-standard.ts
7154
+ var init_reverse_standard = __esm({
7155
+ "../runner/src/reverse-standard.ts"() {
7156
+ "use strict";
7157
+ }
7158
+ });
7159
+
7160
+ // ../runner/src/verify-scope.ts
7161
+ var CHANGED;
7162
+ var init_verify_scope = __esm({
7163
+ "../runner/src/verify-scope.ts"() {
7164
+ "use strict";
7165
+ CHANGED = [
7166
+ "Scope verifikasi: HANYA yang berubah. Mesin ini menjalankan beberapa sesi sekaligus \u2014",
7167
+ "memverifikasi seluruh project menghabiskan RAM & CPU yang sedang dipakai sesi lain.",
7168
+ "",
7169
+ "Berkas yang berubah di worktree ini:",
7170
+ '`git diff --name-only "$HANOMAN_BASE_SHA"...HEAD` (yang sudah di-commit) dan',
7171
+ "`git status --porcelain` (yang belum).",
7172
+ "",
7173
+ "- Test: jalankan HANYA test yang berkaitan dengan berkas itu. Repo vitest:",
7174
+ ' `pnpm vitest --run --changed "$HANOMAN_BASE_SHA"` (vitest sendiri menurunkan berkas yang',
7175
+ " berubah dari git, termasuk yang belum di-commit), atau `pnpm vitest related --run <berkas\u2026>`,",
7176
+ " atau sebut path berkas test-nya langsung. Stack lain: `pytest <path>`, `go test ./paket/...`.",
7177
+ " JANGAN `pnpm test` atau `vitest run` polos \u2014 itu seluruh suite.",
7178
+ " Jebakan: `--changed` menyalakan `passWithNoTests`, jadi nol test TERLIHAT hijau. Pastikan",
7179
+ ' test-nya memang berjalan, jangan menerima "no test files" sebagai bukti.',
7180
+ "- Typecheck: hanya paket yang tersentuh (mis. `pnpm --filter ./server typecheck`).",
7181
+ " JANGAN `pnpm -r typecheck` \u2014 itu menyalakan satu proses tsc per paket sekaligus.",
7182
+ "- Lint: hanya berkas yang berubah, bukan seluruh repo.",
7183
+ "- Build penuh: hanya bila yang kamu ubah memang soal build/bundling.",
7184
+ "- Boot server + curl / smoke end-to-end: hanya bila task ini menyentuh endpoint atau perilaku",
7185
+ " runtime-nya, sekali di akhir \u2014 bukan rutin tiap task.",
7186
+ "- Membereskan proses nyangkut: JANGAN `pkill -f <pola>` maupun `killall`. Prompt setiap sesi",
7187
+ " hidup di ARGV proses agennya, jadi pola seperti `vitest`/`tsc`/`node` ikut mencocoki agen sesi",
7188
+ " LAIN di mesin ini dan mematikannya di tengah kerja \u2014 dan `pkill` mengecualikan leluhurnya",
7189
+ " sendiri, jadi yang mati selalu sesi tetangga, bukan sesimu. Bunuh per-PID: `ps`/`lsof -ti:<port>`",
7190
+ " \u2192 `kill <pid>`; atau sempitkan polanya sampai memuat path worktree-mu sendiri.",
7191
+ "",
7192
+ "Suite penuh, lint penuh, dan build penuh adalah tugas MANUSIA sebelum merge, bukan tugas sesi.",
7193
+ "Pengecualian yang kamu putuskan sendiri: bila perubahanmu memang berdampak luas (mengubah",
7194
+ "tipe/kontrak bersama, skema, atau berkas yang diimpor banyak modul), perluas scope seperlunya",
7195
+ "dan katakan alasannya. Ini panduan biaya, bukan larangan."
7196
+ ].join("\n");
7197
+ }
7198
+ });
7199
+
7200
+ // ../runner/src/code-style.ts
7201
+ var CODE_STYLE_CLAUSE;
7202
+ var init_code_style = __esm({
7203
+ "../runner/src/code-style.ts"() {
7204
+ "use strict";
7205
+ CODE_STYLE_CLAUSE = [
7206
+ "Gaya kode \u2014 berlaku setiap kali kamu menulis atau mengubah kode:",
7207
+ "- Tulis kode yang rapi dan mengikuti idiom, penamaan, serta struktur kode di sekitarnya.",
7208
+ " Kodemu harus terbaca seperti kode yang sudah ada di berkas itu, bukan seperti tempelan.",
7209
+ "- Jangan menulis komentar yang cuma mengulang apa yang sudah dinyatakan kode.",
7210
+ "- Komentar hanya untuk hal yang TIDAK terbaca dari kode: alasan/why sebuah keputusan,",
7211
+ " trade-off yang diambil, workaround beserta rujukan SPEC/ADR-nya, atau invariant yang tak",
7212
+ " kelihatan. Komentar semacam itu justru berharga \u2014 jangan ikut dibuang.",
7213
+ "- Jangan menambahkan komentar pembatas seksi, header berhiasan, atau narasi langkah demi langkah.",
7214
+ "- Jangan meninggalkan kode mati atau kode yang dikomentari. Hapus saja; riwayat git yang menyimpannya."
7215
+ ].join("\n");
7216
+ }
7217
+ });
7218
+
7219
+ // ../runner/src/goal-spec.ts
7220
+ var init_goal_spec = __esm({
7221
+ "../runner/src/goal-spec.ts"() {
7222
+ "use strict";
7223
+ }
7224
+ });
7225
+
7226
+ // ../runner/src/prompt.ts
7227
+ var ESCALATION_CONTRACT, PROJECT_METHOD, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
7228
+ var init_prompt = __esm({
7229
+ "../runner/src/prompt.ts"() {
7230
+ "use strict";
7231
+ init_src();
7232
+ init_reverse_standard();
7233
+ init_verify_scope();
7234
+ init_code_style();
7235
+ init_goal_spec();
7236
+ ESCALATION_CONTRACT = [
7237
+ "REKOMENDASI ESKALASI (wajib, terbaca mesin). Di bagian akhir dokumen audit, tulis bagian",
7238
+ "`## Rekomendasi eskalasi` berisi penjelasan singkat untuk manusia, LALU tepat SATU blok ```json",
7239
+ "berbentuk persis seperti ini (satu-satunya blok json di dokumen itu):",
7240
+ "",
7241
+ "```json",
7242
+ '{ "escalation": { "target": "none|qa|brief|prd", "reason": "<alasan singkat>",',
7243
+ ' "alternatives": ["<target lain yang masuk akal>"],',
7244
+ ' "prefill": { "title": "", "context": "", "outcome": "", "constraints": "", "severity": "", "steps": "" } } }',
7245
+ "```",
7246
+ "",
7247
+ "Pilih `target` dari hasil auditmu, bukan default:",
7248
+ '- "qa" \u2014 bug / regresi / perilaku salah yang perlu diperbaiki. Isi `prefill.severity`',
7249
+ " (critical|major|minor) dan `prefill.steps` (langkah reproduksi).",
7250
+ '- "brief" \u2014 kebutuhan/fitur yang bentuknya sudah jelas dan cakupannya satu backlog.',
7251
+ " Isi `prefill.title`, `prefill.context`, `prefill.outcome`.",
7252
+ '- "prd" \u2014 kebutuhan produk yang besar, ambigu, atau lintas modul sehingga perlu dokumen PRD',
7253
+ " lebih dulu. Isi `prefill.title`, `prefill.context`, `prefill.outcome`.",
7254
+ '- "none" \u2014 pertanyaannya sudah terjawab; tak perlu perbaikan maupun fitur baru.',
7255
+ "",
7256
+ "`alternatives` boleh array kosong. Jangan menulis blok json lain di dokumen itu."
7257
+ ].join("\n");
7258
+ PROJECT_METHOD = resolveMethod();
7259
+ REVERSE_PHASE_GUIDE = [
7260
+ "- Scan: baca source code \u2014 stack, arsitektur, data model, API surface, perilaku domain. Belum menulis docs.",
7261
+ "- Docs teknis: tulis kategori yang bisa diturunkan dari kode (architecture, requirements + EARS dari perilaku nyata, adr ber-Status accepted (reverse-engineered), operations, security, design-system/frontend bila relevan). Isi lengkap dan spesifik, bukan kerangka.",
7262
+ "- Wawancara: untuk product, business, brand, research, entrypoints \u2014 ajukan SATU pertanyaan per giliran ke manusia di terminal ini, tunggu jawabannya, isi docs dari jawaban. Jangan mengarang. Topik tanpa jawaban tandai: Status: draft \u2014 menunggu input.",
7263
+ "- Konvensi & index: tulis internal/docs/README.md (index bernomor lengkap), CLAUDE.md, AGENTS.md, .claude/settings.json + .claude/hooks/ensure-docs-updated.py persis seperti STANDAR DOCS.",
7264
+ "- Serah terima: pastikan setiap berkas docs terdaftar di index, lalu tulis ringkasan hasil + daftar pertanyaan yang belum terjawab ke terminal."
7265
+ ].join("\n");
7266
+ SCAFFOLD_PHASE_GUIDE = [
7267
+ "- Brainstorm: perdalam IDE project (di bawah) jadi masalah, pengguna, scope, dan metrik sukses. Ajukan SATU pertanyaan per giliran ke manusia di terminal ini, tunggu jawabannya. Jangan mengarang; topik yang belum dijawab tandai sebagai open question.",
7268
+ "- Objective: kunci SATU MVP objective yang terukur dari hasil brainstorm, tulis ringkas di docs.",
7269
+ "- Doc index: tulis SELURUH internal/docs/** dari ide+objective+jawaban, mengikuti STANDAR DOCS di bawah \u2014 entrypoints, product, business, requirements (+EARS dari perilaku yang diinginkan), research, architecture (stack/data-model/api-contract/nfr), adr awal (Status accepted), design-system/frontend bila ada UI, operations, security, plus README index bernomor + CLAUDE.md + AGENTS.md + Stop hook. Lengkap dan spesifik terhadap ide ini, BUKAN kerangka."
7270
+ ].join("\n");
7271
+ }
7272
+ });
7273
+
7274
+ // ../runner/src/git.ts
7275
+ var init_git = __esm({
7276
+ "../runner/src/git.ts"() {
7277
+ "use strict";
7278
+ }
7279
+ });
7280
+
7281
+ // ../runner/src/settings.ts
7282
+ var init_settings = __esm({
7283
+ "../runner/src/settings.ts"() {
7284
+ "use strict";
7285
+ }
7286
+ });
7287
+
7288
+ // ../runner/src/goal.ts
7289
+ var init_goal = __esm({
7290
+ "../runner/src/goal.ts"() {
7291
+ "use strict";
7292
+ init_src();
7293
+ init_prompt();
7294
+ init_goal_spec();
7295
+ }
7296
+ });
7297
+
7298
+ // ../runner/src/codex-settings.ts
7299
+ var init_codex_settings = __esm({
7300
+ "../runner/src/codex-settings.ts"() {
7301
+ "use strict";
7302
+ init_src();
7303
+ init_prompt();
7304
+ }
7305
+ });
7306
+
7307
+ // ../runner/src/agent-cli.ts
7308
+ var init_agent_cli = __esm({
7309
+ "../runner/src/agent-cli.ts"() {
7310
+ "use strict";
7311
+ init_settings();
7312
+ init_codex_settings();
7313
+ }
7314
+ });
7315
+
7175
7316
  // ../runner/src/custom-agents.ts
7176
7317
  var init_custom_agents = __esm({
7177
7318
  "../runner/src/custom-agents.ts"() {
@@ -7238,6 +7379,186 @@ var init_paths = __esm({
7238
7379
  }
7239
7380
  });
7240
7381
 
7382
+ // ../runner/src/skills.ts
7383
+ import { homedir as homedir2 } from "node:os";
7384
+ import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
7385
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
7386
+ function agentSkillHome(agent, env = process.env, osHome = homedir2()) {
7387
+ const own = (agent === "codex" ? env.HANOMAN_CODEX_HOME : env.HANOMAN_CLAUDE_HOME)?.trim();
7388
+ if (own) return own;
7389
+ if (agent === "codex") {
7390
+ const cx = env.CODEX_HOME?.trim();
7391
+ if (cx) return cx;
7392
+ }
7393
+ return join2(osHome, agent === "codex" ? ".codex" : ".claude");
7394
+ }
7395
+ function agentsSkillHome(env = process.env, osHome = homedir2()) {
7396
+ return env.HANOMAN_AGENTS_HOME?.trim() || join2(osHome, ".agents");
7397
+ }
7398
+ function dirsIn(dir) {
7399
+ try {
7400
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name);
7401
+ } catch {
7402
+ return [];
7403
+ }
7404
+ }
7405
+ function readJson(file) {
7406
+ try {
7407
+ return JSON.parse(readFileSync(file, "utf8"));
7408
+ } catch {
7409
+ return null;
7410
+ }
7411
+ }
7412
+ function isSkillDir(dir) {
7413
+ return existsSync(join2(dir, "SKILL.md"));
7414
+ }
7415
+ function asSkill(dir, pkg, name = basename(dir)) {
7416
+ return { id: pkg ? `${pkg}:${name}` : name, name, pkg, dir };
7417
+ }
7418
+ function skillsUnder(dir, pkg, depth = 2) {
7419
+ const out = [];
7420
+ for (const name of dirsIn(dir)) {
7421
+ const sub = join2(dir, name);
7422
+ if (isSkillDir(sub)) out.push(asSkill(sub, pkg, name));
7423
+ else if (depth > 1) out.push(...skillsUnder(sub, pkg, depth - 1));
7424
+ }
7425
+ return out;
7426
+ }
7427
+ function manifestSkills(installPath, pkg) {
7428
+ for (const marker of [".claude-plugin", ".codex-plugin"]) {
7429
+ const j = readJson(join2(installPath, marker, "plugin.json"));
7430
+ const declared = j?.skills;
7431
+ if (!Array.isArray(declared)) continue;
7432
+ const out = [];
7433
+ for (const rel of declared) {
7434
+ if (typeof rel !== "string" || !rel) continue;
7435
+ const p = resolve2(installPath, rel);
7436
+ const dir = basename(p) === "SKILL.md" ? dirname2(p) : p;
7437
+ if (isSkillDir(dir)) out.push(asSkill(dir, pkg));
7438
+ }
7439
+ if (out.length) return out;
7440
+ }
7441
+ return [];
7442
+ }
7443
+ function pluginSkills(installPath, pkg) {
7444
+ const declared = manifestSkills(installPath, pkg);
7445
+ return declared.length ? declared : skillsUnder(join2(installPath, "skills"), pkg);
7446
+ }
7447
+ function lockPluginNames(agentsHome) {
7448
+ const out = /* @__PURE__ */ new Map();
7449
+ const j = readJson(join2(agentsHome, ".skill-lock.json"));
7450
+ const skills = j?.skills;
7451
+ if (!skills || typeof skills !== "object") return out;
7452
+ for (const [name, entry] of Object.entries(skills)) {
7453
+ const pkg = entry?.pluginName;
7454
+ if (typeof pkg === "string" && pkg.trim()) out.set(name, pkg.trim());
7455
+ }
7456
+ return out;
7457
+ }
7458
+ function splitPluginKey(key) {
7459
+ const at = key.lastIndexOf("@");
7460
+ return at > 0 ? { pkg: key.slice(0, at), marketplace: key.slice(at + 1) } : { pkg: key, marketplace: "" };
7461
+ }
7462
+ function manifestRoots(home) {
7463
+ const j = readJson(join2(home, "plugins", "installed_plugins.json"));
7464
+ const plugins = j?.plugins;
7465
+ if (!plugins || typeof plugins !== "object") return [];
7466
+ const out = [];
7467
+ for (const [key, entries] of Object.entries(plugins)) {
7468
+ const { pkg, marketplace } = splitPluginKey(key);
7469
+ for (const e of Array.isArray(entries) ? entries : []) {
7470
+ const p = e?.installPath;
7471
+ if (typeof p === "string" && p) out.push({ pkg, marketplace, dir: p });
7472
+ }
7473
+ }
7474
+ return out;
7475
+ }
7476
+ function cacheRoots(home) {
7477
+ const cache = join2(home, "plugins", "cache");
7478
+ const out = [];
7479
+ for (const marketplace of dirsIn(cache))
7480
+ for (const pkg of dirsIn(join2(cache, marketplace)))
7481
+ for (const version3 of dirsIn(join2(cache, marketplace, pkg)))
7482
+ out.push({ pkg, marketplace, dir: join2(cache, marketplace, pkg, version3) });
7483
+ return out;
7484
+ }
7485
+ function disabledPlugins(agent, home) {
7486
+ const out = /* @__PURE__ */ new Set();
7487
+ if (agent === "claude") {
7488
+ const en = readJson(join2(home, "settings.json"))?.enabledPlugins;
7489
+ if (en && typeof en === "object") {
7490
+ for (const [k, v] of Object.entries(en)) if (v === false) out.add(k);
7491
+ }
7492
+ return out;
7493
+ }
7494
+ let toml;
7495
+ try {
7496
+ toml = readFileSync(join2(home, "config.toml"), "utf8");
7497
+ } catch {
7498
+ return out;
7499
+ }
7500
+ let current = null;
7501
+ for (const raw of toml.split("\n")) {
7502
+ const line = raw.trim();
7503
+ const head = /^\[([^\]]*)\]$/.exec(line);
7504
+ if (head) {
7505
+ const m = /^plugins\."(.+)"$/.exec(head[1] ?? "");
7506
+ current = m ? m[1] : null;
7507
+ continue;
7508
+ }
7509
+ if (current && /^enabled\s*=\s*false\b/.test(line)) out.add(current);
7510
+ }
7511
+ return out;
7512
+ }
7513
+ function scanAgentSkills(agent, env = process.env, osHome = homedir2()) {
7514
+ const home = agentSkillHome(agent, env, osHome);
7515
+ const disabled = disabledPlugins(agent, home);
7516
+ const roots = [];
7517
+ const skills = [];
7518
+ const packages = /* @__PURE__ */ new Set();
7519
+ const seenSkill = /* @__PURE__ */ new Set();
7520
+ const seenRoot = /* @__PURE__ */ new Set();
7521
+ const add = (dir, found) => {
7522
+ if (!found.length) return;
7523
+ if (!seenRoot.has(dir)) {
7524
+ seenRoot.add(dir);
7525
+ roots.push(dir);
7526
+ }
7527
+ for (const s2 of found) if (!seenSkill.has(s2.id)) {
7528
+ seenSkill.add(s2.id);
7529
+ skills.push(s2);
7530
+ }
7531
+ };
7532
+ const userDir = join2(home, "skills");
7533
+ add(userDir, skillsUnder(userDir, null));
7534
+ for (const r of [...manifestRoots(home), ...cacheRoots(home)]) {
7535
+ if (disabled.has(`${r.pkg}@${r.marketplace}`)) continue;
7536
+ const found = pluginSkills(r.dir, r.pkg);
7537
+ if (!found.length) continue;
7538
+ packages.add(r.pkg);
7539
+ add(join2(r.dir, "skills"), found);
7540
+ }
7541
+ if (agent === "codex") {
7542
+ const agentsHome = agentsSkillHome(env, osHome);
7543
+ const dir = join2(agentsHome, "skills");
7544
+ const names = lockPluginNames(agentsHome);
7545
+ const flat = skillsUnder(dir, null);
7546
+ const withPkg = flat.flatMap((s2) => {
7547
+ const pkg = names.get(s2.name);
7548
+ if (!pkg) return [s2];
7549
+ packages.add(pkg);
7550
+ return [asSkill(s2.dir, pkg, s2.name), s2];
7551
+ });
7552
+ add(dir, withPkg);
7553
+ }
7554
+ return { agent, home, roots, skills, packages: [...packages] };
7555
+ }
7556
+ var init_skills = __esm({
7557
+ "../runner/src/skills.ts"() {
7558
+ "use strict";
7559
+ }
7560
+ });
7561
+
7241
7562
  // ../runner/src/telegram-operator.ts
7242
7563
  var init_telegram_operator = __esm({
7243
7564
  "../runner/src/telegram-operator.ts"() {
@@ -7262,29 +7583,30 @@ var init_src2 = __esm({
7262
7583
  init_verify_scope();
7263
7584
  init_code_style();
7264
7585
  init_paths();
7586
+ init_skills();
7265
7587
  init_telegram_operator();
7266
7588
  }
7267
7589
  });
7268
7590
 
7269
7591
  // src/layout.ts
7270
- import { join as join2, resolve as resolve2 } from "node:path";
7592
+ import { join as join3, resolve as resolve3 } from "node:path";
7271
7593
  function resolveLayout(distDir2, exists) {
7272
- const pkg = resolve2(distDir2, "..");
7273
- if (exists(join2(pkg, "prisma", "schema.prisma"))) {
7594
+ const pkg = resolve3(distDir2, "..");
7595
+ if (exists(join3(pkg, "prisma", "schema.prisma"))) {
7274
7596
  return {
7275
7597
  root: pkg,
7276
- schema: join2(pkg, "prisma", "schema.prisma"),
7277
- server: join2(pkg, "dist", "server.js"),
7278
- web: exists(join2(pkg, "web")) ? join2(pkg, "web") : null
7598
+ schema: join3(pkg, "prisma", "schema.prisma"),
7599
+ server: join3(pkg, "dist", "server.js"),
7600
+ web: exists(join3(pkg, "web")) ? join3(pkg, "web") : null
7279
7601
  };
7280
7602
  }
7281
- const repo = resolve2(distDir2, "../..");
7282
- if (exists(join2(repo, "server", "prisma", "schema.prisma"))) {
7603
+ const repo = resolve3(distDir2, "../..");
7604
+ if (exists(join3(repo, "server", "prisma", "schema.prisma"))) {
7283
7605
  return {
7284
7606
  root: repo,
7285
- schema: join2(repo, "server", "prisma", "schema.prisma"),
7286
- server: join2(repo, "server", "dist", "server.js"),
7287
- web: exists(join2(repo, "src", "dist")) ? join2(repo, "src", "dist") : null
7607
+ schema: join3(repo, "server", "prisma", "schema.prisma"),
7608
+ server: join3(repo, "server", "dist", "server.js"),
7609
+ web: exists(join3(repo, "src", "dist")) ? join3(repo, "src", "dist") : null
7288
7610
  };
7289
7611
  }
7290
7612
  throw new Error(`hanoman: prisma/schema.prisma tak ditemukan dari ${distDir2} \u2014 instalasi rusak?`);
@@ -7382,8 +7704,8 @@ __export(start_exports, {
7382
7704
  });
7383
7705
  import { spawn, execFileSync as execFileSync2 } from "node:child_process";
7384
7706
  import { createRequire } from "node:module";
7385
- import { existsSync, mkdirSync, readdirSync, statSync, chmodSync } from "node:fs";
7386
- import { dirname as dirname2, join as join3, resolve as resolvePath } from "node:path";
7707
+ import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, statSync, chmodSync } from "node:fs";
7708
+ import { dirname as dirname3, join as join4, resolve as resolvePath } from "node:path";
7387
7709
  import { fileURLToPath } from "node:url";
7388
7710
  function parseStartArgs(argv) {
7389
7711
  const out = { port: null, host: null, db: null, migrate: true };
@@ -7424,7 +7746,7 @@ function parseStartArgs(argv) {
7424
7746
  return out;
7425
7747
  }
7426
7748
  function distDir() {
7427
- return dirname2(fileURLToPath(import.meta.url));
7749
+ return dirname3(fileURLToPath(import.meta.url));
7428
7750
  }
7429
7751
  function runPrisma(args, dbUrl) {
7430
7752
  const prismaCli = prismaCliPath(createRequire(import.meta.url).resolve);
@@ -7476,9 +7798,9 @@ async function ensurePrismaClient(schema, dbUrl, ctx) {
7476
7798
  }
7477
7799
  }
7478
7800
  function spawnHelperPaths(ptyDir, listDir, exists) {
7479
- const dirs = [join3(ptyDir, "build", "Release")];
7480
- for (const name of listDir(join3(ptyDir, "prebuilds"))) dirs.push(join3(ptyDir, "prebuilds", name));
7481
- return dirs.map((d) => join3(d, "spawn-helper")).filter(exists);
7801
+ const dirs = [join4(ptyDir, "build", "Release")];
7802
+ for (const name of listDir(join4(ptyDir, "prebuilds"))) dirs.push(join4(ptyDir, "prebuilds", name));
7803
+ return dirs.map((d) => join4(d, "spawn-helper")).filter(exists);
7482
7804
  }
7483
7805
  function ensureSpawnHelpersExecutable(paths2, ops) {
7484
7806
  const fixed = [];
@@ -7496,17 +7818,17 @@ function ensureSpawnHelpersExecutable(paths2, ops) {
7496
7818
  function repairSpawnHelper(ctx) {
7497
7819
  let ptyDir;
7498
7820
  try {
7499
- ptyDir = dirname2(createRequire(import.meta.url).resolve("node-pty/package.json"));
7821
+ ptyDir = dirname3(createRequire(import.meta.url).resolve("node-pty/package.json"));
7500
7822
  } catch {
7501
7823
  return;
7502
7824
  }
7503
7825
  const paths2 = spawnHelperPaths(ptyDir, (d) => {
7504
7826
  try {
7505
- return readdirSync(d);
7827
+ return readdirSync2(d);
7506
7828
  } catch {
7507
7829
  return [];
7508
7830
  }
7509
- }, existsSync);
7831
+ }, existsSync2);
7510
7832
  const fixed = ensureSpawnHelpersExecutable(paths2, {
7511
7833
  mode: (p) => statSync(p).mode,
7512
7834
  chmod: (p, m) => chmodSync(p, m)
@@ -7558,8 +7880,8 @@ async function start(argv, ctx) {
7558
7880
  let layout;
7559
7881
  let dbUrl;
7560
7882
  try {
7561
- layout = resolveLayout(distDir(), existsSync);
7562
- dbUrl = opts.db ? `file:${resolvePath(opts.db)}` : resolveDbUrl(ctx.env, dirname2(layout.schema));
7883
+ layout = resolveLayout(distDir(), existsSync2);
7884
+ dbUrl = opts.db ? `file:${resolvePath(opts.db)}` : resolveDbUrl(ctx.env, dirname3(layout.schema));
7563
7885
  } catch (e) {
7564
7886
  ctx.stderr(`${e.message}
7565
7887
  `);
@@ -7570,8 +7892,8 @@ async function start(argv, ctx) {
7570
7892
  `);
7571
7893
  const home = resolveHome(ctx.env);
7572
7894
  mkdirSync(home, { recursive: true });
7573
- mkdirSync(dirname2(dbFilePath(dbUrl)), { recursive: true });
7574
- if (!existsSync(layout.server)) {
7895
+ mkdirSync(dirname3(dbFilePath(dbUrl)), { recursive: true });
7896
+ if (!existsSync2(layout.server)) {
7575
7897
  ctx.stderr(`hanoman: bundle server tak ada di ${layout.server} \u2014 jalankan \`pnpm build\` dulu
7576
7898
  `);
7577
7899
  return 1;
@@ -7655,8 +7977,8 @@ __export(doctor_exports, {
7655
7977
  doctorReport: () => doctorReport
7656
7978
  });
7657
7979
  import { execFileSync as execFileSync3 } from "node:child_process";
7658
- import { accessSync, constants, existsSync as existsSync2 } from "node:fs";
7659
- import { dirname as dirname3 } from "node:path";
7980
+ import { accessSync, constants, existsSync as existsSync3 } from "node:fs";
7981
+ import { dirname as dirname4 } from "node:path";
7660
7982
  function doctorReport(p) {
7661
7983
  const major = Number(/^v?(\d+)/.exec(p.node)?.[1] ?? 0);
7662
7984
  const rows = [
@@ -7675,6 +7997,15 @@ function doctorReport(p) {
7675
7997
  { mark: p.web ? "\u2713" : "!", text: p.web ? "aset dashboard ada" : "aset dashboard tak ada \u2014 API jalan, dashboard tidak", fatal: false },
7676
7998
  { mark: "\xB7", text: `db ${p.db}`, fatal: false }
7677
7999
  ];
8000
+ for (const m of p.methods) {
8001
+ const agent = m.agent === "codex" ? "Codex CLI" : "Claude Code";
8002
+ rows.push({
8003
+ mark: m.ready ? "\u2713" : "!",
8004
+ text: m.ready ? `metode ${m.method} \xB7 ${agent} \u2014 siap` : `metode ${m.method} \xB7 ${agent} \u2014 belum siap: ` + [...m.missingPackages, ...m.missingSkills].join(", ") + m.install.map((c) => `
8005
+ ${c}`).join(""),
8006
+ fatal: false
8007
+ });
8008
+ }
7678
8009
  if (!p.claude && !p.codex) {
7679
8010
  rows.push({ mark: "\u2717", text: "tak ada CLI agen (claude ATAU codex wajib ada)", fatal: true });
7680
8011
  }
@@ -7691,8 +8022,8 @@ async function doctor(_argv, ctx) {
7691
8022
  let layout;
7692
8023
  let db;
7693
8024
  try {
7694
- layout = resolveLayout(distDir(), existsSync2);
7695
- db = dbFilePath(resolveDbUrl(ctx.env, dirname3(layout.schema)));
8025
+ layout = resolveLayout(distDir(), existsSync3);
8026
+ db = dbFilePath(resolveDbUrl(ctx.env, dirname4(layout.schema)));
7696
8027
  } catch (e) {
7697
8028
  ctx.stderr(`${e.message}
7698
8029
  `);
@@ -7701,20 +8032,28 @@ async function doctor(_argv, ctx) {
7701
8032
  const home = resolveHome(ctx.env);
7702
8033
  let homeWritable = false;
7703
8034
  try {
7704
- accessSync(existsSync2(home) ? home : dirname3(home), constants.W_OK);
8035
+ accessSync(existsSync3(home) ? home : dirname4(home), constants.W_OK);
7705
8036
  homeWritable = true;
7706
8037
  } catch {
7707
8038
  }
8039
+ const claude = version(ctx.env.HANOMAN_CLAUDE_BIN ?? "claude", ["--version"]);
8040
+ const codex = version(ctx.env.HANOMAN_CODEX_BIN ?? "codex", ["--version"]);
8041
+ const method = METHODS[DEFAULT_METHOD];
8042
+ const methods = zAgent.options.filter((a) => (a === "codex" ? codex : claude) !== null).map((a) => {
8043
+ const s2 = scanAgentSkills(a, ctx.env);
8044
+ return methodStatus(method, a, { skills: s2.skills.map((k) => k.id), packages: s2.packages });
8045
+ });
7708
8046
  const r = doctorReport({
7709
8047
  node: process.version,
7710
8048
  git: version("git", ["--version"]),
7711
8049
  tmux: version("tmux", ["-V"]),
7712
- claude: version(ctx.env.HANOMAN_CLAUDE_BIN ?? "claude", ["--version"]),
7713
- codex: version(ctx.env.HANOMAN_CODEX_BIN ?? "codex", ["--version"]),
8050
+ claude,
8051
+ codex,
7714
8052
  gh: version(ctx.env.HANOMAN_GH_BIN ?? "gh", ["--version"]),
7715
8053
  homeWritable,
7716
8054
  web: layout.web !== null,
7717
- db
8055
+ db,
8056
+ methods
7718
8057
  });
7719
8058
  ctx.stdout(`hanoman doctor
7720
8059
  ${r.lines.join("\n")}
@@ -7730,6 +8069,7 @@ var init_doctor = __esm({
7730
8069
  "src/commands/doctor.ts"() {
7731
8070
  "use strict";
7732
8071
  init_src2();
8072
+ init_src();
7733
8073
  init_layout();
7734
8074
  init_start();
7735
8075
  }
@@ -17097,14 +17437,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
17097
17437
  return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
17098
17438
  }
17099
17439
  function sleep(ms, signal) {
17100
- return new Promise((resolve3, reject) => {
17440
+ return new Promise((resolve4, reject) => {
17101
17441
  if (signal?.aborted) {
17102
17442
  reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
17103
17443
  return;
17104
17444
  }
17105
17445
  const timer = setTimeout(() => {
17106
17446
  signal?.removeEventListener("abort", onAbort);
17107
- resolve3();
17447
+ resolve4();
17108
17448
  }, ms);
17109
17449
  const onAbort = () => {
17110
17450
  clearTimeout(timer);
@@ -18915,7 +19255,7 @@ var init_src_CX2iR2pK = __esm({
18915
19255
  const flowStartedAt = Date.now();
18916
19256
  let onAbort;
18917
19257
  let cleanupMessageId;
18918
- return new Promise((resolve3, reject) => {
19258
+ return new Promise((resolve4, reject) => {
18919
19259
  const earlyReject = (error2) => {
18920
19260
  reject(error2);
18921
19261
  };
@@ -18983,7 +19323,7 @@ var init_src_CX2iR2pK = __esm({
18983
19323
  }
18984
19324
  if (decoded.kind === "invalid") return reject(decoded.error);
18985
19325
  if (decoded.kind === "input_required") {
18986
- if (options?.allowInputRequired === true) return resolve3(manualInputRequiredValue(decoded));
19326
+ if (options?.allowInputRequired === true) return resolve4(manualInputRequiredValue(decoded));
18987
19327
  const flow = {
18988
19328
  codec,
18989
19329
  request,
@@ -18995,11 +19335,11 @@ var init_src_CX2iR2pK = __esm({
18995
19335
  params
18996
19336
  }, resultSchema, legOptions)
18997
19337
  };
18998
- return resolve3(this._resolveNonCompleteResult(decoded, flow));
19338
+ return resolve4(this._resolveNonCompleteResult(decoded, flow));
18999
19339
  }
19000
19340
  const result = decoded.result;
19001
19341
  validateStandardSchema(resultSchema, result).then((parseResult) => {
19002
- if (parseResult.success) resolve3(parseResult.data);
19342
+ if (parseResult.success) resolve4(parseResult.data);
19003
19343
  else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
19004
19344
  }, reject);
19005
19345
  });
@@ -21815,7 +22155,7 @@ var init_ajvProvider_CEoC_sr = __esm({
21815
22155
  ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
21816
22156
  const schOrFunc = root.refs[ref];
21817
22157
  if (schOrFunc) return schOrFunc;
21818
- let _sch = resolve3.call(this, root, ref);
22158
+ let _sch = resolve4.call(this, root, ref);
21819
22159
  if (_sch === void 0) {
21820
22160
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
21821
22161
  const { schemaId } = this.opts;
@@ -21841,7 +22181,7 @@ var init_ajvProvider_CEoC_sr = __esm({
21841
22181
  function sameSchemaEnv(s1, s2) {
21842
22182
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
21843
22183
  }
21844
- function resolve3(root, ref) {
22184
+ function resolve4(root, ref) {
21845
22185
  let sch;
21846
22186
  while (typeof (sch = this.refs[ref]) == "string") ref = sch;
21847
22187
  return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
@@ -22291,7 +22631,7 @@ var init_ajvProvider_CEoC_sr = __esm({
22291
22631
  else if (typeof uri === "object") uri = parse4(serialize(uri, options), options);
22292
22632
  return uri;
22293
22633
  }
22294
- function resolve3(baseURI, relativeURI, options) {
22634
+ function resolve4(baseURI, relativeURI, options) {
22295
22635
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
22296
22636
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
22297
22637
  schemelessOptions.skipEscape = true;
@@ -22465,7 +22805,7 @@ var init_ajvProvider_CEoC_sr = __esm({
22465
22805
  const fastUri = {
22466
22806
  SCHEMES,
22467
22807
  normalize,
22468
- resolve: resolve3,
22808
+ resolve: resolve4,
22469
22809
  resolveComponent,
22470
22810
  equal,
22471
22811
  serialize,
@@ -28157,7 +28497,7 @@ var init_stdio = __esm({
28157
28497
  }
28158
28498
  send(message) {
28159
28499
  if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
28160
- return new Promise((resolve3, reject) => {
28500
+ return new Promise((resolve4, reject) => {
28161
28501
  const json = serializeMessage(message);
28162
28502
  let settled = false;
28163
28503
  const onError = (error2) => {
@@ -28172,14 +28512,14 @@ var init_stdio = __esm({
28172
28512
  settled = true;
28173
28513
  this._stdout.off("error", onError);
28174
28514
  this._stdout.off("drain", onDrain);
28175
- resolve3();
28515
+ resolve4();
28176
28516
  };
28177
28517
  this._stdout.once("error", onError);
28178
28518
  if (this._stdout.write(json)) {
28179
28519
  if (settled) return;
28180
28520
  settled = true;
28181
28521
  this._stdout.off("error", onError);
28182
- resolve3();
28522
+ resolve4();
28183
28523
  } else if (!settled) this._stdout.once("drain", onDrain);
28184
28524
  });
28185
28525
  }
@@ -28233,14 +28573,14 @@ var init_stdio = __esm({
28233
28573
  */
28234
28574
  async whenRequestsAnswered(timeoutMs) {
28235
28575
  if (this._closed || this._pendingRequests.size === 0) return true;
28236
- return await new Promise((resolve3) => {
28576
+ return await new Promise((resolve4) => {
28237
28577
  const waiter = () => {
28238
28578
  clearTimeout(timer);
28239
- resolve3(true);
28579
+ resolve4(true);
28240
28580
  };
28241
28581
  const timer = setTimeout(() => {
28242
28582
  this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
28243
- resolve3(false);
28583
+ resolve4(false);
28244
28584
  }, timeoutMs);
28245
28585
  this._drainWaiters.push(waiter);
28246
28586
  });
@@ -28519,9 +28859,9 @@ var mcp_exports = {};
28519
28859
  __export(mcp_exports, {
28520
28860
  default: () => mcp
28521
28861
  });
28522
- import { readFileSync } from "node:fs";
28523
- import { homedir as homedir2 } from "node:os";
28524
- import { join as join4 } from "node:path";
28862
+ import { readFileSync as readFileSync2 } from "node:fs";
28863
+ import { homedir as homedir3 } from "node:os";
28864
+ import { join as join5 } from "node:path";
28525
28865
  async function mcp(argv, ctx) {
28526
28866
  const cfg = resolveMcpConfig(argv, ctx.env, () => tokenFile(ctx.env));
28527
28867
  const say = (s2) => ctx.stderr(redactToken(s2, cfg.token) + "\n");
@@ -28535,9 +28875,9 @@ async function mcp(argv, ctx) {
28535
28875
  await handle.close();
28536
28876
  return 0;
28537
28877
  }
28538
- await new Promise((resolve3) => {
28539
- process.stdin.once("close", resolve3);
28540
- process.stdin.once("end", resolve3);
28878
+ await new Promise((resolve4) => {
28879
+ process.stdin.once("close", resolve4);
28880
+ process.stdin.once("end", resolve4);
28541
28881
  });
28542
28882
  return 0;
28543
28883
  }
@@ -28552,9 +28892,9 @@ var init_mcp2 = __esm({
28552
28892
  init_server();
28553
28893
  init_redact();
28554
28894
  tokenFile = (env) => {
28555
- const home = env.HANOMAN_HOME ?? join4(homedir2(), ".hanoman");
28895
+ const home = env.HANOMAN_HOME ?? join5(homedir3(), ".hanoman");
28556
28896
  try {
28557
- return readFileSync(join4(home, "agent-token"), "utf8");
28897
+ return readFileSync2(join5(home, "agent-token"), "utf8");
28558
28898
  } catch {
28559
28899
  return null;
28560
28900
  }
@@ -28574,8 +28914,8 @@ __export(migrate_pg_exports, {
28574
28914
  migrationSteps: () => migrationSteps,
28575
28915
  parseMigrateArgs: () => parseMigrateArgs
28576
28916
  });
28577
- import { existsSync as existsSync3 } from "node:fs";
28578
- import { dirname as dirname4, resolve as resolvePath2 } from "node:path";
28917
+ import { existsSync as existsSync4 } from "node:fs";
28918
+ import { dirname as dirname5, resolve as resolvePath2 } from "node:path";
28579
28919
  function coerceInt8(rows, fields, model) {
28580
28920
  const cols = fields.filter((f) => f.dataTypeID === INT8_OID).map((f) => f.name);
28581
28921
  if (!cols.length) return rows;
@@ -28642,8 +28982,8 @@ async function migratePg(argv, ctx) {
28642
28982
  let layout;
28643
28983
  let dbUrl;
28644
28984
  try {
28645
- layout = resolveLayout(distDir(), existsSync3);
28646
- dbUrl = opts.to ? `file:${resolvePath2(opts.to)}` : resolveDbUrl(ctx.env, dirname4(layout.schema));
28985
+ layout = resolveLayout(distDir(), existsSync4);
28986
+ dbUrl = opts.to ? `file:${resolvePath2(opts.to)}` : resolveDbUrl(ctx.env, dirname5(layout.schema));
28647
28987
  } catch (e) {
28648
28988
  ctx.stderr(`${e.message}
28649
28989
  `);
@@ -28781,11 +29121,11 @@ var init_migrate_pg = __esm({
28781
29121
  });
28782
29122
 
28783
29123
  // src/config.ts
28784
- import { readFileSync as readFileSync2, existsSync as existsSync4 } from "node:fs";
28785
- import { join as join5 } from "node:path";
29124
+ import { readFileSync as readFileSync3, existsSync as existsSync5 } from "node:fs";
29125
+ import { join as join6 } from "node:path";
28786
29126
  function loadConfig(repoRoot) {
28787
- const p = join5(repoRoot, "hanoman.config.json");
28788
- const raw = existsSync4(p) ? JSON.parse(readFileSync2(p, "utf8")) : {};
29127
+ const p = join6(repoRoot, "hanoman.config.json");
29128
+ const raw = existsSync5(p) ? JSON.parse(readFileSync3(p, "utf8")) : {};
28789
29129
  return zHanomanConfig.parse(raw);
28790
29130
  }
28791
29131
  var init_config3 = __esm({
@@ -28797,12 +29137,12 @@ var init_config3 = __esm({
28797
29137
 
28798
29138
  // src/repo.ts
28799
29139
  import { spawnSync } from "node:child_process";
28800
- import { join as join6 } from "node:path";
29140
+ import { join as join7 } from "node:path";
28801
29141
  function resolveRepo(cwd) {
28802
29142
  const r = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
28803
29143
  const root = r.status === 0 ? r.stdout.trim() : cwd;
28804
29144
  const { docsDir } = loadConfig(root);
28805
- return { root, docsDir, indexPath: join6(root, docsDir, "README.md") };
29145
+ return { root, docsDir, indexPath: join7(root, docsDir, "README.md") };
28806
29146
  }
28807
29147
  var init_repo = __esm({
28808
29148
  "src/repo.ts"() {
@@ -28812,10 +29152,10 @@ var init_repo = __esm({
28812
29152
  });
28813
29153
 
28814
29154
  // src/docs-model.ts
28815
- import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
28816
- import { join as join7, relative } from "node:path";
29155
+ import { readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
29156
+ import { join as join8, relative } from "node:path";
28817
29157
  function parseIndex(indexPath) {
28818
- const md = readFileSync3(indexPath, "utf8");
29158
+ const md = readFileSync4(indexPath, "utf8");
28819
29159
  const out = /* @__PURE__ */ new Set();
28820
29160
  for (const m of md.matchAll(LINK_RE2)) {
28821
29161
  let t = m[1].trim();
@@ -28828,9 +29168,9 @@ function parseIndex(indexPath) {
28828
29168
  function walkDocs(docsRoot) {
28829
29169
  const out = [];
28830
29170
  const walk = (dir) => {
28831
- for (const name of readdirSync2(dir)) {
29171
+ for (const name of readdirSync3(dir)) {
28832
29172
  if (name.startsWith(".")) continue;
28833
- const abs = join7(dir, name);
29173
+ const abs = join8(dir, name);
28834
29174
  if (statSync2(abs).isDirectory()) walk(abs);
28835
29175
  else out.push(relative(docsRoot, abs).split("\\").join("/"));
28836
29176
  }
@@ -28862,17 +29202,17 @@ var init_docs_model = __esm({
28862
29202
  });
28863
29203
 
28864
29204
  // src/verify.ts
28865
- import { join as join8 } from "node:path";
28866
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
29205
+ import { join as join9 } from "node:path";
29206
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
28867
29207
  function scanCoverage(cwd) {
28868
29208
  const { root, docsDir, indexPath } = resolveRepo(cwd);
28869
- const docsRoot = join8(root, docsDir);
28870
- if (!existsSync5(docsRoot)) return { coverage: 100, cats: [] };
28871
- if (!existsSync5(indexPath)) throw new Error(`index Source of Truth tidak ada: ${indexPath}`);
29209
+ const docsRoot = join9(root, docsDir);
29210
+ if (!existsSync6(docsRoot)) return { coverage: 100, cats: [] };
29211
+ if (!existsSync6(indexPath)) throw new Error(`index Source of Truth tidak ada: ${indexPath}`);
28872
29212
  const corpus = walkDocs(docsRoot);
28873
29213
  const read = (rel) => {
28874
29214
  try {
28875
- return readFileSync4(join8(docsRoot, rel), "utf8");
29215
+ return readFileSync5(join9(docsRoot, rel), "utf8");
28876
29216
  } catch {
28877
29217
  return null;
28878
29218
  }
@@ -28915,9 +29255,9 @@ var init_docs_scan = __esm({
28915
29255
  });
28916
29256
 
28917
29257
  // src/index-edit.ts
28918
- import { readFileSync as readFileSync5, writeFileSync } from "node:fs";
29258
+ import { readFileSync as readFileSync6, writeFileSync } from "node:fs";
28919
29259
  function addLink(indexPath, relPath, category) {
28920
- let md = readFileSync5(indexPath, "utf8");
29260
+ let md = readFileSync6(indexPath, "utf8");
28921
29261
  if (md.includes(`(${relPath})`)) return;
28922
29262
  const base = relPath.split("/").pop().replace(/\.md$/, "");
28923
29263
  const line = `- [${base}](${relPath})`;
@@ -28946,23 +29286,23 @@ __export(docs_index_exports, {
28946
29286
  default: () => docs_index_default
28947
29287
  });
28948
29288
  import { parseArgs as parseArgs2 } from "node:util";
28949
- import { join as join9 } from "node:path";
28950
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
29289
+ import { join as join10 } from "node:path";
29290
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
28951
29291
  async function docs_index_default(args, ctx) {
28952
29292
  const { values } = parseArgs2({ args, options: { check: { type: "boolean" }, fix: { type: "boolean" } }, allowPositionals: true });
28953
29293
  const { root, docsDir, indexPath } = resolveRepo(ctx.cwd);
28954
- const docsRoot = join9(root, docsDir);
29294
+ const docsRoot = join10(root, docsDir);
28955
29295
  const corpus = walkDocs(docsRoot);
28956
29296
  const read = (rel) => {
28957
29297
  try {
28958
- return readFileSync6(join9(docsRoot, rel), "utf8");
29298
+ return readFileSync7(join10(docsRoot, rel), "utf8");
28959
29299
  } catch {
28960
29300
  return null;
28961
29301
  }
28962
29302
  };
28963
29303
  const linked = linkedSetFrom(INDEX_NAME, corpus, read);
28964
29304
  const unlinked = corpus.filter((f) => f !== INDEX_NAME && !linked.has(f));
28965
- const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync6(join9(docsRoot, p)));
29305
+ const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync7(join10(docsRoot, p)));
28966
29306
  if (values.fix) {
28967
29307
  for (const f of unlinked) addLink(indexPath, f, f.split("/")[0]);
28968
29308
  ctx.stdout(`linked ${unlinked.length} doc(s)
@@ -29015,7 +29355,7 @@ var init_docs_link = __esm({
29015
29355
  });
29016
29356
 
29017
29357
  // src/release/pack.ts
29018
- import { join as join10 } from "node:path";
29358
+ import { join as join11 } from "node:path";
29019
29359
  function packageJsonFor(version3, deps) {
29020
29360
  return {
29021
29361
  name: PKG_NAME,
@@ -29053,17 +29393,17 @@ function verifyPackedDeps(pkg) {
29053
29393
  }
29054
29394
  function copyPlan(repo) {
29055
29395
  return [
29056
- { from: join10(repo, "server/dist/server.js"), to: "dist/server.js" },
29057
- { from: join10(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
29058
- { from: join10(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
29059
- { from: join10(repo, "src/dist"), to: "web", dir: true },
29060
- { from: join10(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
29061
- { from: join10(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
29062
- { from: join10(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
29396
+ { from: join11(repo, "server/dist/server.js"), to: "dist/server.js" },
29397
+ { from: join11(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
29398
+ { from: join11(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
29399
+ { from: join11(repo, "src/dist"), to: "web", dir: true },
29400
+ { from: join11(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
29401
+ { from: join11(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
29402
+ { from: join11(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
29063
29403
  // SPEC-489 · dibaca runtime oleh pickGuideFile (kandidat `<pkg>/docs/…`). Bukan README:
29064
29404
  // README paket adalah npm-readme.md (berhadapan-MANUSIA), ini naskah berhadapan-AGEN.
29065
- { from: join10(repo, "docs/agent-integration.md"), to: "docs/agent-integration.md" },
29066
- { from: join10(repo, "LICENSE"), to: "LICENSE" }
29405
+ { from: join11(repo, "docs/agent-integration.md"), to: "docs/agent-integration.md" },
29406
+ { from: join11(repo, "LICENSE"), to: "LICENSE" }
29067
29407
  ];
29068
29408
  }
29069
29409
  var PKG_NAME, REPO_URL, RUNTIME_DEPS, REQUIRED_ARTIFACTS, BIN_SHIM;
@@ -29109,10 +29449,10 @@ var pack_exports = {};
29109
29449
  __export(pack_exports, {
29110
29450
  default: () => pack
29111
29451
  });
29112
- import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
29113
- import { dirname as dirname5, join as join11 } from "node:path";
29452
+ import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
29453
+ import { dirname as dirname6, join as join12 } from "node:path";
29114
29454
  function depVersions(repo) {
29115
- const read = (p) => JSON.parse(readFileSync7(join11(repo, p), "utf8"));
29455
+ const read = (p) => JSON.parse(readFileSync8(join12(repo, p), "utf8"));
29116
29456
  const server = read("server/package.json"), cli = read("cli/package.json");
29117
29457
  const pools = [server.dependencies, server.devDependencies, cli.dependencies, cli.devDependencies];
29118
29458
  const out = {};
@@ -29126,19 +29466,19 @@ function depVersions(repo) {
29126
29466
  async function pack(argv, ctx) {
29127
29467
  let repo;
29128
29468
  try {
29129
- repo = resolveLayout(distDir(), existsSync7).root;
29469
+ repo = resolveLayout(distDir(), existsSync8).root;
29130
29470
  } catch (e) {
29131
29471
  ctx.stderr(`${e.message}
29132
29472
  `);
29133
29473
  return 1;
29134
29474
  }
29135
- if (!existsSync7(join11(repo, "server/prisma/schema.prisma"))) {
29475
+ if (!existsSync8(join12(repo, "server/prisma/schema.prisma"))) {
29136
29476
  ctx.stderr("pack: hanya bisa dijalankan dari checkout repo hanoman\n");
29137
29477
  return 1;
29138
29478
  }
29139
29479
  const outIdx = argv.indexOf("--out");
29140
- const out = outIdx === -1 ? join11(repo, "dist-npm") : join11(repo, argv[outIdx + 1] ?? "dist-npm");
29141
- const version3 = JSON.parse(readFileSync7(join11(repo, "package.json"), "utf8")).version;
29480
+ const out = outIdx === -1 ? join12(repo, "dist-npm") : join12(repo, argv[outIdx + 1] ?? "dist-npm");
29481
+ const version3 = JSON.parse(readFileSync8(join12(repo, "package.json"), "utf8")).version;
29142
29482
  if (!version3) {
29143
29483
  ctx.stderr("pack: root package.json tanpa field version\n");
29144
29484
  return 1;
@@ -29152,20 +29492,20 @@ async function pack(argv, ctx) {
29152
29492
  return 1;
29153
29493
  }
29154
29494
  rmSync(out, { recursive: true, force: true });
29155
- mkdirSync2(join11(out, "bin"), { recursive: true });
29495
+ mkdirSync2(join12(out, "bin"), { recursive: true });
29156
29496
  for (const item of copyPlan(repo)) {
29157
- if (!existsSync7(item.from)) {
29497
+ if (!existsSync8(item.from)) {
29158
29498
  ctx.stderr(`pack: artefak hilang \u2014 ${item.from} (sudah \`pnpm build\`?)
29159
29499
  `);
29160
29500
  return 1;
29161
29501
  }
29162
- const dest = join11(out, item.to);
29163
- mkdirSync2(dirname5(dest), { recursive: true });
29502
+ const dest = join12(out, item.to);
29503
+ mkdirSync2(dirname6(dest), { recursive: true });
29164
29504
  cpSync(item.from, dest, item.dir ? { recursive: true } : {});
29165
29505
  }
29166
- writeFileSync2(join11(out, "bin/hanoman.mjs"), BIN_SHIM);
29167
- writeFileSync2(join11(out, "package.json"), JSON.stringify(packageJsonFor(version3, deps), null, 2) + "\n");
29168
- const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync7(join11(out, a)));
29506
+ writeFileSync2(join12(out, "bin/hanoman.mjs"), BIN_SHIM);
29507
+ writeFileSync2(join12(out, "package.json"), JSON.stringify(packageJsonFor(version3, deps), null, 2) + "\n");
29508
+ const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync8(join12(out, a)));
29169
29509
  if (missing.length) {
29170
29510
  ctx.stderr(`pack: artefak wajib hilang: ${missing.join(", ")}
29171
29511
  `);
@@ -29190,13 +29530,13 @@ var verify_packed_exports = {};
29190
29530
  __export(verify_packed_exports, {
29191
29531
  default: () => verifyPacked
29192
29532
  });
29193
- import { readFileSync as readFileSync8 } from "node:fs";
29194
- import { join as join12 } from "node:path";
29533
+ import { readFileSync as readFileSync9 } from "node:fs";
29534
+ import { join as join13 } from "node:path";
29195
29535
  function verifyPacked(_argv, ctx) {
29196
- const path = join12(process.cwd(), "package.json");
29536
+ const path = join13(process.cwd(), "package.json");
29197
29537
  let pkg;
29198
29538
  try {
29199
- pkg = JSON.parse(readFileSync8(path, "utf8"));
29539
+ pkg = JSON.parse(readFileSync9(path, "utf8"));
29200
29540
  } catch {
29201
29541
  ctx.stderr(`verify: tak bisa membaca ${path}
29202
29542
  `);