@mytegroupinc/myte-core 0.0.43 → 0.0.45
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/README.md +48 -43
- package/THIRD_PARTY_NOTICES.md +21 -21
- package/TRADEMARKS.md +10 -10
- package/cli.js +556 -199
- package/lib/mytecody-release-public-key.pem +11 -11
- package/lib/mytecody-splash.js +251 -251
- package/mytecody-cli.js +2025 -2025
- package/package.json +14 -14
package/cli.js
CHANGED
|
@@ -208,10 +208,10 @@ function printHelp() {
|
|
|
208
208
|
" myte feedback reviews [--status open|terminal|all] [--request-id <id>] [--json]",
|
|
209
209
|
" myte feedback review --request-id <id> --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
|
|
210
210
|
" myte feedback review --request-ids \"<id1,id2>\" --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
|
|
211
|
-
" myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
|
|
212
|
-
" myte feedback move --feedback-ids \"<id1,id2>\" --to-state in_progress --reason \"...\" [--json]",
|
|
213
|
-
" myte feedback comment --feedback-id <id> --body \"...\" [--body-file ./comment.md] [--json]",
|
|
214
|
-
" myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
|
|
211
|
+
" myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
|
|
212
|
+
" myte feedback move --feedback-ids \"<id1,id2>\" --to-state in_progress --reason \"...\" [--json]",
|
|
213
|
+
" myte feedback comment --feedback-id <id> --body \"...\" [--body-file ./comment.md] [--json]",
|
|
214
|
+
" myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
|
|
215
215
|
" myte feedback prd-versions --feedback-id <id> [--json]",
|
|
216
216
|
" myte feedback prd-diff --feedback-id <id> --version-id <id> [--compare-to <id>] [--json]",
|
|
217
217
|
" myte feedback validate --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
|
|
@@ -235,6 +235,12 @@ function printHelp() {
|
|
|
235
235
|
" - Set MYTE_API_KEY in a workspace .env (or env var)",
|
|
236
236
|
" - Set MYTEAI_API_KEY in a workspace .env (or env var) for `myte ai`",
|
|
237
237
|
"",
|
|
238
|
+
"Live write approval:",
|
|
239
|
+
" - Live project-key mutations require --confirm-write --approval-artifact <path>",
|
|
240
|
+
" - The artifact must be a local .md, .yml/.yaml, or .json file shown to and approved by the user before the command is rerun",
|
|
241
|
+
" - Batch artifacts must enumerate every feedback id, mission id, suggestion id, request id, or PRD item",
|
|
242
|
+
" - Read/sync/query/validate, --print-context, and --dry-run commands do not require write approval",
|
|
243
|
+
"",
|
|
238
244
|
"bootstrap contract:",
|
|
239
245
|
" - Run from any workspace where you want local MyteCommandCenter data written",
|
|
240
246
|
" - Writes MyteCommandCenter/data/project.yml plus phases, epics, stories, and missions locally",
|
|
@@ -313,7 +319,7 @@ function printHelp() {
|
|
|
313
319
|
" - Archived feedback is intentionally excluded from normal sync; returning it to active work is web UI only",
|
|
314
320
|
" - Writes project feedback metadata and conversation turns into MyteCommandCenter/data/feedback.yml",
|
|
315
321
|
" - Stores full PRD context in MyteCommandCenter/PRD/feedback-sync/*.md and points to those files from feedback.yml",
|
|
316
|
-
" - Reads existing feedback comments and can create text-only feedback-specific comments through the project-key API",
|
|
322
|
+
" - Reads existing feedback comments and can create text-only feedback-specific comments through the project-key API",
|
|
317
323
|
"",
|
|
318
324
|
"feedback review contract:",
|
|
319
325
|
" - Draft commands write review artifacts under MyteCommandCenter/reviews/feedback/*.yml for local IDE diff review",
|
|
@@ -322,9 +328,9 @@ function printHelp() {
|
|
|
322
328
|
" - reviews/review expose owner/delegate approval, rejection, request-changes, and cancel decisions",
|
|
323
329
|
" - review with --request-ids sends one batch review request; it does not loop single-review calls",
|
|
324
330
|
" - live lifecycle moves return pending_feedback_review while an active linked review request exists",
|
|
325
|
-
" - move/undo operate safe board states directly; governed states remain backend-authorized",
|
|
326
|
-
" - move with --feedback-ids sends one batch board move request; it supports active-state moves and authorized archive",
|
|
327
|
-
" - comment creates text-only feedback-specific comments; attachments remain web UI only for this endpoint",
|
|
331
|
+
" - move/undo operate safe board states directly; governed states remain backend-authorized",
|
|
332
|
+
" - move with --feedback-ids sends one batch board move request; it supports active-state moves and authorized archive",
|
|
333
|
+
" - comment creates text-only feedback-specific comments; attachments remain web UI only for this endpoint",
|
|
328
334
|
" - project-key API/CLI cannot unarchive archived Feedback; use the web archived Feedback view",
|
|
329
335
|
" - get/history read current feedback snapshots and audited event history through the project-key API",
|
|
330
336
|
" - prd-versions/prd-diff expose retained PRD versions and backend-generated text diffs",
|
|
@@ -340,9 +346,9 @@ function printHelp() {
|
|
|
340
346
|
" - revise: resubmit the original submitter's request after request_changes",
|
|
341
347
|
" - reviews: list open/terminal/all requests or inspect one request",
|
|
342
348
|
" - review: approve, reject, request_changes, or cancel; backend restricts this to owner/delegate capability",
|
|
343
|
-
" - move: direct audited board movement for allowed states; governed transitions remain backend-authorized",
|
|
344
|
-
" - comment: create a text-only comment on one Feedback item",
|
|
345
|
-
" - undo: reverse an audited board event when conflict checks allow it",
|
|
349
|
+
" - move: direct audited board movement for allowed states; governed transitions remain backend-authorized",
|
|
350
|
+
" - comment: create a text-only comment on one Feedback item",
|
|
351
|
+
" - undo: reverse an audited board event when conflict checks allow it",
|
|
346
352
|
" - prd-versions: list retained PRD baselines/revisions for a feedback item",
|
|
347
353
|
" - prd-diff: fetch backend-generated text diff between PRD versions",
|
|
348
354
|
" - history: list board/refinement events for audit review",
|
|
@@ -363,11 +369,11 @@ function printHelp() {
|
|
|
363
369
|
" --stdin Read supported command content from stdin instead of inline text or a file path",
|
|
364
370
|
" --title <text> Override PRD title for raw markdown uploads",
|
|
365
371
|
" --description <text> Set feedback description/card summary for raw markdown uploads",
|
|
366
|
-
" --content <text> Team update content for update-team",
|
|
367
|
-
" --body <text> Feedback comment body for feedback comment",
|
|
368
|
-
" --subject <text> Subject for update-owner or update-client",
|
|
372
|
+
" --content <text> Team update content for update-team",
|
|
373
|
+
" --body <text> Feedback comment body for feedback comment",
|
|
374
|
+
" --subject <text> Subject for update-owner or update-client",
|
|
369
375
|
" --body-markdown <md> Markdown body for update-owner or update-client",
|
|
370
|
-
" --body-file <path> Read feedback comment, update-owner, or update-client markdown body from a file",
|
|
376
|
+
" --body-file <path> Read feedback comment, update-owner, or update-client markdown body from a file",
|
|
371
377
|
" --target-contact-id Add one client contact ObjectId (repeatable)",
|
|
372
378
|
" --target-contact-ids Comma-separated client contact ObjectIds",
|
|
373
379
|
" --feedback-id <id> Feedback ObjectId for feedback review commands",
|
|
@@ -396,6 +402,8 @@ function printHelp() {
|
|
|
396
402
|
" --wait Poll batch status until terminal completion for run-qaqc",
|
|
397
403
|
" --sync After run-qaqc completes, refresh local QAQC file",
|
|
398
404
|
" --force Allow run-qaqc to bypass stale-state protection when supported",
|
|
405
|
+
" --confirm-write Confirm the user explicitly approved a live Myte project-key mutation",
|
|
406
|
+
" --approval-artifact Local .md/.yml/.json approval artifact shown to and approved by the user",
|
|
399
407
|
" --no-sync Skip automatic post-mutation sync for suggestions, missions, and feedback mutations",
|
|
400
408
|
" --print-context Print JSON payload and exit (no query call)",
|
|
401
409
|
" --no-fetch Don't git fetch origin main/master before diff",
|
|
@@ -421,10 +429,10 @@ function printHelp() {
|
|
|
421
429
|
" myte feedback status --feedback-id 507f1f77bcf86cd799439011 --status in_review --reason \"Ready for owner review\"",
|
|
422
430
|
" myte feedback submit --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-edit.yml --json",
|
|
423
431
|
" myte feedback reviews --status open --json",
|
|
424
|
-
" myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
|
|
425
|
-
" myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
|
|
426
|
-
" myte feedback comment --feedback-id 507f1f77bcf86cd799439011 --body \"Implementation notes are ready for review.\" --json",
|
|
427
|
-
" myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
|
|
432
|
+
" myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
|
|
433
|
+
" myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
|
|
434
|
+
" myte feedback comment --feedback-id 507f1f77bcf86cd799439011 --body \"Implementation notes are ready for review.\" --json",
|
|
435
|
+
" myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
|
|
428
436
|
" myte feedback apply --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
|
|
429
437
|
" myte suggestions create --file ./suggestions/create.yml",
|
|
430
438
|
" myte suggestions revise",
|
|
@@ -693,6 +701,164 @@ async function fetchJsonWithTimeout(fetchFn, url, options, timeoutMs) {
|
|
|
693
701
|
}
|
|
694
702
|
}
|
|
695
703
|
|
|
704
|
+
const WRITE_APPROVAL_TRANSPORT_KEYS = new Set(["write_approval", "client_session_id"]);
|
|
705
|
+
|
|
706
|
+
function canonicalizeWritePayload(value) {
|
|
707
|
+
if (Array.isArray(value)) {
|
|
708
|
+
return value.map((item) => canonicalizeWritePayload(item));
|
|
709
|
+
}
|
|
710
|
+
if (value && typeof value === "object") {
|
|
711
|
+
return Object.fromEntries(
|
|
712
|
+
Object.keys(value)
|
|
713
|
+
.filter((key) => !WRITE_APPROVAL_TRANSPORT_KEYS.has(String(key)))
|
|
714
|
+
.sort()
|
|
715
|
+
.map((key) => [key, canonicalizeWritePayload(value[key])])
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
return value;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function projectApiWritePayloadHash(payload) {
|
|
722
|
+
return createHash("sha256")
|
|
723
|
+
.update(pythonJsonDumpsCompatible(canonicalizeWritePayload(payload || {})))
|
|
724
|
+
.digest("hex");
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function isConfirmWrite(args) {
|
|
728
|
+
return Boolean(args["confirm-write"] || args.confirmWrite || args.confirm_write);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function resolveWriteApprovalArtifactPath(args) {
|
|
732
|
+
const explicit = firstNonEmptyString(args["approval-artifact"], args.approvalArtifact, args.approval_artifact);
|
|
733
|
+
if (!explicit) return "";
|
|
734
|
+
const resolved = path.resolve(process.cwd(), explicit);
|
|
735
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
|
|
736
|
+
console.error(`Approval artifact not found: ${resolved}`);
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
return resolved;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function inferApprovalArtifactKind(filePath, fallbackKind) {
|
|
743
|
+
const ext = path.extname(String(filePath || "")).toLowerCase();
|
|
744
|
+
if (ext === ".md" || ext === ".markdown") return "markdown";
|
|
745
|
+
if (ext === ".yml" || ext === ".yaml") return "yaml";
|
|
746
|
+
if (ext === ".json") return "json";
|
|
747
|
+
return fallbackKind || "artifact";
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function normalizeApprovalTargets(targets) {
|
|
751
|
+
return toStringArray(targets).map((item) => item.trim()).filter(Boolean);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function writePayloadItems(payload) {
|
|
755
|
+
if (Array.isArray(payload?.items)) return payload.items;
|
|
756
|
+
if (Array.isArray(payload)) return payload;
|
|
757
|
+
return payload && typeof payload === "object" ? [payload] : [];
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function suggestionApprovalTargets(payload) {
|
|
761
|
+
return writePayloadItems(payload).map((item, index) => {
|
|
762
|
+
if (!item || typeof item !== "object") return `index:${index}`;
|
|
763
|
+
return firstNonEmptyString(
|
|
764
|
+
item.suggestion_id,
|
|
765
|
+
item.id,
|
|
766
|
+
item._id,
|
|
767
|
+
item.mission_id,
|
|
768
|
+
item.target_mission_id,
|
|
769
|
+
item.title ? `${item.change_type || "suggestion"}:${item.title}` : ""
|
|
770
|
+
) || `index:${index}`;
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function prdApprovalTargets(payload) {
|
|
775
|
+
return writePayloadItems(payload).map((item, index) => {
|
|
776
|
+
if (!item || typeof item !== "object") return `index:${index}`;
|
|
777
|
+
return firstNonEmptyString(item.client_ref, item.title) || `index:${index}`;
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function requireWriteApproval(args, payload, { operation, artifactKind, targets = [], batchCount = null } = {}) {
|
|
782
|
+
const payloadSha256 = projectApiWritePayloadHash(payload || {});
|
|
783
|
+
const targetValues = normalizeApprovalTargets(targets);
|
|
784
|
+
const expectedBatchCount = batchCount !== null && batchCount !== undefined
|
|
785
|
+
? Number(batchCount)
|
|
786
|
+
: targetValues.length > 1
|
|
787
|
+
? targetValues.length
|
|
788
|
+
: null;
|
|
789
|
+
|
|
790
|
+
if (!isConfirmWrite(args)) {
|
|
791
|
+
console.error("Refusing live Myte write without explicit human approval.");
|
|
792
|
+
console.error(`Operation: ${operation || "unknown"}`);
|
|
793
|
+
console.error(`Payload SHA256: ${payloadSha256}`);
|
|
794
|
+
if (targetValues.length) console.error(`Targets: ${targetValues.join(", ")}`);
|
|
795
|
+
if (expectedBatchCount !== null) console.error(`Batch count: ${expectedBatchCount}`);
|
|
796
|
+
console.error("Create a local .md/.yml/.json approval artifact that lists the exact operation, payload summary, and every target.");
|
|
797
|
+
console.error("Show that artifact to the user and ask for explicit approval.");
|
|
798
|
+
console.error("Then rerun with --confirm-write --approval-artifact <path>.");
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const artifactPath = resolveWriteApprovalArtifactPath(args);
|
|
803
|
+
if (!artifactPath) {
|
|
804
|
+
console.error("Missing --approval-artifact <path> for approved Myte write.");
|
|
805
|
+
console.error(`Payload SHA256: ${payloadSha256}`);
|
|
806
|
+
console.error("The artifact must be a local .md/.yml/.json file shown to and explicitly approved by the user.");
|
|
807
|
+
process.exit(1);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return {
|
|
811
|
+
approved: true,
|
|
812
|
+
source: "explicit_user_approval",
|
|
813
|
+
artifact_path: artifactPath,
|
|
814
|
+
artifact_kind: inferApprovalArtifactKind(artifactPath, artifactKind),
|
|
815
|
+
payload_sha256: payloadSha256,
|
|
816
|
+
targets: targetValues,
|
|
817
|
+
batch_count: expectedBatchCount,
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function writeApprovalHeaders(writeApproval) {
|
|
822
|
+
if (!writeApproval) return {};
|
|
823
|
+
const headers = {
|
|
824
|
+
"X-Myte-Write-Approval": "explicit_user_approval",
|
|
825
|
+
"X-Myte-Approval-Artifact": String(writeApproval.artifact_path || ""),
|
|
826
|
+
"X-Myte-Approval-Artifact-Kind": String(writeApproval.artifact_kind || ""),
|
|
827
|
+
"X-Myte-Approval-Payload-SHA256": String(writeApproval.payload_sha256 || ""),
|
|
828
|
+
};
|
|
829
|
+
if (writeApproval.batch_count !== null && writeApproval.batch_count !== undefined) {
|
|
830
|
+
headers["X-Myte-Approval-Batch-Count"] = String(writeApproval.batch_count);
|
|
831
|
+
}
|
|
832
|
+
if (Array.isArray(writeApproval.targets) && writeApproval.targets.length) {
|
|
833
|
+
headers["X-Myte-Approval-Targets"] = writeApproval.targets.join(",");
|
|
834
|
+
}
|
|
835
|
+
return headers;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function formatProjectApiErrorMessage(body, fallback) {
|
|
839
|
+
const message = body?.message || fallback;
|
|
840
|
+
const data = body?.data || {};
|
|
841
|
+
const code = String(data?.code || "");
|
|
842
|
+
if (!code.startsWith("myte_write_approval")) {
|
|
843
|
+
return message;
|
|
844
|
+
}
|
|
845
|
+
const lines = [
|
|
846
|
+
message,
|
|
847
|
+
`Approval error: ${code}`,
|
|
848
|
+
];
|
|
849
|
+
if (data.operation) lines.push(`Operation: ${data.operation}`);
|
|
850
|
+
if (data.payload_sha256) lines.push(`Payload SHA256: ${data.payload_sha256}`);
|
|
851
|
+
if (Array.isArray(data.targets) && data.targets.length) lines.push(`Targets: ${data.targets.join(", ")}`);
|
|
852
|
+
if (data.batch_count !== undefined && data.batch_count !== null) lines.push(`Batch count: ${data.batch_count}`);
|
|
853
|
+
if (Array.isArray(data.required_agent_steps) && data.required_agent_steps.length) {
|
|
854
|
+
lines.push("Required agent steps:");
|
|
855
|
+
for (const step of data.required_agent_steps) {
|
|
856
|
+
lines.push(`- ${step}`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return lines.join("\n");
|
|
860
|
+
}
|
|
861
|
+
|
|
696
862
|
function summarizeDiffDiagnosticsForContext(diagnostics) {
|
|
697
863
|
if (!diagnostics) return null;
|
|
698
864
|
const repos = Array.isArray(diagnostics.repo_summaries)
|
|
@@ -1756,7 +1922,7 @@ async function fetchFeedbackHistory({ apiBase, key, timeoutMs, feedbackId }) {
|
|
|
1756
1922
|
return body.data || {};
|
|
1757
1923
|
}
|
|
1758
1924
|
|
|
1759
|
-
async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mode, payload, idempotencyKey, clientSessionId }) {
|
|
1925
|
+
async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mode, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1760
1926
|
const fetchFn = await getFetch();
|
|
1761
1927
|
const action = String(mode || "").trim();
|
|
1762
1928
|
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/refinement/${action}`;
|
|
@@ -1764,6 +1930,7 @@ async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mod
|
|
|
1764
1930
|
"Content-Type": "application/json",
|
|
1765
1931
|
Authorization: `Bearer ${key}`,
|
|
1766
1932
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
1933
|
+
...writeApprovalHeaders(writeApproval),
|
|
1767
1934
|
};
|
|
1768
1935
|
if (idempotencyKey) {
|
|
1769
1936
|
headers["X-Idempotency-Key"] = String(idempotencyKey).trim();
|
|
@@ -1780,7 +1947,7 @@ async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mod
|
|
|
1780
1947
|
);
|
|
1781
1948
|
|
|
1782
1949
|
if (!resp.ok || body.status !== "success") {
|
|
1783
|
-
const msg = body
|
|
1950
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback refinement ${action} failed (${resp.status})`);
|
|
1784
1951
|
const err = new Error(msg);
|
|
1785
1952
|
err.status = resp.status;
|
|
1786
1953
|
err.data = body?.data;
|
|
@@ -1859,7 +2026,7 @@ async function fetchFeedbackEvents({ apiBase, key, timeoutMs, feedbackId, limit
|
|
|
1859
2026
|
return body.data || {};
|
|
1860
2027
|
}
|
|
1861
2028
|
|
|
1862
|
-
async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId }) {
|
|
2029
|
+
async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1863
2030
|
const fetchFn = await getFetch();
|
|
1864
2031
|
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/refinement/requests`;
|
|
1865
2032
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -1872,6 +2039,7 @@ async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackI
|
|
|
1872
2039
|
Authorization: `Bearer ${key}`,
|
|
1873
2040
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1874
2041
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2042
|
+
...writeApprovalHeaders(writeApproval),
|
|
1875
2043
|
},
|
|
1876
2044
|
body: JSON.stringify(payload || {}),
|
|
1877
2045
|
},
|
|
@@ -1879,7 +2047,7 @@ async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackI
|
|
|
1879
2047
|
);
|
|
1880
2048
|
|
|
1881
2049
|
if (!resp.ok || body.status !== "success") {
|
|
1882
|
-
const msg = body
|
|
2050
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback review submission failed (${resp.status})`);
|
|
1883
2051
|
const err = new Error(msg);
|
|
1884
2052
|
err.status = resp.status;
|
|
1885
2053
|
err.data = body?.data;
|
|
@@ -1888,7 +2056,7 @@ async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackI
|
|
|
1888
2056
|
return body.data || {};
|
|
1889
2057
|
}
|
|
1890
2058
|
|
|
1891
|
-
async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requestId, endpointAction, payload, idempotencyKey, clientSessionId }) {
|
|
2059
|
+
async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requestId, endpointAction, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1892
2060
|
const fetchFn = await getFetch();
|
|
1893
2061
|
const url = `${apiBase}/project-assistant/feedback-review-requests/${encodeURIComponent(String(requestId || ""))}/${endpointAction}`;
|
|
1894
2062
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -1901,6 +2069,7 @@ async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requ
|
|
|
1901
2069
|
Authorization: `Bearer ${key}`,
|
|
1902
2070
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1903
2071
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2072
|
+
...writeApprovalHeaders(writeApproval),
|
|
1904
2073
|
},
|
|
1905
2074
|
body: JSON.stringify(payload || {}),
|
|
1906
2075
|
},
|
|
@@ -1908,7 +2077,7 @@ async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requ
|
|
|
1908
2077
|
);
|
|
1909
2078
|
|
|
1910
2079
|
if (!resp.ok || body.status !== "success") {
|
|
1911
|
-
const msg = body
|
|
2080
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback review ${endpointAction} failed (${resp.status})`);
|
|
1912
2081
|
const err = new Error(msg);
|
|
1913
2082
|
err.status = resp.status;
|
|
1914
2083
|
err.data = body?.data;
|
|
@@ -1917,7 +2086,7 @@ async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requ
|
|
|
1917
2086
|
return body.data || {};
|
|
1918
2087
|
}
|
|
1919
2088
|
|
|
1920
|
-
async function postFeedbackBatchReviewMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
2089
|
+
async function postFeedbackBatchReviewMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1921
2090
|
const fetchFn = await getFetch();
|
|
1922
2091
|
const url = `${apiBase}/project-assistant/feedback-review-requests/batch-review`;
|
|
1923
2092
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -1930,6 +2099,7 @@ async function postFeedbackBatchReviewMutation({ apiBase, key, timeoutMs, payloa
|
|
|
1930
2099
|
Authorization: `Bearer ${key}`,
|
|
1931
2100
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1932
2101
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2102
|
+
...writeApprovalHeaders(writeApproval),
|
|
1933
2103
|
},
|
|
1934
2104
|
body: JSON.stringify(payload || {}),
|
|
1935
2105
|
},
|
|
@@ -1937,7 +2107,7 @@ async function postFeedbackBatchReviewMutation({ apiBase, key, timeoutMs, payloa
|
|
|
1937
2107
|
);
|
|
1938
2108
|
|
|
1939
2109
|
if (!resp.ok || body.status !== "success") {
|
|
1940
|
-
const msg = body
|
|
2110
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback batch review failed (${resp.status})`);
|
|
1941
2111
|
const err = new Error(msg);
|
|
1942
2112
|
err.status = resp.status;
|
|
1943
2113
|
err.data = body?.data;
|
|
@@ -1946,7 +2116,7 @@ async function postFeedbackBatchReviewMutation({ apiBase, key, timeoutMs, payloa
|
|
|
1946
2116
|
return body.data || {};
|
|
1947
2117
|
}
|
|
1948
2118
|
|
|
1949
|
-
async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId, endpoint, payload, idempotencyKey, clientSessionId }) {
|
|
2119
|
+
async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId, endpoint, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1950
2120
|
const fetchFn = await getFetch();
|
|
1951
2121
|
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/${endpoint}`;
|
|
1952
2122
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -1959,6 +2129,7 @@ async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId,
|
|
|
1959
2129
|
Authorization: `Bearer ${key}`,
|
|
1960
2130
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1961
2131
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2132
|
+
...writeApprovalHeaders(writeApproval),
|
|
1962
2133
|
},
|
|
1963
2134
|
body: JSON.stringify(payload || {}),
|
|
1964
2135
|
},
|
|
@@ -1966,7 +2137,7 @@ async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId,
|
|
|
1966
2137
|
);
|
|
1967
2138
|
|
|
1968
2139
|
if (!resp.ok || body.status !== "success") {
|
|
1969
|
-
const msg = body
|
|
2140
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback board mutation failed (${resp.status})`);
|
|
1970
2141
|
const err = new Error(msg);
|
|
1971
2142
|
err.status = resp.status;
|
|
1972
2143
|
err.data = body?.data;
|
|
@@ -1975,7 +2146,7 @@ async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId,
|
|
|
1975
2146
|
return body.data || {};
|
|
1976
2147
|
}
|
|
1977
2148
|
|
|
1978
|
-
async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
2149
|
+
async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
1979
2150
|
const fetchFn = await getFetch();
|
|
1980
2151
|
const url = `${apiBase}/project-assistant/feedback/batch-board-move`;
|
|
1981
2152
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -1988,6 +2159,7 @@ async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload
|
|
|
1988
2159
|
Authorization: `Bearer ${key}`,
|
|
1989
2160
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1990
2161
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2162
|
+
...writeApprovalHeaders(writeApproval),
|
|
1991
2163
|
},
|
|
1992
2164
|
body: JSON.stringify(payload || {}),
|
|
1993
2165
|
},
|
|
@@ -1995,45 +2167,46 @@ async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload
|
|
|
1995
2167
|
);
|
|
1996
2168
|
|
|
1997
2169
|
if (!resp.ok || body.status !== "success") {
|
|
1998
|
-
const msg = body
|
|
2170
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback batch board mutation failed (${resp.status})`);
|
|
1999
2171
|
const err = new Error(msg);
|
|
2000
2172
|
err.status = resp.status;
|
|
2001
2173
|
err.data = body?.data;
|
|
2002
2174
|
throw err;
|
|
2003
2175
|
}
|
|
2004
|
-
return body.data || {};
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
async function postFeedbackComment({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId }) {
|
|
2008
|
-
const fetchFn = await getFetch();
|
|
2009
|
-
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/comments`;
|
|
2010
|
-
const { resp, body } = await fetchJsonWithTimeout(
|
|
2011
|
-
fetchFn,
|
|
2012
|
-
url,
|
|
2013
|
-
{
|
|
2014
|
-
method: "POST",
|
|
2015
|
-
headers: {
|
|
2016
|
-
"Content-Type": "application/json",
|
|
2017
|
-
Authorization: `Bearer ${key}`,
|
|
2018
|
-
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2019
|
-
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
const
|
|
2029
|
-
err
|
|
2030
|
-
err.
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2176
|
+
return body.data || {};
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
async function postFeedbackComment({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
2180
|
+
const fetchFn = await getFetch();
|
|
2181
|
+
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/comments`;
|
|
2182
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
2183
|
+
fetchFn,
|
|
2184
|
+
url,
|
|
2185
|
+
{
|
|
2186
|
+
method: "POST",
|
|
2187
|
+
headers: {
|
|
2188
|
+
"Content-Type": "application/json",
|
|
2189
|
+
Authorization: `Bearer ${key}`,
|
|
2190
|
+
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2191
|
+
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2192
|
+
...writeApprovalHeaders(writeApproval),
|
|
2193
|
+
},
|
|
2194
|
+
body: JSON.stringify(payload || {}),
|
|
2195
|
+
},
|
|
2196
|
+
timeoutMs
|
|
2197
|
+
);
|
|
2198
|
+
|
|
2199
|
+
if (!resp.ok || body.status !== "success") {
|
|
2200
|
+
const msg = formatProjectApiErrorMessage(body, `Feedback comment failed (${resp.status})`);
|
|
2201
|
+
const err = new Error(msg);
|
|
2202
|
+
err.status = resp.status;
|
|
2203
|
+
err.data = body?.data;
|
|
2204
|
+
throw err;
|
|
2205
|
+
}
|
|
2206
|
+
return body.data || {};
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
async function fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId }) {
|
|
2037
2210
|
const fetchFn = await getFetch();
|
|
2038
2211
|
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions`;
|
|
2039
2212
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -2130,7 +2303,7 @@ function resolveProjectMutationIdempotencyKey({ args, operation, payload }) {
|
|
|
2130
2303
|
return createHash("sha256").update(fingerprint).digest("hex");
|
|
2131
2304
|
}
|
|
2132
2305
|
|
|
2133
|
-
async function postSuggestionsMutation({ apiBase, key, timeoutMs, endpoint, payload, idempotencyKey, clientSessionId }) {
|
|
2306
|
+
async function postSuggestionsMutation({ apiBase, key, timeoutMs, endpoint, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
2134
2307
|
const fetchFn = await getFetch();
|
|
2135
2308
|
const url = `${apiBase}${endpoint}`;
|
|
2136
2309
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -2143,6 +2316,7 @@ async function postSuggestionsMutation({ apiBase, key, timeoutMs, endpoint, payl
|
|
|
2143
2316
|
Authorization: `Bearer ${key}`,
|
|
2144
2317
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2145
2318
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2319
|
+
...writeApprovalHeaders(writeApproval),
|
|
2146
2320
|
},
|
|
2147
2321
|
body: JSON.stringify(payload),
|
|
2148
2322
|
},
|
|
@@ -2150,15 +2324,16 @@ async function postSuggestionsMutation({ apiBase, key, timeoutMs, endpoint, payl
|
|
|
2150
2324
|
);
|
|
2151
2325
|
|
|
2152
2326
|
if (!resp.ok || body.status !== "success") {
|
|
2153
|
-
const msg = body
|
|
2327
|
+
const msg = formatProjectApiErrorMessage(body, `Mission suggestions request failed (${resp.status})`);
|
|
2154
2328
|
const err = new Error(msg);
|
|
2155
2329
|
err.status = resp.status;
|
|
2330
|
+
err.data = body?.data;
|
|
2156
2331
|
throw err;
|
|
2157
2332
|
}
|
|
2158
2333
|
return body.data || {};
|
|
2159
2334
|
}
|
|
2160
2335
|
|
|
2161
|
-
async function createRunQaqcBatch({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
2336
|
+
async function createRunQaqcBatch({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
2162
2337
|
const fetchFn = await getFetch();
|
|
2163
2338
|
const url = `${apiBase}/project-assistant/run-qaqc`;
|
|
2164
2339
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -2171,6 +2346,7 @@ async function createRunQaqcBatch({ apiBase, key, timeoutMs, payload, idempotenc
|
|
|
2171
2346
|
Authorization: `Bearer ${key}`,
|
|
2172
2347
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2173
2348
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2349
|
+
...writeApprovalHeaders(writeApproval),
|
|
2174
2350
|
},
|
|
2175
2351
|
body: JSON.stringify(payload),
|
|
2176
2352
|
},
|
|
@@ -2178,15 +2354,16 @@ async function createRunQaqcBatch({ apiBase, key, timeoutMs, payload, idempotenc
|
|
|
2178
2354
|
);
|
|
2179
2355
|
|
|
2180
2356
|
if (!resp.ok || body.status !== "success") {
|
|
2181
|
-
const msg = body
|
|
2357
|
+
const msg = formatProjectApiErrorMessage(body, `Run QAQC request failed (${resp.status})`);
|
|
2182
2358
|
const err = new Error(msg);
|
|
2183
2359
|
err.status = resp.status;
|
|
2360
|
+
err.data = body?.data;
|
|
2184
2361
|
throw err;
|
|
2185
2362
|
}
|
|
2186
2363
|
return body.data || {};
|
|
2187
2364
|
}
|
|
2188
2365
|
|
|
2189
|
-
async function createMissionStatusUpdate({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
2366
|
+
async function createMissionStatusUpdate({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
2190
2367
|
const fetchFn = await getFetch();
|
|
2191
2368
|
const url = `${apiBase}/project-assistant/mission-status-update`;
|
|
2192
2369
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -2199,6 +2376,7 @@ async function createMissionStatusUpdate({ apiBase, key, timeoutMs, payload, ide
|
|
|
2199
2376
|
Authorization: `Bearer ${key}`,
|
|
2200
2377
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2201
2378
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2379
|
+
...writeApprovalHeaders(writeApproval),
|
|
2202
2380
|
},
|
|
2203
2381
|
body: JSON.stringify(payload),
|
|
2204
2382
|
},
|
|
@@ -2206,15 +2384,16 @@ async function createMissionStatusUpdate({ apiBase, key, timeoutMs, payload, ide
|
|
|
2206
2384
|
);
|
|
2207
2385
|
|
|
2208
2386
|
if (!resp.ok || body.status !== "success") {
|
|
2209
|
-
const msg = body
|
|
2387
|
+
const msg = formatProjectApiErrorMessage(body, `Mission status update request failed (${resp.status})`);
|
|
2210
2388
|
const err = new Error(msg);
|
|
2211
2389
|
err.status = resp.status;
|
|
2390
|
+
err.data = body?.data;
|
|
2212
2391
|
throw err;
|
|
2213
2392
|
}
|
|
2214
2393
|
return body.data || {};
|
|
2215
2394
|
}
|
|
2216
2395
|
|
|
2217
|
-
async function createMissionArchiveUpdate({ apiBase, key, timeoutMs, endpoint, payload, idempotencyKey, clientSessionId }) {
|
|
2396
|
+
async function createMissionArchiveUpdate({ apiBase, key, timeoutMs, endpoint, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
2218
2397
|
const fetchFn = await getFetch();
|
|
2219
2398
|
const url = `${apiBase}${endpoint}`;
|
|
2220
2399
|
const { resp, body } = await fetchJsonWithTimeout(
|
|
@@ -2227,6 +2406,7 @@ async function createMissionArchiveUpdate({ apiBase, key, timeoutMs, endpoint, p
|
|
|
2227
2406
|
Authorization: `Bearer ${key}`,
|
|
2228
2407
|
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
2229
2408
|
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
2409
|
+
...writeApprovalHeaders(writeApproval),
|
|
2230
2410
|
},
|
|
2231
2411
|
body: JSON.stringify(payload),
|
|
2232
2412
|
},
|
|
@@ -2234,9 +2414,10 @@ async function createMissionArchiveUpdate({ apiBase, key, timeoutMs, endpoint, p
|
|
|
2234
2414
|
);
|
|
2235
2415
|
|
|
2236
2416
|
if (!resp.ok || body.status !== "success") {
|
|
2237
|
-
const msg = body
|
|
2417
|
+
const msg = formatProjectApiErrorMessage(body, `Mission archive request failed (${resp.status})`);
|
|
2238
2418
|
const err = new Error(msg);
|
|
2239
2419
|
err.status = resp.status;
|
|
2420
|
+
err.data = body?.data;
|
|
2240
2421
|
throw err;
|
|
2241
2422
|
}
|
|
2242
2423
|
return body.data || {};
|
|
@@ -2370,6 +2551,12 @@ async function runRunQaqc(args) {
|
|
|
2370
2551
|
const apiBase = resolveApiBase(args);
|
|
2371
2552
|
const shouldWait = Boolean(args.wait || args.sync);
|
|
2372
2553
|
const shouldSync = Boolean(args.sync);
|
|
2554
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
2555
|
+
operation: "run-qaqc",
|
|
2556
|
+
artifactKind: "yaml",
|
|
2557
|
+
targets: missionIds,
|
|
2558
|
+
batchCount: missionIds.length,
|
|
2559
|
+
});
|
|
2373
2560
|
|
|
2374
2561
|
let data;
|
|
2375
2562
|
try {
|
|
@@ -2380,6 +2567,7 @@ async function runRunQaqc(args) {
|
|
|
2380
2567
|
payload,
|
|
2381
2568
|
idempotencyKey,
|
|
2382
2569
|
clientSessionId,
|
|
2570
|
+
writeApproval,
|
|
2383
2571
|
});
|
|
2384
2572
|
} catch (err) {
|
|
2385
2573
|
if (err?.name === "AbortError") {
|
|
@@ -2523,6 +2711,12 @@ async function runMissionStatus(args) {
|
|
|
2523
2711
|
|
|
2524
2712
|
const timeoutMs = resolveTimeoutMs(args);
|
|
2525
2713
|
const apiBase = resolveApiBase(args);
|
|
2714
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
2715
|
+
operation: "mission-status-update",
|
|
2716
|
+
artifactKind: "yaml",
|
|
2717
|
+
targets: missionIds,
|
|
2718
|
+
batchCount: missionIds.length,
|
|
2719
|
+
});
|
|
2526
2720
|
|
|
2527
2721
|
let data;
|
|
2528
2722
|
try {
|
|
@@ -2533,6 +2727,7 @@ async function runMissionStatus(args) {
|
|
|
2533
2727
|
payload,
|
|
2534
2728
|
idempotencyKey,
|
|
2535
2729
|
clientSessionId,
|
|
2730
|
+
writeApproval,
|
|
2536
2731
|
});
|
|
2537
2732
|
} catch (err) {
|
|
2538
2733
|
if (err?.name === "AbortError") {
|
|
@@ -2630,6 +2825,12 @@ async function runMissionArchiveCommand(args) {
|
|
|
2630
2825
|
const timeoutMs = resolveTimeoutMs(args);
|
|
2631
2826
|
const apiBase = resolveApiBase(args);
|
|
2632
2827
|
const endpoint = "/project-assistant/mission-archive";
|
|
2828
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
2829
|
+
operation: "mission-archive",
|
|
2830
|
+
artifactKind: "yaml",
|
|
2831
|
+
targets: missionIds,
|
|
2832
|
+
batchCount: missionIds.length,
|
|
2833
|
+
});
|
|
2633
2834
|
|
|
2634
2835
|
let data;
|
|
2635
2836
|
try {
|
|
@@ -2641,6 +2842,7 @@ async function runMissionArchiveCommand(args) {
|
|
|
2641
2842
|
payload,
|
|
2642
2843
|
idempotencyKey,
|
|
2643
2844
|
clientSessionId,
|
|
2845
|
+
writeApproval,
|
|
2644
2846
|
});
|
|
2645
2847
|
} catch (err) {
|
|
2646
2848
|
if (err?.name === "AbortError") {
|
|
@@ -2740,6 +2942,10 @@ async function runUpdateTeam(args) {
|
|
|
2740
2942
|
operation: "update_team",
|
|
2741
2943
|
payload,
|
|
2742
2944
|
});
|
|
2945
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
2946
|
+
operation: "update_team",
|
|
2947
|
+
artifactKind: "markdown",
|
|
2948
|
+
});
|
|
2743
2949
|
|
|
2744
2950
|
let data;
|
|
2745
2951
|
try {
|
|
@@ -2751,6 +2957,7 @@ async function runUpdateTeam(args) {
|
|
|
2751
2957
|
endpoint: "/project-assistant/project-comment",
|
|
2752
2958
|
idempotencyKey,
|
|
2753
2959
|
clientSessionId,
|
|
2960
|
+
writeApproval,
|
|
2754
2961
|
});
|
|
2755
2962
|
} catch (err) {
|
|
2756
2963
|
if (err?.name === "AbortError") {
|
|
@@ -2809,6 +3016,10 @@ async function runUpdateOwner(args) {
|
|
|
2809
3016
|
operation: "update_owner",
|
|
2810
3017
|
payload,
|
|
2811
3018
|
});
|
|
3019
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
3020
|
+
operation: "update_owner",
|
|
3021
|
+
artifactKind: "markdown",
|
|
3022
|
+
});
|
|
2812
3023
|
|
|
2813
3024
|
let data;
|
|
2814
3025
|
try {
|
|
@@ -2820,6 +3031,7 @@ async function runUpdateOwner(args) {
|
|
|
2820
3031
|
endpoint: "/project-assistant/update-owner",
|
|
2821
3032
|
idempotencyKey,
|
|
2822
3033
|
clientSessionId,
|
|
3034
|
+
writeApproval,
|
|
2823
3035
|
});
|
|
2824
3036
|
} catch (err) {
|
|
2825
3037
|
if (err?.name === "AbortError") {
|
|
@@ -2887,6 +3099,10 @@ async function runUpdateClient(args) {
|
|
|
2887
3099
|
operation: "update_client",
|
|
2888
3100
|
payload,
|
|
2889
3101
|
});
|
|
3102
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
3103
|
+
operation: "update_client",
|
|
3104
|
+
artifactKind: "markdown",
|
|
3105
|
+
});
|
|
2890
3106
|
|
|
2891
3107
|
let data;
|
|
2892
3108
|
try {
|
|
@@ -2898,6 +3114,7 @@ async function runUpdateClient(args) {
|
|
|
2898
3114
|
endpoint: "/project-assistant/client-update-drafts",
|
|
2899
3115
|
idempotencyKey,
|
|
2900
3116
|
clientSessionId,
|
|
3117
|
+
writeApproval,
|
|
2901
3118
|
});
|
|
2902
3119
|
} catch (err) {
|
|
2903
3120
|
if (err?.name === "AbortError") {
|
|
@@ -2990,20 +3207,48 @@ function stringifyYaml(value) {
|
|
|
2990
3207
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
2991
3208
|
}
|
|
2992
3209
|
|
|
2993
|
-
function stableJsonStringify(value) {
|
|
2994
|
-
if (Array.isArray(value)) {
|
|
2995
|
-
return `[${value.map((item) => stableJsonStringify(item)).join(",")}]`;
|
|
3210
|
+
function stableJsonStringify(value) {
|
|
3211
|
+
if (Array.isArray(value)) {
|
|
3212
|
+
return `[${value.map((item) => stableJsonStringify(item)).join(",")}]`;
|
|
2996
3213
|
}
|
|
2997
3214
|
if (value && typeof value === "object") {
|
|
2998
3215
|
const entries = Object.keys(value)
|
|
2999
3216
|
.sort()
|
|
3000
3217
|
.map((key) => `${JSON.stringify(key)}:${stableJsonStringify(value[key])}`);
|
|
3001
3218
|
return `{${entries.join(",")}}`;
|
|
3002
|
-
}
|
|
3003
|
-
return JSON.stringify(value);
|
|
3004
|
-
}
|
|
3005
|
-
|
|
3006
|
-
function
|
|
3219
|
+
}
|
|
3220
|
+
return JSON.stringify(value);
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
function jsonStringifyEnsureAscii(value) {
|
|
3224
|
+
return JSON.stringify(String(value)).replace(/[^\x00-\x7F]/g, (char) => {
|
|
3225
|
+
const code = char.charCodeAt(0);
|
|
3226
|
+
return `\\u${code.toString(16).padStart(4, "0")}`;
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
function pythonJsonDumpsCompatible(value) {
|
|
3231
|
+
if (Array.isArray(value)) {
|
|
3232
|
+
return `[${value.map((item) => pythonJsonDumpsCompatible(item)).join(", ")}]`;
|
|
3233
|
+
}
|
|
3234
|
+
if (value && typeof value === "object") {
|
|
3235
|
+
const entries = Object.keys(value)
|
|
3236
|
+
.sort()
|
|
3237
|
+
.map((key) => `${jsonStringifyEnsureAscii(key)}: ${pythonJsonDumpsCompatible(value[key])}`);
|
|
3238
|
+
return `{${entries.join(", ")}}`;
|
|
3239
|
+
}
|
|
3240
|
+
if (value === null) return "null";
|
|
3241
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
3242
|
+
if (typeof value === "number") {
|
|
3243
|
+
if (Number.isNaN(value)) return "NaN";
|
|
3244
|
+
if (value === Infinity) return "Infinity";
|
|
3245
|
+
if (value === -Infinity) return "-Infinity";
|
|
3246
|
+
return JSON.stringify(value);
|
|
3247
|
+
}
|
|
3248
|
+
return jsonStringifyEnsureAscii(value);
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
function parseYaml(text) {
|
|
3007
3252
|
const raw = String(text || "").trim();
|
|
3008
3253
|
if (!raw) return null;
|
|
3009
3254
|
if (raw.startsWith("{") || raw.startsWith("[")) {
|
|
@@ -4269,7 +4514,7 @@ async function fetchSuggestionsSyncSnapshot({ apiBase, key, timeoutMs, actorScop
|
|
|
4269
4514
|
return body.data || {};
|
|
4270
4515
|
}
|
|
4271
4516
|
|
|
4272
|
-
async function createProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
4517
|
+
async function createProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
4273
4518
|
return postSuggestionsMutation({
|
|
4274
4519
|
apiBase,
|
|
4275
4520
|
key,
|
|
@@ -4278,10 +4523,11 @@ async function createProjectSuggestions({ apiBase, key, timeoutMs, payload, idem
|
|
|
4278
4523
|
payload,
|
|
4279
4524
|
idempotencyKey,
|
|
4280
4525
|
clientSessionId,
|
|
4526
|
+
writeApproval,
|
|
4281
4527
|
});
|
|
4282
4528
|
}
|
|
4283
4529
|
|
|
4284
|
-
async function reviseProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
4530
|
+
async function reviseProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
4285
4531
|
return postSuggestionsMutation({
|
|
4286
4532
|
apiBase,
|
|
4287
4533
|
key,
|
|
@@ -4290,10 +4536,11 @@ async function reviseProjectSuggestions({ apiBase, key, timeoutMs, payload, idem
|
|
|
4290
4536
|
payload,
|
|
4291
4537
|
idempotencyKey,
|
|
4292
4538
|
clientSessionId,
|
|
4539
|
+
writeApproval,
|
|
4293
4540
|
});
|
|
4294
4541
|
}
|
|
4295
4542
|
|
|
4296
|
-
async function reviewProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
|
|
4543
|
+
async function reviewProjectSuggestions({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId, writeApproval }) {
|
|
4297
4544
|
return postSuggestionsMutation({
|
|
4298
4545
|
apiBase,
|
|
4299
4546
|
key,
|
|
@@ -4302,6 +4549,7 @@ async function reviewProjectSuggestions({ apiBase, key, timeoutMs, payload, idem
|
|
|
4302
4549
|
payload,
|
|
4303
4550
|
idempotencyKey,
|
|
4304
4551
|
clientSessionId,
|
|
4552
|
+
writeApproval,
|
|
4305
4553
|
});
|
|
4306
4554
|
}
|
|
4307
4555
|
|
|
@@ -4419,6 +4667,11 @@ async function runCreatePrd(args) {
|
|
|
4419
4667
|
operation: "create_prd",
|
|
4420
4668
|
payload,
|
|
4421
4669
|
});
|
|
4670
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
4671
|
+
operation: "create_prd",
|
|
4672
|
+
artifactKind: "markdown",
|
|
4673
|
+
targets: prdApprovalTargets(payload),
|
|
4674
|
+
});
|
|
4422
4675
|
try {
|
|
4423
4676
|
data = await postSuggestionsMutation({
|
|
4424
4677
|
apiBase,
|
|
@@ -4428,6 +4681,7 @@ async function runCreatePrd(args) {
|
|
|
4428
4681
|
endpoint: "/project-assistant/create-prd",
|
|
4429
4682
|
idempotencyKey,
|
|
4430
4683
|
clientSessionId,
|
|
4684
|
+
writeApproval,
|
|
4431
4685
|
});
|
|
4432
4686
|
} catch (err) {
|
|
4433
4687
|
if (err?.name === "AbortError") {
|
|
@@ -4478,6 +4732,13 @@ async function runCreatePrd(args) {
|
|
|
4478
4732
|
operation: "create_prds",
|
|
4479
4733
|
payload,
|
|
4480
4734
|
});
|
|
4735
|
+
const prdTargets = prdApprovalTargets(payload);
|
|
4736
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
4737
|
+
operation: "create_prds",
|
|
4738
|
+
artifactKind: "markdown",
|
|
4739
|
+
targets: prdTargets,
|
|
4740
|
+
batchCount: prdTargets.length,
|
|
4741
|
+
});
|
|
4481
4742
|
const data = await postSuggestionsMutation({
|
|
4482
4743
|
apiBase,
|
|
4483
4744
|
key,
|
|
@@ -4486,6 +4747,7 @@ async function runCreatePrd(args) {
|
|
|
4486
4747
|
endpoint: "/project-assistant/create-prds",
|
|
4487
4748
|
idempotencyKey,
|
|
4488
4749
|
clientSessionId,
|
|
4750
|
+
writeApproval,
|
|
4489
4751
|
});
|
|
4490
4752
|
aggregated.project_id = data.project_id || null;
|
|
4491
4753
|
aggregated.created_count = Number(data.created_count || 0);
|
|
@@ -5065,6 +5327,13 @@ async function runFeedbackValidateOrApply(args, mode) {
|
|
|
5065
5327
|
payload,
|
|
5066
5328
|
})
|
|
5067
5329
|
: null;
|
|
5330
|
+
const writeApproval = mode === "apply"
|
|
5331
|
+
? requireWriteApproval(args, payload, {
|
|
5332
|
+
operation: `feedback_refinement_apply:${feedbackId}`,
|
|
5333
|
+
artifactKind: "yaml",
|
|
5334
|
+
targets: [feedbackId],
|
|
5335
|
+
})
|
|
5336
|
+
: null;
|
|
5068
5337
|
|
|
5069
5338
|
let data;
|
|
5070
5339
|
try {
|
|
@@ -5077,6 +5346,7 @@ async function runFeedbackValidateOrApply(args, mode) {
|
|
|
5077
5346
|
payload,
|
|
5078
5347
|
idempotencyKey,
|
|
5079
5348
|
clientSessionId,
|
|
5349
|
+
writeApproval,
|
|
5080
5350
|
});
|
|
5081
5351
|
} catch (err) {
|
|
5082
5352
|
if (args.json) {
|
|
@@ -5122,33 +5392,33 @@ function resolveFeedbackRequestIdArg(args) {
|
|
|
5122
5392
|
return firstNonEmptyString(args["request-id"], args.requestId, args.request_id, args._?.[1]);
|
|
5123
5393
|
}
|
|
5124
5394
|
|
|
5125
|
-
function resolveFeedbackEventIdArg(args) {
|
|
5126
|
-
return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
|
|
5127
|
-
}
|
|
5395
|
+
function resolveFeedbackEventIdArg(args) {
|
|
5396
|
+
return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
|
|
5397
|
+
}
|
|
5128
5398
|
|
|
5129
|
-
function resolveFeedbackVersionIdArg(args) {
|
|
5130
|
-
return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
|
|
5131
|
-
}
|
|
5132
|
-
|
|
5133
|
-
async function resolveFeedbackCommentContent(args, feedbackId) {
|
|
5134
|
-
const explicit = firstNonEmptyString(args.body, args.content, args["body-markdown"], args.bodyMarkdown, args.body_markdown);
|
|
5135
|
-
if (explicit) return explicit;
|
|
5136
|
-
|
|
5137
|
-
const bodyFile = firstNonEmptyString(args["body-file"], args.bodyFile, args.body_file);
|
|
5138
|
-
if (bodyFile) {
|
|
5139
|
-
const bodyPath = resolveInputFile(bodyFile, "Feedback comment body");
|
|
5140
|
-
return fs.readFileSync(bodyPath, "utf8");
|
|
5141
|
-
}
|
|
5142
|
-
|
|
5143
|
-
if (args.stdin) {
|
|
5144
|
-
return readStdinText();
|
|
5145
|
-
}
|
|
5146
|
-
|
|
5147
|
-
const positionals = Array.isArray(args._) ? args._.slice(1) : [];
|
|
5148
|
-
const feedbackIdFromPositional = positionals[0] && String(positionals[0]) === String(feedbackId);
|
|
5149
|
-
const textParts = feedbackIdFromPositional ? positionals.slice(1) : positionals;
|
|
5150
|
-
return textParts.join(" ");
|
|
5151
|
-
}
|
|
5399
|
+
function resolveFeedbackVersionIdArg(args) {
|
|
5400
|
+
return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
|
|
5401
|
+
}
|
|
5402
|
+
|
|
5403
|
+
async function resolveFeedbackCommentContent(args, feedbackId) {
|
|
5404
|
+
const explicit = firstNonEmptyString(args.body, args.content, args["body-markdown"], args.bodyMarkdown, args.body_markdown);
|
|
5405
|
+
if (explicit) return explicit;
|
|
5406
|
+
|
|
5407
|
+
const bodyFile = firstNonEmptyString(args["body-file"], args.bodyFile, args.body_file);
|
|
5408
|
+
if (bodyFile) {
|
|
5409
|
+
const bodyPath = resolveInputFile(bodyFile, "Feedback comment body");
|
|
5410
|
+
return fs.readFileSync(bodyPath, "utf8");
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
if (args.stdin) {
|
|
5414
|
+
return readStdinText();
|
|
5415
|
+
}
|
|
5416
|
+
|
|
5417
|
+
const positionals = Array.isArray(args._) ? args._.slice(1) : [];
|
|
5418
|
+
const feedbackIdFromPositional = positionals[0] && String(positionals[0]) === String(feedbackId);
|
|
5419
|
+
const textParts = feedbackIdFromPositional ? positionals.slice(1) : positionals;
|
|
5420
|
+
return textParts.join(" ");
|
|
5421
|
+
}
|
|
5152
5422
|
|
|
5153
5423
|
function summarizeFeedbackReviewRequestPayload(data) {
|
|
5154
5424
|
const request = data?.request || data || {};
|
|
@@ -5207,6 +5477,11 @@ async function runFeedbackSubmit(args) {
|
|
|
5207
5477
|
operation: `feedback_refinement_request_submit:${feedbackId}`,
|
|
5208
5478
|
payload,
|
|
5209
5479
|
});
|
|
5480
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5481
|
+
operation: `feedback_refinement_request_submit:${feedbackId}`,
|
|
5482
|
+
artifactKind: "yaml",
|
|
5483
|
+
targets: [feedbackId],
|
|
5484
|
+
});
|
|
5210
5485
|
|
|
5211
5486
|
let data;
|
|
5212
5487
|
try {
|
|
@@ -5218,6 +5493,7 @@ async function runFeedbackSubmit(args) {
|
|
|
5218
5493
|
payload,
|
|
5219
5494
|
idempotencyKey,
|
|
5220
5495
|
clientSessionId,
|
|
5496
|
+
writeApproval,
|
|
5221
5497
|
});
|
|
5222
5498
|
} catch (err) {
|
|
5223
5499
|
if (args.json) {
|
|
@@ -5273,6 +5549,11 @@ async function runFeedbackRevise(args) {
|
|
|
5273
5549
|
operation: `feedback_refinement_request_revise:${requestId}`,
|
|
5274
5550
|
payload,
|
|
5275
5551
|
});
|
|
5552
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5553
|
+
operation: `feedback_refinement_request_revise:${requestId}`,
|
|
5554
|
+
artifactKind: "yaml",
|
|
5555
|
+
targets: [requestId],
|
|
5556
|
+
});
|
|
5276
5557
|
|
|
5277
5558
|
let data;
|
|
5278
5559
|
try {
|
|
@@ -5285,6 +5566,7 @@ async function runFeedbackRevise(args) {
|
|
|
5285
5566
|
payload,
|
|
5286
5567
|
idempotencyKey,
|
|
5287
5568
|
clientSessionId,
|
|
5569
|
+
writeApproval,
|
|
5288
5570
|
});
|
|
5289
5571
|
} catch (err) {
|
|
5290
5572
|
if (args.json) {
|
|
@@ -5416,6 +5698,15 @@ async function runFeedbackReviewDecision(args) {
|
|
|
5416
5698
|
operation: `feedback_review_batch:${action}:${items.map((item) => item.request_id || item.id || "").join(",")}`,
|
|
5417
5699
|
payload,
|
|
5418
5700
|
});
|
|
5701
|
+
const requestTargets = items
|
|
5702
|
+
.map((item, index) => firstNonEmptyString(item.request_id, item.id, item._id) || `index:${index}`)
|
|
5703
|
+
.filter(Boolean);
|
|
5704
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5705
|
+
operation: `feedback_review_batch:${action}`,
|
|
5706
|
+
artifactKind: "yaml",
|
|
5707
|
+
targets: requestTargets,
|
|
5708
|
+
batchCount: items.length,
|
|
5709
|
+
});
|
|
5419
5710
|
|
|
5420
5711
|
let data;
|
|
5421
5712
|
try {
|
|
@@ -5426,6 +5717,7 @@ async function runFeedbackReviewDecision(args) {
|
|
|
5426
5717
|
payload,
|
|
5427
5718
|
idempotencyKey,
|
|
5428
5719
|
clientSessionId,
|
|
5720
|
+
writeApproval,
|
|
5429
5721
|
});
|
|
5430
5722
|
} catch (err) {
|
|
5431
5723
|
if (args.json) {
|
|
@@ -5475,6 +5767,11 @@ async function runFeedbackReviewDecision(args) {
|
|
|
5475
5767
|
operation: `feedback_review_request_${operationAction}:${requestId}`,
|
|
5476
5768
|
payload,
|
|
5477
5769
|
});
|
|
5770
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5771
|
+
operation: `feedback_review_request_${operationAction}:${requestId}`,
|
|
5772
|
+
artifactKind: "yaml",
|
|
5773
|
+
targets: [requestId],
|
|
5774
|
+
});
|
|
5478
5775
|
|
|
5479
5776
|
let data;
|
|
5480
5777
|
try {
|
|
@@ -5487,6 +5784,7 @@ async function runFeedbackReviewDecision(args) {
|
|
|
5487
5784
|
payload,
|
|
5488
5785
|
idempotencyKey,
|
|
5489
5786
|
clientSessionId,
|
|
5787
|
+
writeApproval,
|
|
5490
5788
|
});
|
|
5491
5789
|
} catch (err) {
|
|
5492
5790
|
if (args.json) {
|
|
@@ -5507,7 +5805,7 @@ async function runFeedbackReviewDecision(args) {
|
|
|
5507
5805
|
if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
|
|
5508
5806
|
}
|
|
5509
5807
|
|
|
5510
|
-
async function runFeedbackMove(args) {
|
|
5808
|
+
async function runFeedbackMove(args) {
|
|
5511
5809
|
const key = getProjectApiKey();
|
|
5512
5810
|
if (!key) {
|
|
5513
5811
|
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
@@ -5545,6 +5843,12 @@ async function runFeedbackMove(args) {
|
|
|
5545
5843
|
operation: `feedback_board_batch_move:${toState}:${feedbackIds.join(",")}`,
|
|
5546
5844
|
payload,
|
|
5547
5845
|
});
|
|
5846
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5847
|
+
operation: `feedback_board_batch_move:${toState}`,
|
|
5848
|
+
artifactKind: "yaml",
|
|
5849
|
+
targets: feedbackIds,
|
|
5850
|
+
batchCount: feedbackIds.length,
|
|
5851
|
+
});
|
|
5548
5852
|
let data;
|
|
5549
5853
|
try {
|
|
5550
5854
|
data = await postFeedbackBatchBoardMutation({
|
|
@@ -5554,6 +5858,7 @@ async function runFeedbackMove(args) {
|
|
|
5554
5858
|
payload,
|
|
5555
5859
|
idempotencyKey,
|
|
5556
5860
|
clientSessionId,
|
|
5861
|
+
writeApproval,
|
|
5557
5862
|
});
|
|
5558
5863
|
} catch (err) {
|
|
5559
5864
|
if (args.json) {
|
|
@@ -5581,6 +5886,11 @@ async function runFeedbackMove(args) {
|
|
|
5581
5886
|
operation: `feedback_board_move:${feedbackId}`,
|
|
5582
5887
|
payload,
|
|
5583
5888
|
});
|
|
5889
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5890
|
+
operation: `feedback_board_move:${feedbackId}`,
|
|
5891
|
+
artifactKind: "yaml",
|
|
5892
|
+
targets: [feedbackId],
|
|
5893
|
+
});
|
|
5584
5894
|
|
|
5585
5895
|
let data;
|
|
5586
5896
|
try {
|
|
@@ -5593,6 +5903,7 @@ async function runFeedbackMove(args) {
|
|
|
5593
5903
|
payload,
|
|
5594
5904
|
idempotencyKey,
|
|
5595
5905
|
clientSessionId,
|
|
5906
|
+
writeApproval,
|
|
5596
5907
|
});
|
|
5597
5908
|
} catch (err) {
|
|
5598
5909
|
if (args.json) {
|
|
@@ -5613,85 +5924,92 @@ async function runFeedbackMove(args) {
|
|
|
5613
5924
|
console.log(`State: ${data.feedback_state || toState}`);
|
|
5614
5925
|
if (data.event?.event_id) console.log(`Event: ${data.event.event_id}`);
|
|
5615
5926
|
if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
|
|
5616
|
-
}
|
|
5617
|
-
|
|
5618
|
-
async function runFeedbackComment(args) {
|
|
5619
|
-
const key = getProjectApiKey();
|
|
5620
|
-
if (!key) {
|
|
5621
|
-
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
5622
|
-
process.exit(1);
|
|
5623
|
-
}
|
|
5624
|
-
const feedbackId = resolveFeedbackIdArg(args);
|
|
5625
|
-
if (!feedbackId) {
|
|
5626
|
-
console.error("Missing --feedback-id.");
|
|
5627
|
-
process.exit(1);
|
|
5628
|
-
}
|
|
5629
|
-
|
|
5630
|
-
const content = String(await resolveFeedbackCommentContent(args, feedbackId) || "").trim();
|
|
5631
|
-
if (!content) {
|
|
5632
|
-
console.error("Missing feedback comment body. Use --body, --body-file, --stdin, or positional text.");
|
|
5633
|
-
process.exit(1);
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
|
-
const payload = { content };
|
|
5637
|
-
const timeoutMs = resolveTimeoutMs(args);
|
|
5638
|
-
const apiBase = resolveApiBase(args);
|
|
5639
|
-
const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
|
|
5640
|
-
if (clientSessionId) payload.client_session_id = clientSessionId;
|
|
5641
|
-
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
5642
|
-
args,
|
|
5643
|
-
operation: `feedback_comment_create:${feedbackId}`,
|
|
5644
|
-
payload,
|
|
5645
|
-
});
|
|
5646
|
-
|
|
5647
|
-
if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
|
|
5648
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
5649
|
-
return;
|
|
5650
|
-
}
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
}
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
if (
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
}
|
|
5690
|
-
if (
|
|
5691
|
-
if (
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5927
|
+
}
|
|
5928
|
+
|
|
5929
|
+
async function runFeedbackComment(args) {
|
|
5930
|
+
const key = getProjectApiKey();
|
|
5931
|
+
if (!key) {
|
|
5932
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
5933
|
+
process.exit(1);
|
|
5934
|
+
}
|
|
5935
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
5936
|
+
if (!feedbackId) {
|
|
5937
|
+
console.error("Missing --feedback-id.");
|
|
5938
|
+
process.exit(1);
|
|
5939
|
+
}
|
|
5940
|
+
|
|
5941
|
+
const content = String(await resolveFeedbackCommentContent(args, feedbackId) || "").trim();
|
|
5942
|
+
if (!content) {
|
|
5943
|
+
console.error("Missing feedback comment body. Use --body, --body-file, --stdin, or positional text.");
|
|
5944
|
+
process.exit(1);
|
|
5945
|
+
}
|
|
5946
|
+
|
|
5947
|
+
const payload = { content };
|
|
5948
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
5949
|
+
const apiBase = resolveApiBase(args);
|
|
5950
|
+
const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
|
|
5951
|
+
if (clientSessionId) payload.client_session_id = clientSessionId;
|
|
5952
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
5953
|
+
args,
|
|
5954
|
+
operation: `feedback_comment_create:${feedbackId}`,
|
|
5955
|
+
payload,
|
|
5956
|
+
});
|
|
5957
|
+
|
|
5958
|
+
if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
|
|
5959
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
5960
|
+
return;
|
|
5961
|
+
}
|
|
5962
|
+
|
|
5963
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
5964
|
+
operation: `feedback_comment_create:${feedbackId}`,
|
|
5965
|
+
artifactKind: "markdown",
|
|
5966
|
+
targets: [feedbackId],
|
|
5967
|
+
});
|
|
5968
|
+
|
|
5969
|
+
let data;
|
|
5970
|
+
try {
|
|
5971
|
+
data = await postFeedbackComment({
|
|
5972
|
+
apiBase,
|
|
5973
|
+
key,
|
|
5974
|
+
timeoutMs,
|
|
5975
|
+
feedbackId,
|
|
5976
|
+
payload,
|
|
5977
|
+
idempotencyKey,
|
|
5978
|
+
clientSessionId,
|
|
5979
|
+
writeApproval,
|
|
5980
|
+
});
|
|
5981
|
+
} catch (err) {
|
|
5982
|
+
if (args.json) {
|
|
5983
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
|
|
5984
|
+
} else {
|
|
5985
|
+
console.error("Feedback comment failed:", err?.message || err);
|
|
5986
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
5987
|
+
}
|
|
5988
|
+
process.exit(1);
|
|
5989
|
+
}
|
|
5990
|
+
|
|
5991
|
+
const postSync = await refreshFeedbackSnapshotAfterMutation(args, { apiBase, key, timeoutMs });
|
|
5992
|
+
if (args.json) {
|
|
5993
|
+
console.log(JSON.stringify({ ok: true, ...data, post_sync: postSync }, null, 2));
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
if (data.comment_id) console.log(`Comment ID: ${data.comment_id}`);
|
|
5998
|
+
console.log(`Feedback: ${data.feedback_id || feedbackId}`);
|
|
5999
|
+
if (data.project_id) console.log(`Project ID: ${data.project_id}`);
|
|
6000
|
+
if (data.user_name) console.log(`Author: ${data.user_name}`);
|
|
6001
|
+
if (data.created_at) console.log(`Created At: ${data.created_at}`);
|
|
6002
|
+
if (data.content) {
|
|
6003
|
+
const body = String(data.content);
|
|
6004
|
+
const truncated = body.length > 2000 ? `${body.slice(0, 2000)}\n...[truncated]` : body;
|
|
6005
|
+
console.log("Content:");
|
|
6006
|
+
console.log(truncated);
|
|
6007
|
+
}
|
|
6008
|
+
if (postSync?.data_root) console.log(`Synced feedback: ${postSync.data_root}`);
|
|
6009
|
+
if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
|
|
6010
|
+
}
|
|
6011
|
+
|
|
6012
|
+
async function runFeedbackUndo(args) {
|
|
5695
6013
|
const key = getProjectApiKey();
|
|
5696
6014
|
if (!key) {
|
|
5697
6015
|
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
@@ -5717,6 +6035,17 @@ async function runFeedbackUndo(args) {
|
|
|
5717
6035
|
payload,
|
|
5718
6036
|
});
|
|
5719
6037
|
|
|
6038
|
+
if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
|
|
6039
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
6040
|
+
return;
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
const writeApproval = requireWriteApproval(args, payload, {
|
|
6044
|
+
operation: `feedback_event_undo:${feedbackId}:${eventId}`,
|
|
6045
|
+
artifactKind: "yaml",
|
|
6046
|
+
targets: [feedbackId, eventId],
|
|
6047
|
+
});
|
|
6048
|
+
|
|
5720
6049
|
let data;
|
|
5721
6050
|
try {
|
|
5722
6051
|
data = await postFeedbackBoardMutation({
|
|
@@ -5728,6 +6057,7 @@ async function runFeedbackUndo(args) {
|
|
|
5728
6057
|
payload,
|
|
5729
6058
|
idempotencyKey,
|
|
5730
6059
|
clientSessionId,
|
|
6060
|
+
writeApproval,
|
|
5731
6061
|
});
|
|
5732
6062
|
} catch (err) {
|
|
5733
6063
|
if (args.json) {
|
|
@@ -5886,15 +6216,15 @@ async function runFeedback(args) {
|
|
|
5886
6216
|
await runFeedbackReviewDecision(args);
|
|
5887
6217
|
return;
|
|
5888
6218
|
}
|
|
5889
|
-
if (subcommand === "move") {
|
|
5890
|
-
await runFeedbackMove(args);
|
|
5891
|
-
return;
|
|
5892
|
-
}
|
|
5893
|
-
if (subcommand === "comment" || subcommand === "comments") {
|
|
5894
|
-
await runFeedbackComment(args);
|
|
5895
|
-
return;
|
|
5896
|
-
}
|
|
5897
|
-
if (subcommand === "undo") {
|
|
6219
|
+
if (subcommand === "move") {
|
|
6220
|
+
await runFeedbackMove(args);
|
|
6221
|
+
return;
|
|
6222
|
+
}
|
|
6223
|
+
if (subcommand === "comment" || subcommand === "comments") {
|
|
6224
|
+
await runFeedbackComment(args);
|
|
6225
|
+
return;
|
|
6226
|
+
}
|
|
6227
|
+
if (subcommand === "undo") {
|
|
5898
6228
|
await runFeedbackUndo(args);
|
|
5899
6229
|
return;
|
|
5900
6230
|
}
|
|
@@ -5910,7 +6240,7 @@ async function runFeedback(args) {
|
|
|
5910
6240
|
await runFeedbackGetOrHistory(args, subcommand);
|
|
5911
6241
|
return;
|
|
5912
6242
|
}
|
|
5913
|
-
console.error("Unknown feedback command. Use status, edit, assign, archive, submit, revise, reviews, review, move, comment, undo, prd-versions, prd-diff, get, history, validate, or apply.");
|
|
6243
|
+
console.error("Unknown feedback command. Use status, edit, assign, archive, submit, revise, reviews, review, move, comment, undo, prd-versions, prd-diff, get, history, validate, or apply.");
|
|
5914
6244
|
process.exit(1);
|
|
5915
6245
|
}
|
|
5916
6246
|
|
|
@@ -6106,6 +6436,14 @@ async function runSuggestionsCreate(args) {
|
|
|
6106
6436
|
return;
|
|
6107
6437
|
}
|
|
6108
6438
|
|
|
6439
|
+
const createTargets = suggestionApprovalTargets(context.payload);
|
|
6440
|
+
const writeApproval = requireWriteApproval(args, context.payload, {
|
|
6441
|
+
operation: "suggestions.create",
|
|
6442
|
+
artifactKind: "yaml",
|
|
6443
|
+
targets: createTargets,
|
|
6444
|
+
batchCount: createTargets.length > 1 ? createTargets.length : null,
|
|
6445
|
+
});
|
|
6446
|
+
|
|
6109
6447
|
let data;
|
|
6110
6448
|
try {
|
|
6111
6449
|
data = await createProjectSuggestions({
|
|
@@ -6115,6 +6453,7 @@ async function runSuggestionsCreate(args) {
|
|
|
6115
6453
|
payload: context.payload,
|
|
6116
6454
|
idempotencyKey: context.idempotencyKey,
|
|
6117
6455
|
clientSessionId: context.clientSessionId,
|
|
6456
|
+
writeApproval,
|
|
6118
6457
|
});
|
|
6119
6458
|
} catch (err) {
|
|
6120
6459
|
console.error("Suggestions create failed:", err?.message || err);
|
|
@@ -6191,6 +6530,14 @@ async function runSuggestionsRevise(args) {
|
|
|
6191
6530
|
return;
|
|
6192
6531
|
}
|
|
6193
6532
|
|
|
6533
|
+
const reviseTargets = suggestionApprovalTargets(context.payload);
|
|
6534
|
+
const writeApproval = requireWriteApproval(args, context.payload, {
|
|
6535
|
+
operation: "suggestions.revise",
|
|
6536
|
+
artifactKind: "yaml",
|
|
6537
|
+
targets: reviseTargets,
|
|
6538
|
+
batchCount: reviseTargets.length > 1 ? reviseTargets.length : null,
|
|
6539
|
+
});
|
|
6540
|
+
|
|
6194
6541
|
let data;
|
|
6195
6542
|
try {
|
|
6196
6543
|
data = await reviseProjectSuggestions({
|
|
@@ -6200,6 +6547,7 @@ async function runSuggestionsRevise(args) {
|
|
|
6200
6547
|
payload: context.payload,
|
|
6201
6548
|
idempotencyKey: context.idempotencyKey,
|
|
6202
6549
|
clientSessionId: context.clientSessionId,
|
|
6550
|
+
writeApproval,
|
|
6203
6551
|
});
|
|
6204
6552
|
} catch (err) {
|
|
6205
6553
|
console.error("Suggestions revise failed:", err?.message || err);
|
|
@@ -6274,6 +6622,14 @@ async function runSuggestionsReview(args) {
|
|
|
6274
6622
|
return;
|
|
6275
6623
|
}
|
|
6276
6624
|
|
|
6625
|
+
const reviewTargets = suggestionApprovalTargets(context.payload);
|
|
6626
|
+
const writeApproval = requireWriteApproval(args, context.payload, {
|
|
6627
|
+
operation: "suggestions.review",
|
|
6628
|
+
artifactKind: "yaml",
|
|
6629
|
+
targets: reviewTargets,
|
|
6630
|
+
batchCount: reviewTargets.length > 1 ? reviewTargets.length : null,
|
|
6631
|
+
});
|
|
6632
|
+
|
|
6277
6633
|
let data;
|
|
6278
6634
|
try {
|
|
6279
6635
|
data = await reviewProjectSuggestions({
|
|
@@ -6283,6 +6639,7 @@ async function runSuggestionsReview(args) {
|
|
|
6283
6639
|
payload: context.payload,
|
|
6284
6640
|
idempotencyKey: context.idempotencyKey,
|
|
6285
6641
|
clientSessionId: context.clientSessionId,
|
|
6642
|
+
writeApproval,
|
|
6286
6643
|
});
|
|
6287
6644
|
} catch (err) {
|
|
6288
6645
|
console.error("Suggestions review failed:", err?.message || err);
|