hanoman 0.9.12 → 0.9.14
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 +337 -35
- package/dist/server.js +20798 -17844
- package/package.json +1 -1
- package/prisma/migrations/20261001081455_qa_workspace/migration.sql +85 -0
- package/prisma/migrations/20261001093314_qa_attachment_sync/migration.sql +31 -0
- package/prisma/schema.prisma +97 -0
- package/web/assets/{ChangelogScreen-dSx0GN6n.js → ChangelogScreen-CS_GSORI.js} +1 -1
- package/web/assets/{DalangHanomanScreen-CxluNxA4.js → DalangHanomanScreen-maIEXuvi.js} +1 -1
- package/web/assets/{DocPreviewModal-Dh5W00Jj.js → DocPreviewModal-DFqCURCs.js} +1 -1
- package/web/assets/{DocsWorkspace-BmNqLMpK.js → DocsWorkspace-hxJ037xu.js} +1 -1
- package/web/assets/{IdeScreen-HMV7PR16.js → IdeScreen-CHXLJ0Az.js} +1 -1
- package/web/assets/{LeadScreen-KY5Xm_nj.js → LeadScreen-DI8TW6QX.js} +1 -1
- package/web/assets/{ReviewScreen-eeSPJSzp.js → ReviewScreen-CAi2WDfW.js} +1 -1
- package/web/assets/{SchedulerScreen-B3Co7Nqy.js → SchedulerScreen-D1vCNuRr.js} +1 -1
- package/web/assets/{SettingsScreen-CGED6Nhv.js → SettingsScreen-1GM6-O69.js} +4 -4
- package/web/assets/{TeamScreen-C-5AYuby.js → TeamScreen-eYlsFpzN.js} +1 -1
- package/web/assets/{TerminalScreen-O9aIsNxm.js → TerminalScreen-u6e-6CB7.js} +1 -1
- package/web/assets/{TriageScreen-B7cDwzbY.js → TriageScreen-B6czWJOz.js} +1 -1
- package/web/assets/{VpsScreen-BYjIBuH2.js → VpsScreen-CaYPux2B.js} +1 -1
- package/web/assets/index--wY0Cvay.js +1043 -0
- package/web/assets/{live-Ck3G14iM.js → live-WWHlTNwv.js} +1 -1
- package/web/index.html +1 -1
- package/web/assets/index-CvvGNA1i.js +0 -1017
package/dist/build-info.json
CHANGED
package/dist/cli.js
CHANGED
|
@@ -3626,23 +3626,23 @@ var init_types2 = __esm({
|
|
|
3626
3626
|
}
|
|
3627
3627
|
if (effect.type === "transform") {
|
|
3628
3628
|
if (ctx.common.async === false) {
|
|
3629
|
-
const
|
|
3629
|
+
const base2 = this._def.schema._parseSync({
|
|
3630
3630
|
data: ctx.data,
|
|
3631
3631
|
path: ctx.path,
|
|
3632
3632
|
parent: ctx
|
|
3633
3633
|
});
|
|
3634
|
-
if (!isValid(
|
|
3634
|
+
if (!isValid(base2))
|
|
3635
3635
|
return INVALID;
|
|
3636
|
-
const result = effect.transform(
|
|
3636
|
+
const result = effect.transform(base2.value, checkCtx);
|
|
3637
3637
|
if (result instanceof Promise) {
|
|
3638
3638
|
throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
|
|
3639
3639
|
}
|
|
3640
3640
|
return { status: status.value, value: result };
|
|
3641
3641
|
} else {
|
|
3642
|
-
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((
|
|
3643
|
-
if (!isValid(
|
|
3642
|
+
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base2) => {
|
|
3643
|
+
if (!isValid(base2))
|
|
3644
3644
|
return INVALID;
|
|
3645
|
-
return Promise.resolve(effect.transform(
|
|
3645
|
+
return Promise.resolve(effect.transform(base2.value, checkCtx)).then((result) => ({
|
|
3646
3646
|
status: status.value,
|
|
3647
3647
|
value: result
|
|
3648
3648
|
}));
|
|
@@ -5202,6 +5202,8 @@ var init_agent = __esm({
|
|
|
5202
5202
|
// "Backlog — tulis" diam-diam membuka papan orang, dan sebaliknya.
|
|
5203
5203
|
"team:read",
|
|
5204
5204
|
"team:write",
|
|
5205
|
+
"qa:read",
|
|
5206
|
+
"qa:write",
|
|
5205
5207
|
// ADR-0155 · akses KETIGA: `danger`. Dipecah dari `:write` karena keempat operasi ini bukan
|
|
5206
5208
|
// "menulis lebih banyak", melainkan menjalankan sesuatu di luar proses hanoman — sesi agen di
|
|
5207
5209
|
// worktree, perintah di VPS, git yang mengubah sejarah, penghapusan artefak dokumen.
|
|
@@ -7289,9 +7291,9 @@ var init_api = __esm({
|
|
|
7289
7291
|
branches: (id) => `${API}/projects/${id}/branches`,
|
|
7290
7292
|
// SPEC-360 · ADR-0077 · daftar branch (nilai turunan git) + hapus batch local/origin.
|
|
7291
7293
|
// SPEC-859 · `include=all` memuat branch yang belum ter-merge; tanpa itu hanya yang ter-merge.
|
|
7292
|
-
branchesUnused: (id,
|
|
7294
|
+
branchesUnused: (id, base2, include) => {
|
|
7293
7295
|
const q = new URLSearchParams();
|
|
7294
|
-
if (
|
|
7296
|
+
if (base2) q.set("base", base2);
|
|
7295
7297
|
if (include) q.set("include", include);
|
|
7296
7298
|
const s2 = q.toString();
|
|
7297
7299
|
return `${API}/projects/${id}/branches/unused${s2 ? `?${s2}` : ""}`;
|
|
@@ -7367,7 +7369,7 @@ var init_api = __esm({
|
|
|
7367
7369
|
// SPEC-233 · remote mgmt + pr-url + archive
|
|
7368
7370
|
ideRemotes: (id) => `${API}/projects/${id}/remotes`,
|
|
7369
7371
|
ideRemote: (id, name) => `${API}/projects/${id}/remotes/${encodeURIComponent(name)}`,
|
|
7370
|
-
idePrUrl: (id, branch,
|
|
7372
|
+
idePrUrl: (id, branch, base2) => `${API}/projects/${id}/pr-url?branch=${encodeURIComponent(branch)}${base2 ? `&base=${encodeURIComponent(base2)}` : ""}`,
|
|
7371
7373
|
ideArchive: (id, ref, format = "zip") => `${API}/projects/${id}/archive?ref=${encodeURIComponent(ref)}&format=${format}`,
|
|
7372
7374
|
ideCommit: (id, sha) => `${API}/projects/${id}/commit/${sha}`,
|
|
7373
7375
|
// SPEC-233 · diff satu file di commit (vs parent)
|
|
@@ -7515,6 +7517,20 @@ var init_api = __esm({
|
|
|
7515
7517
|
skillFile: (key) => `${API}/skills/${encodeURIComponent(key)}/file`,
|
|
7516
7518
|
skillEntry: (key) => `${API}/skills/${encodeURIComponent(key)}/entry`,
|
|
7517
7519
|
skillFork: (key) => `${API}/skills/${encodeURIComponent(key)}/fork`,
|
|
7520
|
+
// Workspace QA · laporan QA per project. Semua di bawah /projects/:id/qa (capability `qa`).
|
|
7521
|
+
qaReports: (pid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports`,
|
|
7522
|
+
qaReport: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}`,
|
|
7523
|
+
qaCases: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/cases`,
|
|
7524
|
+
qaCase: (pid, rid, cid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/cases/${encodeURIComponent(cid)}`,
|
|
7525
|
+
qaFindings: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/findings`,
|
|
7526
|
+
qaFinding: (pid, rid, fid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/findings/${encodeURIComponent(fid)}`,
|
|
7527
|
+
qaAttachments: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/attachments`,
|
|
7528
|
+
qaAttachment: (pid, rid, aid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/attachments/${encodeURIComponent(aid)}`,
|
|
7529
|
+
qaFindingBacklog: (pid, rid, fid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/findings/${encodeURIComponent(fid)}/backlog`,
|
|
7530
|
+
qaReportBacklog: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/backlog`,
|
|
7531
|
+
qaCasesImport: (pid, rid) => `${API}/projects/${encodeURIComponent(pid)}/qa/reports/${encodeURIComponent(rid)}/cases/import`,
|
|
7532
|
+
qaImport: (pid) => `${API}/projects/${encodeURIComponent(pid)}/qa/import`,
|
|
7533
|
+
qaTemplate: `${API}/qa/template.xlsx`,
|
|
7518
7534
|
// SPEC-945 · ADR-0150 · papan tim. `members` GLOBAL (bukan per project) — task boleh tanpa
|
|
7519
7535
|
// project, jadi direktori orang tak bisa digantung pada project.
|
|
7520
7536
|
members: `${API}/members`,
|
|
@@ -7572,7 +7588,7 @@ var init_api = __esm({
|
|
|
7572
7588
|
githubIssueUnlink: (id) => `${API}/github-issues/${encodeURIComponent(id)}/unlink`,
|
|
7573
7589
|
// SPEC-361 · ADR-0078 · unduh dokumen: query ditempelkan ke URL endpoint dokumen yang sudah ada
|
|
7574
7590
|
// (tak ada endpoint ekspor terpisah). `base` bisa sudah membawa query, mis. ideFile(?path=…).
|
|
7575
|
-
download: (
|
|
7591
|
+
download: (base2, fmt) => `${base2}${base2.includes("?") ? "&" : "?"}download=${fmt}`
|
|
7576
7592
|
};
|
|
7577
7593
|
}
|
|
7578
7594
|
});
|
|
@@ -7931,6 +7947,69 @@ var init_semver = __esm({
|
|
|
7931
7947
|
}
|
|
7932
7948
|
});
|
|
7933
7949
|
|
|
7950
|
+
// ../shared/src/qa.ts
|
|
7951
|
+
var QA_REPORT_STATUSES, QA_VERDICTS, QA_CASE_STATUSES, QA_SEVERITIES, QA_PRIORITIES, zQaEnvironment, zSteps, zCreateQaReport, zPatchQaReport, zCreateQaCase, zPatchQaCase, zCreateQaFinding, zPatchQaFinding;
|
|
7952
|
+
var init_qa = __esm({
|
|
7953
|
+
"../shared/src/qa.ts"() {
|
|
7954
|
+
"use strict";
|
|
7955
|
+
init_zod();
|
|
7956
|
+
QA_REPORT_STATUSES = ["draft", "submitted", "closed"];
|
|
7957
|
+
QA_VERDICTS = ["go", "no-go", "conditional"];
|
|
7958
|
+
QA_CASE_STATUSES = ["todo", "pass", "fail", "blocked", "skipped"];
|
|
7959
|
+
QA_SEVERITIES = ["blocker", "critical", "major", "minor", "trivial"];
|
|
7960
|
+
QA_PRIORITIES = ["P0", "P1", "P2", "P3"];
|
|
7961
|
+
zQaEnvironment = external_exports.record(external_exports.string().trim().min(1).max(60), external_exports.string().max(500)).refine((e) => Object.keys(e).length <= 20, "maksimal 20 entri environment");
|
|
7962
|
+
zSteps = external_exports.array(external_exports.string().trim().min(1).max(2e3)).max(50);
|
|
7963
|
+
zCreateQaReport = external_exports.object({
|
|
7964
|
+
title: external_exports.string().trim().min(1).max(300),
|
|
7965
|
+
buildVersion: external_exports.string().trim().max(120).default(""),
|
|
7966
|
+
environment: zQaEnvironment.default({}),
|
|
7967
|
+
scope: external_exports.string().max(5e3).default(""),
|
|
7968
|
+
tester: external_exports.string().trim().max(200).default(""),
|
|
7969
|
+
summary: external_exports.string().max(2e4).default(""),
|
|
7970
|
+
verdict: external_exports.enum(QA_VERDICTS).nullable().default(null)
|
|
7971
|
+
});
|
|
7972
|
+
zPatchQaReport = zCreateQaReport.partial().extend({ status: external_exports.enum(QA_REPORT_STATUSES).optional() });
|
|
7973
|
+
zCreateQaCase = external_exports.object({
|
|
7974
|
+
title: external_exports.string().trim().min(1).max(300),
|
|
7975
|
+
steps: external_exports.string().max(1e4).default(""),
|
|
7976
|
+
expected: external_exports.string().max(1e4).default(""),
|
|
7977
|
+
actual: external_exports.string().max(1e4).default(""),
|
|
7978
|
+
status: external_exports.enum(QA_CASE_STATUSES).default("todo"),
|
|
7979
|
+
order: external_exports.number().finite().optional()
|
|
7980
|
+
});
|
|
7981
|
+
zPatchQaCase = zCreateQaCase.partial();
|
|
7982
|
+
zCreateQaFinding = external_exports.object({
|
|
7983
|
+
title: external_exports.string().trim().min(1).max(300),
|
|
7984
|
+
caseId: external_exports.string().max(120).nullable().default(null),
|
|
7985
|
+
severity: external_exports.enum(QA_SEVERITIES).default("major"),
|
|
7986
|
+
priority: external_exports.enum(QA_PRIORITIES).default("P2"),
|
|
7987
|
+
area: external_exports.string().trim().max(120).default(""),
|
|
7988
|
+
steps: zSteps.default([]),
|
|
7989
|
+
expected: external_exports.string().max(1e4).default(""),
|
|
7990
|
+
actual: external_exports.string().max(1e4).default(""),
|
|
7991
|
+
status: external_exports.enum(["open", "wontfix"]).default("open")
|
|
7992
|
+
});
|
|
7993
|
+
zPatchQaFinding = zCreateQaFinding.partial();
|
|
7994
|
+
}
|
|
7995
|
+
});
|
|
7996
|
+
|
|
7997
|
+
// ../shared/src/qa-markdown.ts
|
|
7998
|
+
var init_qa_markdown = __esm({
|
|
7999
|
+
"../shared/src/qa-markdown.ts"() {
|
|
8000
|
+
"use strict";
|
|
8001
|
+
init_qa();
|
|
8002
|
+
}
|
|
8003
|
+
});
|
|
8004
|
+
|
|
8005
|
+
// ../shared/src/qa-tabular.ts
|
|
8006
|
+
var init_qa_tabular = __esm({
|
|
8007
|
+
"../shared/src/qa-tabular.ts"() {
|
|
8008
|
+
"use strict";
|
|
8009
|
+
init_qa();
|
|
8010
|
+
}
|
|
8011
|
+
});
|
|
8012
|
+
|
|
7934
8013
|
// ../shared/src/webhook.ts
|
|
7935
8014
|
var WEBHOOK_BACKOFF_SEC, WEBHOOK_MAX_ATTEMPTS, WEBHOOK_MAX_BYTES, WEBHOOK_PING_TYPE, WEBHOOK_ENTITIES, BY_MODEL, WEBHOOK_EVENTS, zCreateWebhookEndpoint, zUpdateWebhookEndpoint;
|
|
7936
8015
|
var init_webhook = __esm({
|
|
@@ -9183,8 +9262,8 @@ var init_backlog = __esm({
|
|
|
9183
9262
|
sampleMethod: "GET",
|
|
9184
9263
|
build: (a) => {
|
|
9185
9264
|
const path = s(a.path);
|
|
9186
|
-
const
|
|
9187
|
-
return { method: "GET", path: path ? `${
|
|
9265
|
+
const base2 = `/specs/${enc(String(a.spec))}/review`;
|
|
9266
|
+
return { method: "GET", path: path ? `${base2}/${String(path).split("/").map(enc).join("/")}` : base2 };
|
|
9188
9267
|
},
|
|
9189
9268
|
shape: (raw) => raw
|
|
9190
9269
|
},
|
|
@@ -9861,8 +9940,8 @@ var init_ide = __esm({
|
|
|
9861
9940
|
sampleMethod: "GET",
|
|
9862
9941
|
build: (a) => {
|
|
9863
9942
|
const path = s(a.path);
|
|
9864
|
-
const
|
|
9865
|
-
return path ? { method: "GET", path: `${
|
|
9943
|
+
const base2 = `${p(a.project)}/commit/${enc(String(a.sha))}`;
|
|
9944
|
+
return path ? { method: "GET", path: `${base2}/file`, query: { path } } : { method: "GET", path: base2 };
|
|
9866
9945
|
},
|
|
9867
9946
|
shape: (raw) => raw
|
|
9868
9947
|
},
|
|
@@ -11099,8 +11178,8 @@ var init_sessions = __esm({
|
|
|
11099
11178
|
sampleMethod: "GET",
|
|
11100
11179
|
build: (a) => {
|
|
11101
11180
|
const path = s(a.path);
|
|
11102
|
-
const
|
|
11103
|
-
return { method: "GET", path: path ? `${
|
|
11181
|
+
const base2 = `/terminal/sessions/${enc(String(a.session))}/review`;
|
|
11182
|
+
return { method: "GET", path: path ? `${base2}/${String(path).split("/").map(enc).join("/")}` : base2 };
|
|
11104
11183
|
},
|
|
11105
11184
|
shape: (raw) => raw
|
|
11106
11185
|
},
|
|
@@ -12054,6 +12133,216 @@ var init_team2 = __esm({
|
|
|
12054
12133
|
}
|
|
12055
12134
|
});
|
|
12056
12135
|
|
|
12136
|
+
// ../shared/src/mcp-catalog/qa.ts
|
|
12137
|
+
var PROJECT3, REPORT, CASE, FINDING, base, one, parseEnv, parseSteps, nullable2, put, REPORT_FIELDS, reportBody, caseBody, findingBody, CASE_FIELDS, FINDING_FIELDS, QA_TOOLS;
|
|
12138
|
+
var init_qa2 = __esm({
|
|
12139
|
+
"../shared/src/mcp-catalog/qa.ts"() {
|
|
12140
|
+
"use strict";
|
|
12141
|
+
init_qa();
|
|
12142
|
+
init_mcp_schema();
|
|
12143
|
+
init_helpers();
|
|
12144
|
+
PROJECT3 = str("Id project, seperti muncul di hanoman_projects_list.");
|
|
12145
|
+
REPORT = str("Id laporan QA (cuid), seperti muncul di hanoman_qa_reports_list \u2014 BUKAN nomor tampil `QA-007`, yang dihitung saat render.");
|
|
12146
|
+
CASE = str("Id test case (cuid), dari hanoman_qa_report_get.");
|
|
12147
|
+
FINDING = str("Id temuan (cuid), dari hanoman_qa_report_get.");
|
|
12148
|
+
base = (a) => `/projects/${enc(String(a.project))}/qa/reports`;
|
|
12149
|
+
one = (a) => `${base(a)}/${enc(String(a.report))}`;
|
|
12150
|
+
parseEnv = (v) => {
|
|
12151
|
+
if (typeof v !== "string" || !v.trim()) return void 0;
|
|
12152
|
+
const out = {};
|
|
12153
|
+
for (const part of v.split(/[\n;]/)) {
|
|
12154
|
+
const t = part.trim();
|
|
12155
|
+
if (!t) continue;
|
|
12156
|
+
const i = t.indexOf("=");
|
|
12157
|
+
const k = (i < 0 ? t : t.slice(0, i)).trim();
|
|
12158
|
+
if (k) out[k] = i < 0 ? "" : t.slice(i + 1).trim();
|
|
12159
|
+
}
|
|
12160
|
+
return Object.keys(out).length ? out : void 0;
|
|
12161
|
+
};
|
|
12162
|
+
parseSteps = (v) => typeof v === "string" && v.trim() ? v.split("\n").map((l) => l.trim().replace(/^(?:\d+[.)]|[-*])\s+/, "")).filter(Boolean) : void 0;
|
|
12163
|
+
nullable2 = (v) => typeof v !== "string" ? void 0 : v === "" ? null : v;
|
|
12164
|
+
put = (body, key, v) => {
|
|
12165
|
+
if (v !== void 0) body[key] = v;
|
|
12166
|
+
};
|
|
12167
|
+
REPORT_FIELDS = {
|
|
12168
|
+
title: str("Judul laporan, satu baris."),
|
|
12169
|
+
buildVersion: str("Versi/build yang diuji, mis. `0.9.12`."),
|
|
12170
|
+
environment: str("Lingkungan uji: `kunci=nilai` dipisah baris atau `;`, mis. `os=macOS 15; browser=Chrome 130; url=https://staging.x.id`. Maks 20 entri."),
|
|
12171
|
+
scope: str("Cakupan: apa yang diuji dan apa yang sengaja tidak."),
|
|
12172
|
+
tester: str("Nama penguji."),
|
|
12173
|
+
summary: str("Ringkasan hasil, 2\u20133 kalimat, memuat alasan keputusan."),
|
|
12174
|
+
verdict: enumStr(QA_VERDICTS, "Keputusan rilis. Wajib terisi sebelum `status` boleh `submitted`/`closed`.")
|
|
12175
|
+
};
|
|
12176
|
+
reportBody = (a) => {
|
|
12177
|
+
const b = {};
|
|
12178
|
+
for (const k of ["title", "buildVersion", "scope", "tester", "summary", "verdict", "status"]) put(b, k, s(a[k]));
|
|
12179
|
+
put(b, "environment", parseEnv(a.environment));
|
|
12180
|
+
return b;
|
|
12181
|
+
};
|
|
12182
|
+
caseBody = (a) => {
|
|
12183
|
+
const b = {};
|
|
12184
|
+
for (const k of ["title", "steps", "expected", "actual", "status"]) put(b, k, s(a[k]));
|
|
12185
|
+
return b;
|
|
12186
|
+
};
|
|
12187
|
+
findingBody = (a) => {
|
|
12188
|
+
const b = {};
|
|
12189
|
+
for (const k of ["title", "severity", "priority", "area", "expected", "actual", "status"]) put(b, k, s(a[k]));
|
|
12190
|
+
put(b, "steps", parseSteps(a.steps));
|
|
12191
|
+
put(b, "caseId", nullable2(a.testCase));
|
|
12192
|
+
return b;
|
|
12193
|
+
};
|
|
12194
|
+
CASE_FIELDS = {
|
|
12195
|
+
title: str("Judul test case."),
|
|
12196
|
+
steps: str("Langkah uji (teks bebas)."),
|
|
12197
|
+
expected: str("Hasil yang diharapkan."),
|
|
12198
|
+
actual: str("Hasil aktual."),
|
|
12199
|
+
status: enumStr(QA_CASE_STATUSES, "Status eksekusi.")
|
|
12200
|
+
};
|
|
12201
|
+
FINDING_FIELDS = {
|
|
12202
|
+
title: str("Judul temuan \u2014 satu masalah, satu baris."),
|
|
12203
|
+
severity: enumStr(QA_SEVERITIES, "Dampak TEKNIS. Bukan prioritas."),
|
|
12204
|
+
priority: enumStr(QA_PRIORITIES, "Urutan perbaikan P0 (paling mendesak)\u2013P3. Terpisah dari severity: bug minor di halaman checkout bisa P0."),
|
|
12205
|
+
area: str("Area/fitur, mis. `checkout`."),
|
|
12206
|
+
steps: str("Langkah reproduksi, SATU PER BARIS (penomoran `1.` di awal dibuang; server menomori)."),
|
|
12207
|
+
expected: str("Yang seharusnya terjadi."),
|
|
12208
|
+
actual: str("Yang benar-benar terjadi."),
|
|
12209
|
+
status: enumStr(["open", "wontfix"], "`open` atau `wontfix`. `sent` (sudah jadi backlog) hanya ditulis server."),
|
|
12210
|
+
testCase: str("Id test case terkait, dari hanoman_qa_report_get. String KOSONG melepas tautan; tak menyebutnya membiarkan apa adanya.")
|
|
12211
|
+
};
|
|
12212
|
+
QA_TOOLS = [
|
|
12213
|
+
{
|
|
12214
|
+
name: "hanoman_qa_reports_list",
|
|
12215
|
+
title: "Daftar laporan QA",
|
|
12216
|
+
description: "Laporan QA sebuah project, terbaru-diubah dulu, dengan nomor tampil (`QA-001`), status (`draft|submitted|closed`), keputusan (`go|no-go|conditional`), dan statistik (pass-rate dari yang dieksekusi, temuan per severity, jumlah open). Nomor tampil dihitung dari urutan pembuatan \u2014 pakai `id`, bukan nomor, saat memanggil tool lain.",
|
|
12217
|
+
inputSchema: obj({ properties: { project: PROJECT3 }, required: ["project"] }),
|
|
12218
|
+
mode: "read",
|
|
12219
|
+
capability: "qa:read",
|
|
12220
|
+
samplePath: "/projects/p1/qa/reports",
|
|
12221
|
+
sampleMethod: "GET",
|
|
12222
|
+
build: (a) => ({ method: "GET", path: base(a) }),
|
|
12223
|
+
shape: (raw) => raw
|
|
12224
|
+
},
|
|
12225
|
+
{
|
|
12226
|
+
name: "hanoman_qa_report_get",
|
|
12227
|
+
title: "Detail laporan QA",
|
|
12228
|
+
description: "Satu laporan QA lengkap: header, test case (`TC-01\u2026`), temuan (`F-01\u2026`, dengan repro bernomor, severity, prioritas, `caseCode`), dan metadata lampiran (nama, tipe, ukuran \u2014 bukan byte).",
|
|
12229
|
+
inputSchema: obj({ properties: { project: PROJECT3, report: REPORT }, required: ["project", "report"] }),
|
|
12230
|
+
mode: "read",
|
|
12231
|
+
capability: "qa:read",
|
|
12232
|
+
samplePath: "/projects/p1/qa/reports/r1",
|
|
12233
|
+
sampleMethod: "GET",
|
|
12234
|
+
build: (a) => ({ method: "GET", path: one(a) }),
|
|
12235
|
+
shape: (raw) => raw
|
|
12236
|
+
},
|
|
12237
|
+
{
|
|
12238
|
+
name: "hanoman_qa_report_create",
|
|
12239
|
+
title: "Buat laporan QA",
|
|
12240
|
+
description: "Membuat laporan QA berstatus `draft`. Hanya `project` dan `title` yang wajib. Test case dan temuan ditambahkan lewat tool terpisah.",
|
|
12241
|
+
inputSchema: obj({ properties: { project: PROJECT3, ...REPORT_FIELDS }, required: ["project", "title"] }),
|
|
12242
|
+
mode: "write",
|
|
12243
|
+
capability: "qa:write",
|
|
12244
|
+
samplePath: "/projects/p1/qa/reports",
|
|
12245
|
+
sampleMethod: "POST",
|
|
12246
|
+
build: (a) => ({ method: "POST", path: base(a), body: reportBody(a) }),
|
|
12247
|
+
shape: (raw) => raw
|
|
12248
|
+
},
|
|
12249
|
+
{
|
|
12250
|
+
name: "hanoman_qa_report_update",
|
|
12251
|
+
title: "Ubah laporan QA",
|
|
12252
|
+
description: "Mengubah header laporan; hanya field yang disebut yang ditulis. `status`: `draft \u2192 submitted \u2192 closed`; `submitted`/`closed` DITOLAK 400 bila `verdict` belum terisi. Laporan `closed` read-only (409) untuk semua tool QA \u2014 satu-satunya ubahan yang lolos adalah `status: draft` untuk membukanya kembali.",
|
|
12253
|
+
inputSchema: obj({
|
|
12254
|
+
properties: { project: PROJECT3, report: REPORT, ...REPORT_FIELDS, status: enumStr(QA_REPORT_STATUSES, "Status laporan.") },
|
|
12255
|
+
required: ["project", "report"]
|
|
12256
|
+
}),
|
|
12257
|
+
mode: "write",
|
|
12258
|
+
capability: "qa:write",
|
|
12259
|
+
samplePath: "/projects/p1/qa/reports/r1",
|
|
12260
|
+
sampleMethod: "PATCH",
|
|
12261
|
+
build: (a) => ({ method: "PATCH", path: one(a), body: reportBody(a) }),
|
|
12262
|
+
shape: (raw) => raw
|
|
12263
|
+
},
|
|
12264
|
+
{
|
|
12265
|
+
name: "hanoman_qa_case_create",
|
|
12266
|
+
title: "Tambah test case",
|
|
12267
|
+
description: "Menambah test case ke akhir daftar laporan. Hanya `title` yang wajib; status awal `todo`. Jawabannya adalah laporan terbaru (test case baru = entri terakhir `cases`).",
|
|
12268
|
+
inputSchema: obj({ properties: { project: PROJECT3, report: REPORT, ...CASE_FIELDS }, required: ["project", "report", "title"] }),
|
|
12269
|
+
mode: "write",
|
|
12270
|
+
capability: "qa:write",
|
|
12271
|
+
samplePath: "/projects/p1/qa/reports/r1/cases",
|
|
12272
|
+
sampleMethod: "POST",
|
|
12273
|
+
build: (a) => ({ method: "POST", path: `${one(a)}/cases`, body: caseBody(a) }),
|
|
12274
|
+
shape: (raw) => raw
|
|
12275
|
+
},
|
|
12276
|
+
{
|
|
12277
|
+
name: "hanoman_qa_case_update",
|
|
12278
|
+
title: "Ubah test case",
|
|
12279
|
+
description: "Mengubah test case; hanya field yang disebut yang ditulis. Biasa dipakai untuk mencatat hasil: `status` (`pass|fail|blocked|skipped`) dan `actual`.",
|
|
12280
|
+
inputSchema: obj({ properties: { project: PROJECT3, report: REPORT, case: CASE, ...CASE_FIELDS }, required: ["project", "report", "case"] }),
|
|
12281
|
+
mode: "write",
|
|
12282
|
+
capability: "qa:write",
|
|
12283
|
+
samplePath: "/projects/p1/qa/reports/r1/cases/c1",
|
|
12284
|
+
sampleMethod: "PATCH",
|
|
12285
|
+
build: (a) => ({ method: "PATCH", path: `${one(a)}/cases/${enc(String(a.case))}`, body: caseBody(a) }),
|
|
12286
|
+
shape: (raw) => raw
|
|
12287
|
+
},
|
|
12288
|
+
{
|
|
12289
|
+
name: "hanoman_qa_finding_create",
|
|
12290
|
+
title: "Catat temuan QA",
|
|
12291
|
+
description: "Mencatat satu temuan (bug) di laporan. Isi `severity` (dampak teknis) DAN `priority` (urutan perbaikan) secara terpisah \u2014 keduanya tak selalu sama. Tulis `steps` sebagai langkah reproduksi yang bisa diulang orang lain, satu per baris. Temuan baru berstatus `open`; jawabannya laporan terbaru (temuan baru = entri terakhir `findings`).",
|
|
12292
|
+
inputSchema: obj({ properties: { project: PROJECT3, report: REPORT, ...FINDING_FIELDS }, required: ["project", "report", "title"] }),
|
|
12293
|
+
mode: "write",
|
|
12294
|
+
capability: "qa:write",
|
|
12295
|
+
samplePath: "/projects/p1/qa/reports/r1/findings",
|
|
12296
|
+
sampleMethod: "POST",
|
|
12297
|
+
build: (a) => ({ method: "POST", path: `${one(a)}/findings`, body: findingBody(a) }),
|
|
12298
|
+
shape: (raw) => raw
|
|
12299
|
+
},
|
|
12300
|
+
{
|
|
12301
|
+
name: "hanoman_qa_finding_update",
|
|
12302
|
+
title: "Ubah temuan QA",
|
|
12303
|
+
description: "Mengubah temuan; hanya field yang disebut yang ditulis. `status`: `open` atau `wontfix`.",
|
|
12304
|
+
inputSchema: obj({ properties: { project: PROJECT3, report: REPORT, finding: FINDING, ...FINDING_FIELDS }, required: ["project", "report", "finding"] }),
|
|
12305
|
+
mode: "write",
|
|
12306
|
+
capability: "qa:write",
|
|
12307
|
+
samplePath: "/projects/p1/qa/reports/r1/findings/f1",
|
|
12308
|
+
sampleMethod: "PATCH",
|
|
12309
|
+
build: (a) => ({ method: "PATCH", path: `${one(a)}/findings/${enc(String(a.finding))}`, body: findingBody(a) }),
|
|
12310
|
+
shape: (raw) => raw
|
|
12311
|
+
},
|
|
12312
|
+
{
|
|
12313
|
+
name: "hanoman_qa_finding_to_backlog",
|
|
12314
|
+
title: "Kirim temuan QA ke backlog",
|
|
12315
|
+
description: "Membuat backlog item (`source: qa`) dari satu temuan: judul, repro, expected/actual, lingkungan, dan lampiran temuan ikut sebagai konteks agen. Temuan jadi `sent` dan menyimpan `backlogId`; hasil `findings[].spec` menampilkan stage backlog-nya. IDEMPOTEN: temuan yang sudah dikirim menjawab 200 `created: false` dengan spec yang sama. Pemetaan LOSSY: severity blocker/critical\u2192critical, minor/trivial\u2192minor; P0/P1\u2192tinggi, P2\u2192sedang, P3\u2192rendah (severity & prioritas QA asli tetap tertulis di teks backlog). Ini TIDAK membuka sesi agen; Spec-nya lahir tanpa persetujuan peluncuran bila token tak memegang `sessions:write`. Laporan `closed` tetap boleh (hanya tautan yang berubah).",
|
|
12316
|
+
inputSchema: obj({
|
|
12317
|
+
properties: { project: PROJECT3, report: REPORT, finding: FINDING, priority: enumStr(["tinggi", "sedang", "rendah"], "Timpa prioritas backlog. Tanpa ini: diturunkan dari prioritas P0\u2013P3 temuan.") },
|
|
12318
|
+
required: ["project", "report", "finding"]
|
|
12319
|
+
}),
|
|
12320
|
+
mode: "write",
|
|
12321
|
+
capability: "qa:write",
|
|
12322
|
+
samplePath: "/projects/p1/qa/reports/r1/findings/f1/backlog",
|
|
12323
|
+
sampleMethod: "POST",
|
|
12324
|
+
build: (a) => ({ method: "POST", path: `${one(a)}/findings/${enc(String(a.finding))}/backlog`, body: s(a.priority) ? { priority: s(a.priority) } : {} }),
|
|
12325
|
+
shape: (raw) => raw
|
|
12326
|
+
},
|
|
12327
|
+
{
|
|
12328
|
+
name: "hanoman_qa_report_to_backlog",
|
|
12329
|
+
title: "Kirim semua temuan open ke backlog",
|
|
12330
|
+
description: "Mengirim SEMUA temuan berstatus `open` sebuah laporan ke backlog sekaligus (`wontfix` dan yang sudah `sent` dilewati). Jawabannya `results` per temuan (satu yang gagal tak menggagalkan yang lain) dan `sent` = jumlah backlog baru. Semantik per temuan sama dengan hanoman_qa_finding_to_backlog.",
|
|
12331
|
+
inputSchema: obj({
|
|
12332
|
+
properties: { project: PROJECT3, report: REPORT, priority: enumStr(["tinggi", "sedang", "rendah"], "Timpa prioritas SEMUA backlog yang dibuat. Tanpa ini: diturunkan per temuan.") },
|
|
12333
|
+
required: ["project", "report"]
|
|
12334
|
+
}),
|
|
12335
|
+
mode: "write",
|
|
12336
|
+
capability: "qa:write",
|
|
12337
|
+
samplePath: "/projects/p1/qa/reports/r1/backlog",
|
|
12338
|
+
sampleMethod: "POST",
|
|
12339
|
+
build: (a) => ({ method: "POST", path: `${one(a)}/backlog`, body: s(a.priority) ? { priority: s(a.priority) } : {} }),
|
|
12340
|
+
shape: (raw) => raw
|
|
12341
|
+
}
|
|
12342
|
+
];
|
|
12343
|
+
}
|
|
12344
|
+
});
|
|
12345
|
+
|
|
12057
12346
|
// ../shared/src/mcp-catalog/system.ts
|
|
12058
12347
|
var SYSTEM_TOOLS;
|
|
12059
12348
|
var init_system = __esm({
|
|
@@ -12143,6 +12432,7 @@ var init_mcp_catalog = __esm({
|
|
|
12143
12432
|
init_support();
|
|
12144
12433
|
init_lead2();
|
|
12145
12434
|
init_team2();
|
|
12435
|
+
init_qa2();
|
|
12146
12436
|
init_system();
|
|
12147
12437
|
MCP_TOOLS = [
|
|
12148
12438
|
...ABOUT_TOOLS,
|
|
@@ -12163,6 +12453,8 @@ var init_mcp_catalog = __esm({
|
|
|
12163
12453
|
// sengaja: urutan di sini = urutan yang dibaca model, dan pekerjaan sehari-hari agen tetap
|
|
12164
12454
|
// backlog/sesi/IDE — bukan kartu orang lain.
|
|
12165
12455
|
...TEAM_TOOLS,
|
|
12456
|
+
...QA_TOOLS,
|
|
12457
|
+
// Workspace QA · laporan, test case, temuan
|
|
12166
12458
|
...SYSTEM_TOOLS
|
|
12167
12459
|
];
|
|
12168
12460
|
}
|
|
@@ -12488,6 +12780,9 @@ var init_src = __esm({
|
|
|
12488
12780
|
init_config_registry();
|
|
12489
12781
|
init_semver();
|
|
12490
12782
|
init_telegram();
|
|
12783
|
+
init_qa();
|
|
12784
|
+
init_qa_markdown();
|
|
12785
|
+
init_qa_tabular();
|
|
12491
12786
|
init_webhook();
|
|
12492
12787
|
init_mcp();
|
|
12493
12788
|
init_auto_merge();
|
|
@@ -18956,7 +19251,7 @@ function exactOptional(innerType) {
|
|
|
18956
19251
|
innerType
|
|
18957
19252
|
});
|
|
18958
19253
|
}
|
|
18959
|
-
function
|
|
19254
|
+
function nullable3(innerType) {
|
|
18960
19255
|
return new ZodNullable2({
|
|
18961
19256
|
type: "nullable",
|
|
18962
19257
|
innerType
|
|
@@ -19102,10 +19397,10 @@ var init_schemas2 = __esm({
|
|
|
19102
19397
|
return exactOptional(this);
|
|
19103
19398
|
},
|
|
19104
19399
|
nullable() {
|
|
19105
|
-
return
|
|
19400
|
+
return nullable3(this);
|
|
19106
19401
|
},
|
|
19107
19402
|
nullish() {
|
|
19108
|
-
return optional(
|
|
19403
|
+
return optional(nullable3(this));
|
|
19109
19404
|
},
|
|
19110
19405
|
nonoptional(params) {
|
|
19111
19406
|
return nonoptional(this, params);
|
|
@@ -23376,8 +23671,8 @@ function setNegotiatedProtocolVersion(instance, version3) {
|
|
|
23376
23671
|
function isPlainObject$1(value) {
|
|
23377
23672
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
23378
23673
|
}
|
|
23379
|
-
function mergeCapabilities(
|
|
23380
|
-
const result = { ...
|
|
23674
|
+
function mergeCapabilities(base2, additional) {
|
|
23675
|
+
const result = { ...base2 };
|
|
23381
23676
|
for (const key in additional) {
|
|
23382
23677
|
const k = key;
|
|
23383
23678
|
const addValue = additional[k];
|
|
@@ -28463,10 +28758,10 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
28463
28758
|
schemelessOptions.skipEscape = true;
|
|
28464
28759
|
return serialize(resolved, schemelessOptions);
|
|
28465
28760
|
}
|
|
28466
|
-
function resolveComponent(
|
|
28761
|
+
function resolveComponent(base2, relative2, options, skipNormalization) {
|
|
28467
28762
|
const target = {};
|
|
28468
28763
|
if (!skipNormalization) {
|
|
28469
|
-
|
|
28764
|
+
base2 = parse4(serialize(base2, options), options);
|
|
28470
28765
|
relative2 = parse4(serialize(relative2, options), options);
|
|
28471
28766
|
}
|
|
28472
28767
|
options = options || {};
|
|
@@ -28486,24 +28781,24 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
28486
28781
|
target.query = relative2.query;
|
|
28487
28782
|
} else {
|
|
28488
28783
|
if (!relative2.path) {
|
|
28489
|
-
target.path =
|
|
28784
|
+
target.path = base2.path;
|
|
28490
28785
|
if (relative2.query !== void 0) target.query = relative2.query;
|
|
28491
|
-
else target.query =
|
|
28786
|
+
else target.query = base2.query;
|
|
28492
28787
|
} else {
|
|
28493
28788
|
if (relative2.path[0] === "/") target.path = removeDotSegments(relative2.path);
|
|
28494
28789
|
else {
|
|
28495
|
-
if ((
|
|
28496
|
-
else if (!
|
|
28497
|
-
else target.path =
|
|
28790
|
+
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative2.path;
|
|
28791
|
+
else if (!base2.path) target.path = relative2.path;
|
|
28792
|
+
else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative2.path;
|
|
28498
28793
|
target.path = removeDotSegments(target.path);
|
|
28499
28794
|
}
|
|
28500
28795
|
target.query = relative2.query;
|
|
28501
28796
|
}
|
|
28502
|
-
target.userinfo =
|
|
28503
|
-
target.host =
|
|
28504
|
-
target.port =
|
|
28797
|
+
target.userinfo = base2.userinfo;
|
|
28798
|
+
target.host = base2.host;
|
|
28799
|
+
target.port = base2.port;
|
|
28505
28800
|
}
|
|
28506
|
-
target.scheme =
|
|
28801
|
+
target.scheme = base2.scheme;
|
|
28507
28802
|
}
|
|
28508
28803
|
target.fragment = relative2.fragment;
|
|
28509
28804
|
return target;
|
|
@@ -35057,7 +35352,14 @@ var init_migrate_pg = __esm({
|
|
|
35057
35352
|
"Task",
|
|
35058
35353
|
// SPEC-481 · ADR-0100 · WebhookDelivery WAJIB sesudah WebhookEndpoint (FK endpointId).
|
|
35059
35354
|
"WebhookEndpoint",
|
|
35060
|
-
"WebhookDelivery"
|
|
35355
|
+
"WebhookDelivery",
|
|
35356
|
+
// Workspace QA · ADR-0174 · QaReport sesudah Project (FK projectId, cascade); QaCase/QaFinding/
|
|
35357
|
+
// QaAttachment sesudah QaReport (FK reportId, cascade). Keempatnya LOCAL-only dan TIDAK ada di
|
|
35358
|
+
// sumber Postgres lama — jalur 42P01 memperlakukannya sebagai nol baris (cermin Changelog).
|
|
35359
|
+
"QaReport",
|
|
35360
|
+
"QaCase",
|
|
35361
|
+
"QaFinding",
|
|
35362
|
+
"QaAttachment"
|
|
35061
35363
|
];
|
|
35062
35364
|
CHUNK = 200;
|
|
35063
35365
|
INT8_OID = 20;
|
|
@@ -35204,8 +35506,8 @@ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "
|
|
|
35204
35506
|
function addLink(indexPath, relPath, category) {
|
|
35205
35507
|
let md = readFileSync7(indexPath, "utf8");
|
|
35206
35508
|
if (md.includes(`(${relPath})`)) return;
|
|
35207
|
-
const
|
|
35208
|
-
const line = `- [${
|
|
35509
|
+
const base2 = relPath.split("/").pop().replace(/\.md$/, "");
|
|
35510
|
+
const line = `- [${base2}](${relPath})`;
|
|
35209
35511
|
const head = `## ${category}`;
|
|
35210
35512
|
if (md.includes(head)) {
|
|
35211
35513
|
const i = md.indexOf(head) + head.length;
|