hanoman 0.2.0 → 0.2.2
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/build-info.json +3 -3
- package/dist/cli.js +411 -54
- package/dist/server.js +481 -63
- package/docs/agent-integration.md +9 -1
- package/package.json +1 -1
- package/web/assets/{index-BpV7m-Kb.js → index-BllpAcXg.js} +1547 -1547
- package/web/index.html +1 -1
package/dist/build-info.json
CHANGED
package/dist/cli.js
CHANGED
|
@@ -4740,6 +4740,12 @@ var init_agent = __esm({
|
|
|
4740
4740
|
// capability domain produk masing-masing; domain ini tidak memberi akses shell atau sesi.
|
|
4741
4741
|
"telegram:read",
|
|
4742
4742
|
"telegram:write",
|
|
4743
|
+
// ADR-0157 · papan Tim (`/api/tasks`, `/api/members`). Domain TERSENDIRI, bukan menumpang
|
|
4744
|
+
// `backlog`: kartu tim adalah pekerjaan MANUSIA yang `status`-nya bebas dipindah, sementara
|
|
4745
|
+
// `Spec.stage` diturunkan dari fase sesi (ADR-0008/0024). Menumpangkannya berarti satu centang
|
|
4746
|
+
// "Backlog — tulis" diam-diam membuka papan orang, dan sebaliknya.
|
|
4747
|
+
"team:read",
|
|
4748
|
+
"team:write",
|
|
4743
4749
|
// ADR-0155 · akses KETIGA: `danger`. Dipecah dari `:write` karena keempat operasi ini bukan
|
|
4744
4750
|
// "menulis lebih banyak", melainkan menjalankan sesuatu di luar proses hanoman — sesi agen di
|
|
4745
4751
|
// worktree, perintah di VPS, git yang mengubah sejarah, penghapusan artefak dokumen.
|
|
@@ -6060,7 +6066,15 @@ var init_api = __esm({
|
|
|
6060
6066
|
breakdown: (id, prd) => `${API}/projects/${id}/breakdown?prd=${encodeURIComponent(prd)}`,
|
|
6061
6067
|
specsBatch: `${API}/specs/batch`,
|
|
6062
6068
|
// SPEC-182 · IDE Visual
|
|
6063
|
-
|
|
6069
|
+
// `hidden` menyertakan entri .gitignore; `under` membuka satu tingkat isi sebuah direktori.
|
|
6070
|
+
ideTree: (id, ref = "", opts) => {
|
|
6071
|
+
const q = new URLSearchParams();
|
|
6072
|
+
if (ref) q.set("ref", ref);
|
|
6073
|
+
if (opts?.hidden) q.set("hidden", "1");
|
|
6074
|
+
if (opts?.under) q.set("under", opts.under);
|
|
6075
|
+
const s2 = q.toString();
|
|
6076
|
+
return `${API}/projects/${id}/tree${s2 ? `?${s2}` : ""}`;
|
|
6077
|
+
},
|
|
6064
6078
|
ideFile: (id, path, ref = "") => `${API}/projects/${id}/file${path ? `?path=${encodeURIComponent(path)}${ref ? `&ref=${encodeURIComponent(ref)}` : ""}` : ""}`,
|
|
6065
6079
|
ideGraph: (id, limit = 200, opts) => {
|
|
6066
6080
|
const p3 = new URLSearchParams({ limit: String(limit) });
|
|
@@ -7000,6 +7014,50 @@ var init_webhook = __esm({
|
|
|
7000
7014
|
}
|
|
7001
7015
|
});
|
|
7002
7016
|
|
|
7017
|
+
// ../shared/src/team.ts
|
|
7018
|
+
var TASK_STATUSES, zTaskStatus, zEmail, zName, zIso, zCreateMember, zPatchMember, zCreateTask, zPatchTask, ESCALATE_SOURCES, zEscalateTask;
|
|
7019
|
+
var init_team = __esm({
|
|
7020
|
+
"../shared/src/team.ts"() {
|
|
7021
|
+
"use strict";
|
|
7022
|
+
init_zod();
|
|
7023
|
+
init_enums();
|
|
7024
|
+
TASK_STATUSES = ["backlog", "doing", "review", "done"];
|
|
7025
|
+
zTaskStatus = external_exports.enum(TASK_STATUSES);
|
|
7026
|
+
zEmail = external_exports.string().trim().min(3).max(200).email();
|
|
7027
|
+
zName = external_exports.string().trim().min(1).max(120);
|
|
7028
|
+
zIso = external_exports.string().datetime({ offset: true });
|
|
7029
|
+
zCreateMember = external_exports.object({
|
|
7030
|
+
name: zName,
|
|
7031
|
+
email: zEmail,
|
|
7032
|
+
role: external_exports.string().trim().max(60).nullable().optional()
|
|
7033
|
+
});
|
|
7034
|
+
zPatchMember = zCreateMember.omit({ email: true }).partial().extend({
|
|
7035
|
+
active: external_exports.boolean().optional()
|
|
7036
|
+
});
|
|
7037
|
+
zCreateTask = external_exports.object({
|
|
7038
|
+
title: external_exports.string().trim().min(1).max(300),
|
|
7039
|
+
detail: external_exports.string().max(2e4).nullable().optional(),
|
|
7040
|
+
projectId: external_exports.string().max(120).nullable().optional(),
|
|
7041
|
+
status: zTaskStatus.default("backlog"),
|
|
7042
|
+
priority: zPriority.default("sedang"),
|
|
7043
|
+
memberId: external_exports.string().max(200).nullable().optional(),
|
|
7044
|
+
startDate: zIso.nullable().optional(),
|
|
7045
|
+
dueDate: zIso.nullable().optional(),
|
|
7046
|
+
order: external_exports.number().finite().optional()
|
|
7047
|
+
});
|
|
7048
|
+
zPatchTask = zCreateTask.partial();
|
|
7049
|
+
ESCALATE_SOURCES = ["brief", "qa", "audit"];
|
|
7050
|
+
zEscalateTask = external_exports.object({
|
|
7051
|
+
source: external_exports.enum(ESCALATE_SOURCES).default("brief"),
|
|
7052
|
+
priority: zPriority.default("sedang"),
|
|
7053
|
+
// Dipakai HANYA saat kartunya belum punya project (`nextSpecId` butuh repo, dan repo milik
|
|
7054
|
+
// project). Route menolak 400 bila ia menyebut project LAIN — kartu tak boleh berpindah project
|
|
7055
|
+
// sebagai efek samping yang tak diminta.
|
|
7056
|
+
projectId: external_exports.string().max(120).optional()
|
|
7057
|
+
});
|
|
7058
|
+
}
|
|
7059
|
+
});
|
|
7060
|
+
|
|
7003
7061
|
// ../shared/src/mcp-schema.ts
|
|
7004
7062
|
function obj(o) {
|
|
7005
7063
|
return {
|
|
@@ -7011,11 +7069,12 @@ function obj(o) {
|
|
|
7011
7069
|
...o.allOf ? { allOf: o.allOf } : {}
|
|
7012
7070
|
};
|
|
7013
7071
|
}
|
|
7014
|
-
var str, enumStr, bool, int, strArray, PRIORITY_ENUM, SOURCE_ENUM, STAGE_ENUM, SEVERITY_ENUM, PRIORITY, BRIEF_PAYLOAD, QA_PAYLOAD, GOAL_PAYLOAD, SPEC_PAYLOAD_ONEOF, SOURCE_PAYLOAD_ALLOF, PAGE_PARAMS, DATE_PARAMS;
|
|
7072
|
+
var str, enumStr, bool, int, strArray, PRIORITY_ENUM, SOURCE_ENUM, STAGE_ENUM, SEVERITY_ENUM, TASK_STATUS_ENUM, ESCALATE_SOURCE_ENUM, PRIORITY, BRIEF_PAYLOAD, QA_PAYLOAD, GOAL_PAYLOAD, SPEC_PAYLOAD_ONEOF, SOURCE_PAYLOAD_ALLOF, PAGE_PARAMS, DATE_PARAMS;
|
|
7015
7073
|
var init_mcp_schema = __esm({
|
|
7016
7074
|
"../shared/src/mcp-schema.ts"() {
|
|
7017
7075
|
"use strict";
|
|
7018
7076
|
init_enums();
|
|
7077
|
+
init_team();
|
|
7019
7078
|
str = (description, extra = {}) => ({ type: "string", description, ...extra });
|
|
7020
7079
|
enumStr = (values, description) => ({ type: "string", enum: values, description });
|
|
7021
7080
|
bool = (description) => ({ type: "boolean", description });
|
|
@@ -7025,6 +7084,8 @@ var init_mcp_schema = __esm({
|
|
|
7025
7084
|
SOURCE_ENUM = zSpecSource.options;
|
|
7026
7085
|
STAGE_ENUM = zStage.options;
|
|
7027
7086
|
SEVERITY_ENUM = ["critical", "major", "minor"];
|
|
7087
|
+
TASK_STATUS_ENUM = TASK_STATUSES;
|
|
7088
|
+
ESCALATE_SOURCE_ENUM = ESCALATE_SOURCES;
|
|
7028
7089
|
PRIORITY = enumStr(
|
|
7029
7090
|
PRIORITY_ENUM,
|
|
7030
7091
|
"Prioritas backlog. Nilainya bahasa Indonesia \u2014 `tinggi`, `sedang`, `rendah`. Bukan high/medium/low."
|
|
@@ -7134,7 +7195,7 @@ function renderResult(value, maxBytes) {
|
|
|
7134
7195
|
preview: head
|
|
7135
7196
|
}).slice(0, maxBytes);
|
|
7136
7197
|
}
|
|
7137
|
-
var DEFAULT_MAX_BYTES, DEFAULT_LIMIT, MAX_LIMIT, OBJECTIVE_CLIP, MARK, pick, shapeProject, shapeProjectDetail, SPEC_BASE, shapeSpecDetail, shapeSession, shapeNotification, shapeTicket, shapeGithubIssue, shapeLeadDecision;
|
|
7198
|
+
var DEFAULT_MAX_BYTES, DEFAULT_LIMIT, MAX_LIMIT, OBJECTIVE_CLIP, MARK, pick, shapeProject, shapeProjectDetail, SPEC_BASE, shapeSpecDetail, shapeSession, shapeNotification, shapeTicket, shapeGithubIssue, shapeTask, shapeMember, shapeLeadDecision;
|
|
7138
7199
|
var init_mcp_shape = __esm({
|
|
7139
7200
|
"../shared/src/mcp-shape.ts"() {
|
|
7140
7201
|
"use strict";
|
|
@@ -7196,6 +7257,25 @@ var init_mcp_shape = __esm({
|
|
|
7196
7257
|
...pick(row, ["id", "projectId", "repoSlug", "number", "title", "authorLogin", "labels", "url", "issueState", "status", "specId", "issueUpdatedAt"]),
|
|
7197
7258
|
body: clip(row.body, 500)
|
|
7198
7259
|
});
|
|
7260
|
+
shapeTask = (row) => ({
|
|
7261
|
+
...pick(row, [
|
|
7262
|
+
"id",
|
|
7263
|
+
"projectId",
|
|
7264
|
+
"title",
|
|
7265
|
+
"status",
|
|
7266
|
+
"priority",
|
|
7267
|
+
"memberId",
|
|
7268
|
+
"startDate",
|
|
7269
|
+
"dueDate",
|
|
7270
|
+
"order",
|
|
7271
|
+
"specId",
|
|
7272
|
+
"spec",
|
|
7273
|
+
"createdAt",
|
|
7274
|
+
"updatedAt"
|
|
7275
|
+
]),
|
|
7276
|
+
detail: clip(row.detail, 500)
|
|
7277
|
+
});
|
|
7278
|
+
shapeMember = (row) => pick(row, ["id", "name", "email", "role", "active"]);
|
|
7199
7279
|
shapeLeadDecision = (row) => ({
|
|
7200
7280
|
...pick(row, ["id", "projectId", "specId", "sessionId", "gate", "kind", "status", "confidence", "action", "choice", "createdAt"]),
|
|
7201
7281
|
question: clip(row.question, 300),
|
|
@@ -8205,16 +8285,25 @@ var init_ide = __esm({
|
|
|
8205
8285
|
{
|
|
8206
8286
|
name: "hanoman_ide_tree",
|
|
8207
8287
|
title: "Pohon berkas repo project",
|
|
8208
|
-
description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit.",
|
|
8288
|
+
description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit. `hidden` ikut menyertakan yang .gitignore sembunyikan \u2014 direktori yang seluruhnya diabaikan (mis. `node_modules`) diruntuhkan jadi satu entri di `dirs`; buka isinya dengan `under`, satu tingkat per panggilan.",
|
|
8209
8289
|
inputSchema: obj({
|
|
8210
|
-
properties: {
|
|
8290
|
+
properties: {
|
|
8291
|
+
project: PROJECT2,
|
|
8292
|
+
ref: str("Ref git (branch/tag/sha). Kosongkan untuk working tree."),
|
|
8293
|
+
hidden: bool("Sertakan berkas & direktori yang diabaikan .gitignore. Tak berlaku bersama `ref`."),
|
|
8294
|
+
under: str("Jalur direktori; balasannya berisi SATU tingkat isinya. Dipakai untuk membuka direktori terabaikan yang diruntuhkan.")
|
|
8295
|
+
},
|
|
8211
8296
|
required: ["project"]
|
|
8212
8297
|
}),
|
|
8213
8298
|
mode: "read",
|
|
8214
8299
|
capability: "ide:read",
|
|
8215
8300
|
samplePath: "/projects/hanoman/tree",
|
|
8216
8301
|
sampleMethod: "GET",
|
|
8217
|
-
build: (a) => ({
|
|
8302
|
+
build: (a) => ({
|
|
8303
|
+
method: "GET",
|
|
8304
|
+
path: `${p(a.project)}/tree`,
|
|
8305
|
+
query: query({ ref: s(a.ref), hidden: a.hidden ? "1" : "", under: s(a.under) })
|
|
8306
|
+
}),
|
|
8218
8307
|
shape: (raw) => raw
|
|
8219
8308
|
},
|
|
8220
8309
|
{
|
|
@@ -10297,6 +10386,311 @@ var init_lead2 = __esm({
|
|
|
10297
10386
|
}
|
|
10298
10387
|
});
|
|
10299
10388
|
|
|
10389
|
+
// ../shared/src/mcp-catalog/team.ts
|
|
10390
|
+
var TASK_ID, MEMBER_ID, nullable, CLEAR, TASK_FIELDS, taskBody, TASKS, MEMBERS, DESTRUCTIVE, TEAM_TOOLS;
|
|
10391
|
+
var init_team2 = __esm({
|
|
10392
|
+
"../shared/src/mcp-catalog/team.ts"() {
|
|
10393
|
+
"use strict";
|
|
10394
|
+
init_mcp_schema();
|
|
10395
|
+
init_mcp_shape();
|
|
10396
|
+
init_helpers();
|
|
10397
|
+
TASK_ID = str("Id kartu papan Tim, seperti muncul di hanoman_tasks_list.");
|
|
10398
|
+
MEMBER_ID = str("Id anggota \u2014 email ternormalisasi (huruf kecil), seperti muncul di hanoman_members_list.");
|
|
10399
|
+
nullable = (v) => {
|
|
10400
|
+
if (typeof v !== "string") return void 0;
|
|
10401
|
+
return v === "" ? null : v;
|
|
10402
|
+
};
|
|
10403
|
+
CLEAR = " Kirim string KOSONG untuk mengosongkannya; tak menyebutnya sama sekali berarti biarkan apa adanya.";
|
|
10404
|
+
TASK_FIELDS = {
|
|
10405
|
+
title: str("Judul kartu, satu baris."),
|
|
10406
|
+
detail: str("Isi kartu. Markdown bebas." + CLEAR),
|
|
10407
|
+
project: str("Id project pemilik kartu. Kartu BOLEH tanpa project \u2014 papan Tim tak mensyaratkannya." + CLEAR),
|
|
10408
|
+
status: enumStr(TASK_STATUS_ENUM, "Kolom papan. Milik MANUSIA dan bebas dipindah \u2014 ini BUKAN `stage` backlog, yang diturunkan dari fase sesi."),
|
|
10409
|
+
priority: PRIORITY,
|
|
10410
|
+
member: str("Id anggota yang ditugaskan (email huruf kecil), dari hanoman_members_list." + CLEAR),
|
|
10411
|
+
startDate: str("Tanggal mulai, ISO 8601 berzona mis. `2026-09-01T00:00:00.000Z`. `besok` ditolak." + CLEAR),
|
|
10412
|
+
dueDate: str("Tanggal jatuh tempo, ISO 8601 berzona. Ini tanggal KARTU, bukan estimasi backlog." + CLEAR)
|
|
10413
|
+
};
|
|
10414
|
+
taskBody = (a) => {
|
|
10415
|
+
const body = {};
|
|
10416
|
+
if (s(a.title)) body.title = s(a.title);
|
|
10417
|
+
if (s(a.status)) body.status = s(a.status);
|
|
10418
|
+
if (s(a.priority)) body.priority = s(a.priority);
|
|
10419
|
+
if (n(a.order) !== void 0) body.order = n(a.order);
|
|
10420
|
+
for (const [key, arg] of [
|
|
10421
|
+
["detail", a.detail],
|
|
10422
|
+
["projectId", a.project],
|
|
10423
|
+
["memberId", a.member],
|
|
10424
|
+
["startDate", a.startDate],
|
|
10425
|
+
["dueDate", a.dueDate]
|
|
10426
|
+
]) {
|
|
10427
|
+
const v = nullable(arg);
|
|
10428
|
+
if (v !== void 0) body[key] = v;
|
|
10429
|
+
}
|
|
10430
|
+
return body;
|
|
10431
|
+
};
|
|
10432
|
+
TASKS = [
|
|
10433
|
+
{
|
|
10434
|
+
name: "hanoman_tasks_list",
|
|
10435
|
+
title: "Kartu papan Tim",
|
|
10436
|
+
description: "Kartu kerja MANUSIA di papan Tim, terurut sesuai posisinya di kolom. Empat kolom: `backlog`, `doing`, `review`, `done` \u2014 milik manusia, tak diturunkan dari sesi agen. `specId`/`spec` terisi hanya untuk kartu yang sudah dieskalasi jadi backlog item; `specId` terisi sementara `spec` null berarti tautannya PUTUS (backlognya sudah dihapus), bukan belum dieskalasi.",
|
|
10437
|
+
inputSchema: obj({
|
|
10438
|
+
properties: {
|
|
10439
|
+
project: str("Id project. Tanpa ini, seluruh project \u2014 termasuk kartu tanpa project."),
|
|
10440
|
+
status: enumStr(TASK_STATUS_ENUM, "Kolom papan."),
|
|
10441
|
+
member: MEMBER_ID,
|
|
10442
|
+
q: str("Cari substring pada judul + detail, tak peka huruf besar-kecil. Disaring SEBELUM paginasi, jadi hasilnya tak buta terhadap kartu di luar halaman pertama."),
|
|
10443
|
+
...PAGE_PARAMS
|
|
10444
|
+
}
|
|
10445
|
+
}),
|
|
10446
|
+
mode: "read",
|
|
10447
|
+
capability: "team:read",
|
|
10448
|
+
samplePath: "/tasks",
|
|
10449
|
+
sampleMethod: "GET",
|
|
10450
|
+
build: (a) => ({
|
|
10451
|
+
method: "GET",
|
|
10452
|
+
path: "/tasks",
|
|
10453
|
+
query: query({
|
|
10454
|
+
projectId: s(a.project),
|
|
10455
|
+
status: s(a.status),
|
|
10456
|
+
memberId: s(a.member),
|
|
10457
|
+
q: s(a.q),
|
|
10458
|
+
page: n(a.page) === void 0 ? void 0 : String(n(a.page)),
|
|
10459
|
+
limit: n(a.limit) === void 0 ? void 0 : String(n(a.limit))
|
|
10460
|
+
})
|
|
10461
|
+
}),
|
|
10462
|
+
shape: (raw) => reshapePage(raw, shapeTask)
|
|
10463
|
+
},
|
|
10464
|
+
{
|
|
10465
|
+
name: "hanoman_task_create",
|
|
10466
|
+
title: "Buat kartu papan Tim",
|
|
10467
|
+
description: "Membuat kartu kerja manusia di papan Tim. Kartu BARU tak pernah tertaut backlog \u2014 tautan itu lahir dari hanoman_task_escalate, bukan dari field. Untuk pekerjaan yang akan dikerjakan AGEN, pakai hanoman_backlog_create: papan ini bukan antrean sesi.",
|
|
10468
|
+
inputSchema: obj({ properties: { ...TASK_FIELDS }, required: ["title"] }),
|
|
10469
|
+
mode: "write",
|
|
10470
|
+
capability: "team:write",
|
|
10471
|
+
samplePath: "/tasks",
|
|
10472
|
+
sampleMethod: "POST",
|
|
10473
|
+
build: (a) => ({ method: "POST", path: "/tasks", body: taskBody(a) }),
|
|
10474
|
+
shape: (raw) => shapeTask(raw)
|
|
10475
|
+
},
|
|
10476
|
+
{
|
|
10477
|
+
name: "hanoman_task_update",
|
|
10478
|
+
title: "Ubah kartu papan Tim",
|
|
10479
|
+
description: "Mengubah kartu papan Tim. Hanya field yang disebut yang ditulis. Kartu yang SUDAH tertaut backlog menolak pindah project dengan 400 \u2014 lepas tautannya dulu lewat hanoman_task_unlink. `specId` sengaja tak bisa diisi di sini.",
|
|
10480
|
+
inputSchema: obj({ properties: { task: TASK_ID, ...TASK_FIELDS }, required: ["task"] }),
|
|
10481
|
+
mode: "write",
|
|
10482
|
+
capability: "team:write",
|
|
10483
|
+
samplePath: "/tasks/t1",
|
|
10484
|
+
sampleMethod: "PATCH",
|
|
10485
|
+
build: (a) => ({ method: "PATCH", path: `/tasks/${enc(String(a.task))}`, body: taskBody(a) }),
|
|
10486
|
+
shape: (raw) => shapeTask(raw)
|
|
10487
|
+
},
|
|
10488
|
+
{
|
|
10489
|
+
name: "hanoman_task_escalate",
|
|
10490
|
+
title: "Eskalasi kartu jadi backlog item",
|
|
10491
|
+
description: "Melahirkan backlog item dari sebuah kartu papan Tim, lalu menautkan keduanya. IDEMPOTEN: kartu yang sudah tertaut menjawab 200 dengan `created: false` dan spec yang sama, bukan membuat backlog kedua. Kartu TANPA project ditolak 400 \u2014 id backlog diturunkan dari repo milik project, jadi sebutkan `project` lebih dulu. Ini TIDAK membuka sesi agen; peluncurannya tetap tindakan terpisah, dan Spec-nya lahir tanpa persetujuan peluncuran bila token pemanggil tak memegang `sessions:write`.",
|
|
10492
|
+
inputSchema: obj({
|
|
10493
|
+
properties: {
|
|
10494
|
+
task: TASK_ID,
|
|
10495
|
+
source: enumStr(ESCALATE_SOURCE_ENUM, "Bentuk backlog yang dilahirkan. Default `brief`. `goal`/`no_effort`/`help` sengaja tak tersedia: dua yang pertama menuntut kalimat yang hanya operator bisa tulis, yang terakhir milik tiket Help Center."),
|
|
10496
|
+
priority: PRIORITY,
|
|
10497
|
+
project: str("Id project tujuan. HANYA dipakai bila kartunya belum punya project \u2014 menyebut project LAIN dari milik kartu ditolak 400.")
|
|
10498
|
+
},
|
|
10499
|
+
required: ["task"]
|
|
10500
|
+
}),
|
|
10501
|
+
mode: "write",
|
|
10502
|
+
capability: "team:write",
|
|
10503
|
+
samplePath: "/tasks/t1/escalate",
|
|
10504
|
+
sampleMethod: "POST",
|
|
10505
|
+
build: (a) => ({
|
|
10506
|
+
method: "POST",
|
|
10507
|
+
path: `/tasks/${enc(String(a.task))}/escalate`,
|
|
10508
|
+
body: query({ source: s(a.source), priority: s(a.priority), projectId: s(a.project) })
|
|
10509
|
+
}),
|
|
10510
|
+
shape: (raw) => raw
|
|
10511
|
+
},
|
|
10512
|
+
{
|
|
10513
|
+
name: "hanoman_task_unlink",
|
|
10514
|
+
title: "Lepas tautan kartu dari backlog",
|
|
10515
|
+
description: "Melepas tautan kartu papan Tim dari backlog item-nya. NON-DESTRUKTIF: backlog item-nya dibiarkan hidup \u2014 hapus manual lewat hanoman_backlog_delete bila memang salah eskalasi. Idempoten: kartu yang memang tak tertaut menjawab 200 apa adanya.",
|
|
10516
|
+
inputSchema: obj({ properties: { task: TASK_ID }, required: ["task"] }),
|
|
10517
|
+
mode: "write",
|
|
10518
|
+
capability: "team:write",
|
|
10519
|
+
samplePath: "/tasks/t1/escalate",
|
|
10520
|
+
sampleMethod: "DELETE",
|
|
10521
|
+
build: (a) => ({ method: "DELETE", path: `/tasks/${enc(String(a.task))}/escalate` }),
|
|
10522
|
+
shape: (raw) => shapeTask(raw)
|
|
10523
|
+
}
|
|
10524
|
+
];
|
|
10525
|
+
MEMBERS = [
|
|
10526
|
+
{
|
|
10527
|
+
name: "hanoman_members_list",
|
|
10528
|
+
title: "Anggota tim",
|
|
10529
|
+
description: "Direktori orang untuk papan Tim. GLOBAL, bukan per project \u2014 kartu boleh tanpa project. Anggota nonaktif TETAP terdaftar (di bawah): kartu lama yang ditugaskan padanya harus tetap punya nama. `id` = email ternormalisasi, dan itu yang dipakai sebagai `member` di tool kartu.",
|
|
10530
|
+
inputSchema: obj({ properties: { activeOnly: bool("Hanya anggota aktif. Default: semua, nonaktif di urutan bawah."), ...PAGE_PARAMS } }),
|
|
10531
|
+
mode: "read",
|
|
10532
|
+
capability: "team:read",
|
|
10533
|
+
samplePath: "/members",
|
|
10534
|
+
sampleMethod: "GET",
|
|
10535
|
+
build: (a) => ({
|
|
10536
|
+
method: "GET",
|
|
10537
|
+
path: "/members",
|
|
10538
|
+
query: query({
|
|
10539
|
+
active: a.activeOnly === true ? "true" : void 0,
|
|
10540
|
+
page: n(a.page) === void 0 ? void 0 : String(n(a.page)),
|
|
10541
|
+
limit: n(a.limit) === void 0 ? void 0 : String(n(a.limit))
|
|
10542
|
+
})
|
|
10543
|
+
}),
|
|
10544
|
+
shape: (raw) => reshapePage(raw, shapeMember)
|
|
10545
|
+
},
|
|
10546
|
+
{
|
|
10547
|
+
name: "hanoman_member_create",
|
|
10548
|
+
title: "Tambah anggota tim",
|
|
10549
|
+
description: "Menambah orang ke direktori papan Tim. `id` DITURUNKAN dari email (dipangkas + huruf kecil), bukan diacak \u2014 email yang sudah terdaftar menjawab 409 berikut id-nya, bukan membuat baris kedua.",
|
|
10550
|
+
inputSchema: obj({
|
|
10551
|
+
properties: {
|
|
10552
|
+
name: str("Nama yang ditampilkan di kartu."),
|
|
10553
|
+
email: str("Email. Menjadi id anggota sesudah dipangkas & dihurufkecilkan \u2014 dan id itu TAK BISA diubah kemudian."),
|
|
10554
|
+
role: str("Peran, mis. `desainer`. Bebas teks.")
|
|
10555
|
+
},
|
|
10556
|
+
required: ["name", "email"]
|
|
10557
|
+
}),
|
|
10558
|
+
mode: "write",
|
|
10559
|
+
capability: "team:write",
|
|
10560
|
+
samplePath: "/members",
|
|
10561
|
+
sampleMethod: "POST",
|
|
10562
|
+
build: (a) => ({
|
|
10563
|
+
method: "POST",
|
|
10564
|
+
path: "/members",
|
|
10565
|
+
body: { name: String(a.name), email: String(a.email), ...s(a.role) ? { role: s(a.role) } : {} }
|
|
10566
|
+
}),
|
|
10567
|
+
shape: (raw) => shapeMember(raw)
|
|
10568
|
+
},
|
|
10569
|
+
{
|
|
10570
|
+
name: "hanoman_member_update",
|
|
10571
|
+
title: "Ubah anggota tim",
|
|
10572
|
+
description: "Mengubah nama, peran, atau status aktif seorang anggota. `email` TIDAK bisa diubah dan ditolak eksplisit 400: id diturunkan darinya dan sync tak punya operasi rename, jadi id yang berubah meninggalkan baris yatim di setiap mesin lain. Ganti email = hapus lalu buat baru. Menonaktifkan TIDAK melepas kartu-kartunya.",
|
|
10573
|
+
inputSchema: obj({
|
|
10574
|
+
properties: {
|
|
10575
|
+
member: MEMBER_ID,
|
|
10576
|
+
name: str("Nama baru."),
|
|
10577
|
+
role: str("Peran baru." + CLEAR),
|
|
10578
|
+
active: bool("false = nonaktif: tetap terdaftar dan tetap memegang kartunya, hanya turun di daftar.")
|
|
10579
|
+
},
|
|
10580
|
+
required: ["member"]
|
|
10581
|
+
}),
|
|
10582
|
+
mode: "write",
|
|
10583
|
+
capability: "team:write",
|
|
10584
|
+
samplePath: "/members/a@b.id",
|
|
10585
|
+
sampleMethod: "PATCH",
|
|
10586
|
+
build: (a) => {
|
|
10587
|
+
const body = {};
|
|
10588
|
+
if (s(a.name)) body.name = s(a.name);
|
|
10589
|
+
if (typeof a.active === "boolean") body.active = a.active;
|
|
10590
|
+
const role = nullable(a.role);
|
|
10591
|
+
if (role !== void 0) body.role = role;
|
|
10592
|
+
return { method: "PATCH", path: `/members/${enc(String(a.member))}`, body };
|
|
10593
|
+
},
|
|
10594
|
+
shape: (raw) => shapeMember(raw)
|
|
10595
|
+
}
|
|
10596
|
+
];
|
|
10597
|
+
DESTRUCTIVE = [
|
|
10598
|
+
{
|
|
10599
|
+
name: "hanoman_task_delete",
|
|
10600
|
+
title: "Hapus kartu papan Tim (BERBAHAYA)",
|
|
10601
|
+
description: "BERBAHAYA \u2014 menghapus kartu papan Tim secara permanen di SELURUH mesin yang tersync. Backlog item yang tertaut TIDAK ikut terhapus. Untuk sekadar mengarsipkan, pindahkan kartunya ke kolom `done` lewat hanoman_task_update. Hanya muncul saat tingkat `--danger` menyala.",
|
|
10602
|
+
inputSchema: obj({ properties: { task: TASK_ID }, required: ["task"] }),
|
|
10603
|
+
mode: "danger",
|
|
10604
|
+
capability: "team:write",
|
|
10605
|
+
samplePath: "/tasks/t1",
|
|
10606
|
+
sampleMethod: "DELETE",
|
|
10607
|
+
build: (a) => ({ method: "DELETE", path: `/tasks/${enc(String(a.task))}` }),
|
|
10608
|
+
shape: (raw) => raw
|
|
10609
|
+
},
|
|
10610
|
+
{
|
|
10611
|
+
name: "hanoman_member_delete",
|
|
10612
|
+
title: "Hapus anggota tim (BERBAHAYA)",
|
|
10613
|
+
description: "BERBAHAYA \u2014 menghapus anggota secara permanen di SELURUH mesin yang tersync. Kartu-kartunya TIDAK ikut terhapus: penugasannya dikosongkan (`memberId` jadi null), dan tak ada jejak siapa pemegangnya sebelum itu. Untuk orang yang sekadar berhenti aktif, pakai hanoman_member_update dengan `active: false` \u2014 ia tetap terdaftar dan kartunya tetap bernama. Hanya muncul saat tingkat `--danger` menyala.",
|
|
10614
|
+
inputSchema: obj({ properties: { member: MEMBER_ID }, required: ["member"] }),
|
|
10615
|
+
mode: "danger",
|
|
10616
|
+
capability: "team:write",
|
|
10617
|
+
samplePath: "/members/a@b.id",
|
|
10618
|
+
sampleMethod: "DELETE",
|
|
10619
|
+
build: (a) => ({ method: "DELETE", path: `/members/${enc(String(a.member))}` }),
|
|
10620
|
+
shape: (raw) => raw
|
|
10621
|
+
}
|
|
10622
|
+
];
|
|
10623
|
+
TEAM_TOOLS = [...TASKS, ...MEMBERS, ...DESTRUCTIVE];
|
|
10624
|
+
}
|
|
10625
|
+
});
|
|
10626
|
+
|
|
10627
|
+
// ../shared/src/mcp-catalog/system.ts
|
|
10628
|
+
var SYSTEM_TOOLS;
|
|
10629
|
+
var init_system = __esm({
|
|
10630
|
+
"../shared/src/mcp-catalog/system.ts"() {
|
|
10631
|
+
"use strict";
|
|
10632
|
+
init_mcp_schema();
|
|
10633
|
+
SYSTEM_TOOLS = [
|
|
10634
|
+
{
|
|
10635
|
+
name: "hanoman_limits",
|
|
10636
|
+
title: "Sisa kuota langganan Claude",
|
|
10637
|
+
description: "Sisa kuota langganan Claude milik mesin ini berikut kapan jendelanya reset. Dibaca realtime dan di-cache 30 detik di server. Dipakai untuk memutuskan APAKAH pekerjaan berat layak dimulai sekarang \u2014 bukan gerbang: hanoman tak pernah menolak membuka sesi karena angka ini. Tak menuntut capability apa pun.",
|
|
10638
|
+
inputSchema: obj({ properties: {} }),
|
|
10639
|
+
mode: "read",
|
|
10640
|
+
capability: null,
|
|
10641
|
+
samplePath: "/limits",
|
|
10642
|
+
sampleMethod: "GET",
|
|
10643
|
+
build: () => ({ method: "GET", path: "/limits" }),
|
|
10644
|
+
shape: (raw) => raw
|
|
10645
|
+
},
|
|
10646
|
+
{
|
|
10647
|
+
name: "hanoman_limits_codex",
|
|
10648
|
+
title: "Sisa kuota Codex",
|
|
10649
|
+
description: "Sisa kuota codex, dibaca dari snapshot `rate_limits` di rollout sesi codex terakhir \u2014 TANPA panggilan jaringan, jadi kesegarannya seumur sesi codex terakhir di mesin ini, bukan realtime. Endpoint terpisah dari hanoman_limits karena sumber dan semantik kesegarannya memang beda. Tak menuntut capability apa pun.",
|
|
10650
|
+
inputSchema: obj({ properties: {} }),
|
|
10651
|
+
mode: "read",
|
|
10652
|
+
capability: null,
|
|
10653
|
+
samplePath: "/limits/codex",
|
|
10654
|
+
sampleMethod: "GET",
|
|
10655
|
+
build: () => ({ method: "GET", path: "/limits/codex" }),
|
|
10656
|
+
shape: (raw) => raw
|
|
10657
|
+
},
|
|
10658
|
+
{
|
|
10659
|
+
name: "hanoman_update_status",
|
|
10660
|
+
title: "Status versi & pembaruan",
|
|
10661
|
+
description: "Versi hanoman yang berjalan, versi terbaru di registry npm, dan apakah instance ini PUNYA supervisor yang bisa memasangnya (`canApply`). Baca-saja: memasang pembaruan me-restart instance dan sengaja tetap tindakan manusia di dashboard \u2014 tak ada tool untuk itu, apa pun tingkat modenya. Tak menuntut capability apa pun.",
|
|
10662
|
+
inputSchema: obj({ properties: {} }),
|
|
10663
|
+
mode: "read",
|
|
10664
|
+
capability: null,
|
|
10665
|
+
samplePath: "/update",
|
|
10666
|
+
sampleMethod: "GET",
|
|
10667
|
+
build: () => ({ method: "GET", path: "/update" }),
|
|
10668
|
+
shape: (raw) => raw
|
|
10669
|
+
},
|
|
10670
|
+
{
|
|
10671
|
+
name: "hanoman_fs_browse",
|
|
10672
|
+
title: "Telusuri folder mesin ini",
|
|
10673
|
+
description: "Sub-folder nyata di MESIN tempat hanoman berjalan, berikut path absolutnya \u2014 dipakai untuk mengisi `repoDir` saat mengikat project ke codebase yang sudah ada (hanoman_project_binding_set), yang tanpa ini hanya bisa ditebak. Hanya direktori, berkas tak ikut; yang berawalan titik disembunyikan. Tanpa `path`, ia mulai dari home. Tak menuntut capability apa pun.",
|
|
10674
|
+
inputSchema: obj({
|
|
10675
|
+
properties: {
|
|
10676
|
+
path: str("Path absolut folder yang dibuka. Kosong = folder home pengguna yang menjalankan hanoman. Folder yang tak terbaca menjawab 400, bukan daftar kosong.")
|
|
10677
|
+
}
|
|
10678
|
+
}),
|
|
10679
|
+
mode: "read",
|
|
10680
|
+
capability: null,
|
|
10681
|
+
samplePath: "/fs/browse",
|
|
10682
|
+
sampleMethod: "GET",
|
|
10683
|
+
build: (a) => ({
|
|
10684
|
+
method: "GET",
|
|
10685
|
+
path: "/fs/browse",
|
|
10686
|
+
...typeof a.path === "string" && a.path !== "" ? { query: { path: a.path } } : {}
|
|
10687
|
+
}),
|
|
10688
|
+
shape: (raw) => raw
|
|
10689
|
+
}
|
|
10690
|
+
];
|
|
10691
|
+
}
|
|
10692
|
+
});
|
|
10693
|
+
|
|
10300
10694
|
// ../shared/src/mcp-catalog/index.ts
|
|
10301
10695
|
var MCP_TOOLS;
|
|
10302
10696
|
var init_mcp_catalog = __esm({
|
|
@@ -10317,6 +10711,8 @@ var init_mcp_catalog = __esm({
|
|
|
10317
10711
|
init_notifications();
|
|
10318
10712
|
init_support();
|
|
10319
10713
|
init_lead2();
|
|
10714
|
+
init_team2();
|
|
10715
|
+
init_system();
|
|
10320
10716
|
MCP_TOOLS = [
|
|
10321
10717
|
...ABOUT_TOOLS,
|
|
10322
10718
|
...PROJECTS_TOOLS,
|
|
@@ -10330,7 +10726,12 @@ var init_mcp_catalog = __esm({
|
|
|
10330
10726
|
...SESSIONS_TOOLS,
|
|
10331
10727
|
...NOTIFICATIONS_TOOLS,
|
|
10332
10728
|
...SUPPORT_TOOLS,
|
|
10333
|
-
...LEAD_TOOLS
|
|
10729
|
+
...LEAD_TOOLS,
|
|
10730
|
+
// ADR-0157 · papan Tim (kerja manusia) lalu status instance. Keduanya di ekor daftar dengan
|
|
10731
|
+
// sengaja: urutan di sini = urutan yang dibaca model, dan pekerjaan sehari-hari agen tetap
|
|
10732
|
+
// backlog/sesi/IDE — bukan kartu orang lain.
|
|
10733
|
+
...TEAM_TOOLS,
|
|
10734
|
+
...SYSTEM_TOOLS
|
|
10334
10735
|
];
|
|
10335
10736
|
}
|
|
10336
10737
|
});
|
|
@@ -10568,50 +10969,6 @@ var init_presence = __esm({
|
|
|
10568
10969
|
}
|
|
10569
10970
|
});
|
|
10570
10971
|
|
|
10571
|
-
// ../shared/src/team.ts
|
|
10572
|
-
var TASK_STATUSES, zTaskStatus, zEmail, zName, zIso, zCreateMember, zPatchMember, zCreateTask, zPatchTask, ESCALATE_SOURCES, zEscalateTask;
|
|
10573
|
-
var init_team = __esm({
|
|
10574
|
-
"../shared/src/team.ts"() {
|
|
10575
|
-
"use strict";
|
|
10576
|
-
init_zod();
|
|
10577
|
-
init_enums();
|
|
10578
|
-
TASK_STATUSES = ["backlog", "doing", "review", "done"];
|
|
10579
|
-
zTaskStatus = external_exports.enum(TASK_STATUSES);
|
|
10580
|
-
zEmail = external_exports.string().trim().min(3).max(200).email();
|
|
10581
|
-
zName = external_exports.string().trim().min(1).max(120);
|
|
10582
|
-
zIso = external_exports.string().datetime({ offset: true });
|
|
10583
|
-
zCreateMember = external_exports.object({
|
|
10584
|
-
name: zName,
|
|
10585
|
-
email: zEmail,
|
|
10586
|
-
role: external_exports.string().trim().max(60).nullable().optional()
|
|
10587
|
-
});
|
|
10588
|
-
zPatchMember = zCreateMember.omit({ email: true }).partial().extend({
|
|
10589
|
-
active: external_exports.boolean().optional()
|
|
10590
|
-
});
|
|
10591
|
-
zCreateTask = external_exports.object({
|
|
10592
|
-
title: external_exports.string().trim().min(1).max(300),
|
|
10593
|
-
detail: external_exports.string().max(2e4).nullable().optional(),
|
|
10594
|
-
projectId: external_exports.string().max(120).nullable().optional(),
|
|
10595
|
-
status: zTaskStatus.default("backlog"),
|
|
10596
|
-
priority: zPriority.default("sedang"),
|
|
10597
|
-
memberId: external_exports.string().max(200).nullable().optional(),
|
|
10598
|
-
startDate: zIso.nullable().optional(),
|
|
10599
|
-
dueDate: zIso.nullable().optional(),
|
|
10600
|
-
order: external_exports.number().finite().optional()
|
|
10601
|
-
});
|
|
10602
|
-
zPatchTask = zCreateTask.partial();
|
|
10603
|
-
ESCALATE_SOURCES = ["brief", "qa", "audit"];
|
|
10604
|
-
zEscalateTask = external_exports.object({
|
|
10605
|
-
source: external_exports.enum(ESCALATE_SOURCES).default("brief"),
|
|
10606
|
-
priority: zPriority.default("sedang"),
|
|
10607
|
-
// Dipakai HANYA saat kartunya belum punya project (`nextSpecId` butuh repo, dan repo milik
|
|
10608
|
-
// project). Route menolak 400 bila ia menyebut project LAIN — kartu tak boleh berpindah project
|
|
10609
|
-
// sebagai efek samping yang tak diminta.
|
|
10610
|
-
projectId: external_exports.string().max(120).optional()
|
|
10611
|
-
});
|
|
10612
|
-
}
|
|
10613
|
-
});
|
|
10614
|
-
|
|
10615
10972
|
// ../shared/src/index.ts
|
|
10616
10973
|
var init_src = __esm({
|
|
10617
10974
|
"../shared/src/index.ts"() {
|
|
@@ -16880,7 +17237,7 @@ function exactOptional(innerType) {
|
|
|
16880
17237
|
innerType
|
|
16881
17238
|
});
|
|
16882
17239
|
}
|
|
16883
|
-
function
|
|
17240
|
+
function nullable2(innerType) {
|
|
16884
17241
|
return new ZodNullable2({
|
|
16885
17242
|
type: "nullable",
|
|
16886
17243
|
innerType
|
|
@@ -17026,10 +17383,10 @@ var init_schemas2 = __esm({
|
|
|
17026
17383
|
return exactOptional(this);
|
|
17027
17384
|
},
|
|
17028
17385
|
nullable() {
|
|
17029
|
-
return
|
|
17386
|
+
return nullable2(this);
|
|
17030
17387
|
},
|
|
17031
17388
|
nullish() {
|
|
17032
|
-
return optional(
|
|
17389
|
+
return optional(nullable2(this));
|
|
17033
17390
|
},
|
|
17034
17391
|
nonoptional(params) {
|
|
17035
17392
|
return nonoptional(this, params);
|