@mytegroupinc/myte-core 0.0.21 → 0.0.23
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 +3 -1
- package/cli.js +797 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ This package exists so the public wrapper can stay small and versioned cleanly.
|
|
|
19
19
|
## Behavior Summary
|
|
20
20
|
|
|
21
21
|
- Snapshot-style commands such as `bootstrap`, `sync-qaqc`, `feedback-sync`, and `suggestions sync` write local `MyteCommandCenter` data.
|
|
22
|
-
- `feedback status|edit|assign|archive` writes reviewable local YAML artifacts
|
|
22
|
+
- `feedback status|edit|assign|archive` writes reviewable local YAML artifacts; `feedback submit|revise|reviews|review` routes them through the backend owner-review membrane.
|
|
23
|
+
- `feedback move|undo|prd-versions|prd-diff|history` calls the project-key Feedback API while leaving lifecycle rules and permissions server-side.
|
|
24
|
+
- `feedback validate|apply` remains available for validation and owner-direct apply paths; live authorization, stale checks, and history stay server-side.
|
|
23
25
|
- `query --with-diff` requires project repos to be configured for diff collection and fails fast when no matching local project repo can be resolved.
|
|
24
26
|
- Public package documentation is intentionally minimal. Internal rollout and design notes are not part of the npm package contract.
|
package/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
} = require("./lib/ai-gateway");
|
|
23
23
|
|
|
24
24
|
const DEFAULT_API_BASE = "https://api.myte.dev";
|
|
25
|
+
const DEFAULT_DIFF_LIMIT_CHARS = 500_000;
|
|
25
26
|
const REMOVED_COMMAND_MESSAGES = {
|
|
26
27
|
ask: "The `ask` alias has been removed. Use `myte query \"...\"`.",
|
|
27
28
|
chat: "The `chat` command has been removed. Use repeated `myte query` calls instead.",
|
|
@@ -143,6 +144,14 @@ function parseArgs(argv) {
|
|
|
143
144
|
"status",
|
|
144
145
|
"source",
|
|
145
146
|
"feedback-id",
|
|
147
|
+
"request-id",
|
|
148
|
+
"event-id",
|
|
149
|
+
"version-id",
|
|
150
|
+
"compare-to",
|
|
151
|
+
"action",
|
|
152
|
+
"decision",
|
|
153
|
+
"to-state",
|
|
154
|
+
"from-state",
|
|
146
155
|
"reason",
|
|
147
156
|
"review-action",
|
|
148
157
|
"idempotency-key",
|
|
@@ -151,6 +160,7 @@ function parseArgs(argv) {
|
|
|
151
160
|
"due-date",
|
|
152
161
|
"priority",
|
|
153
162
|
"review-note",
|
|
163
|
+
"final-file",
|
|
154
164
|
"tags",
|
|
155
165
|
"tag",
|
|
156
166
|
"mission-ids",
|
|
@@ -226,6 +236,14 @@ function printHelp() {
|
|
|
226
236
|
" myte feedback-sync [--status <value>] [--source <value>] [--with-prd-text|--no-with-prd-text] [--output-dir ./MyteCommandCenter] [--json]",
|
|
227
237
|
" myte feedback status --feedback-id <id> --status todo|in_progress|in_review|completed|deployed|rejected|archived --reason \"...\"",
|
|
228
238
|
" myte feedback edit --feedback-id <id> [--title \"...\"] [--feedback-text \"...\"] [--priority High] [--reason \"...\"]",
|
|
239
|
+
" myte feedback submit --file ./MyteCommandCenter/reviews/feedback/<id>-edit.yml [--json]",
|
|
240
|
+
" myte feedback revise --request-id <id> --file ./MyteCommandCenter/reviews/feedback/<id>-edit.yml [--json]",
|
|
241
|
+
" myte feedback reviews [--status open|terminal|all] [--request-id <id>] [--json]",
|
|
242
|
+
" myte feedback review --request-id <id> --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
|
|
243
|
+
" myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
|
|
244
|
+
" myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
|
|
245
|
+
" myte feedback prd-versions --feedback-id <id> [--json]",
|
|
246
|
+
" myte feedback prd-diff --feedback-id <id> --version-id <id> [--compare-to <id>] [--json]",
|
|
229
247
|
" myte feedback validate --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
|
|
230
248
|
" myte feedback apply --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
|
|
231
249
|
" myte create-prd <file.md> [more.md ...] [--json] [--title \"...\"] [--description \"...\"]",
|
|
@@ -308,13 +326,17 @@ function printHelp() {
|
|
|
308
326
|
"",
|
|
309
327
|
"feedback review contract:",
|
|
310
328
|
" - Draft commands write review artifacts under MyteCommandCenter/reviews/feedback/*.yml for local IDE diff review",
|
|
311
|
-
" -
|
|
329
|
+
" - submit/revise route artifacts through the owner-review membrane before live feedback mutation",
|
|
330
|
+
" - reviews/review expose owner/delegate approval, rejection, request-changes, and cancel decisions",
|
|
331
|
+
" - move/undo operate safe board states directly; governed states remain backend-authorized",
|
|
332
|
+
" - prd-versions/prd-diff expose retained PRD versions and backend-generated text diffs",
|
|
333
|
+
" - validate/apply send artifacts to /api/project-assistant/feedback/<id>/refinement/* for validation or owner-direct apply",
|
|
312
334
|
" - The backend owns authorization, stale snapshot checks, allowed field/transition rules, and history",
|
|
313
335
|
" - apply is idempotent and does not rewrite local feedback.yml; run feedback-sync after apply to refresh local state",
|
|
314
336
|
"",
|
|
315
337
|
"Options:",
|
|
316
338
|
" --with-diff Include deterministic git diffs (project-scoped; fails fast if no project repos are configured or resolved)",
|
|
317
|
-
" --diff-limit <chars> Truncate diff context to N chars (default:
|
|
339
|
+
" --diff-limit <chars> Truncate diff context to N chars (default: 500000)",
|
|
318
340
|
" --timeout-ms <ms> Request timeout (default: 300000)",
|
|
319
341
|
" --base-url <url> API base (default: https://api.myte.dev)",
|
|
320
342
|
" --payload-file <path> Raw OpenAI-style chat-completions payload for `myte ai`",
|
|
@@ -333,6 +355,13 @@ function printHelp() {
|
|
|
333
355
|
" --target-contact-id Add one client contact ObjectId (repeatable)",
|
|
334
356
|
" --target-contact-ids Comma-separated client contact ObjectIds",
|
|
335
357
|
" --feedback-id <id> Feedback ObjectId for feedback review commands",
|
|
358
|
+
" --request-id <id> Feedback review request ObjectId for submit/revise/review flows",
|
|
359
|
+
" --event-id <id> Feedback board event ObjectId for undo",
|
|
360
|
+
" --version-id <id> Feedback PRD version ObjectId for prd-diff",
|
|
361
|
+
" --compare-to <id> Optional base PRD version ObjectId for prd-diff",
|
|
362
|
+
" --action <value> Feedback review action: approve, reject, request_changes, or cancel",
|
|
363
|
+
" --to-state <value> Target canonical feedback board state for feedback move",
|
|
364
|
+
" --from-state <value> Optional current-state guard for feedback move",
|
|
336
365
|
" --reason <text> Human review reason included in feedback refinement artifacts",
|
|
337
366
|
" --status <value> Mission status target, feedback-sync filter, or feedback review state depending on command",
|
|
338
367
|
" --source <value> Feedback source filter for feedback-sync",
|
|
@@ -370,6 +399,10 @@ function printHelp() {
|
|
|
370
399
|
" myte update-client --subject \"Weekly client update\" --body-file ./updates/week-12.md",
|
|
371
400
|
" myte feedback-sync --json",
|
|
372
401
|
" myte feedback status --feedback-id 507f1f77bcf86cd799439011 --status in_review --reason \"Ready for owner review\"",
|
|
402
|
+
" myte feedback submit --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-edit.yml --json",
|
|
403
|
+
" myte feedback reviews --status open --json",
|
|
404
|
+
" myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
|
|
405
|
+
" myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
|
|
373
406
|
" myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
|
|
374
407
|
" myte feedback apply --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
|
|
375
408
|
" myte suggestions create --file ./suggestions/create.yml",
|
|
@@ -604,6 +637,8 @@ async function fetchJsonWithTimeout(fetchFn, url, options, timeoutMs) {
|
|
|
604
637
|
} catch {
|
|
605
638
|
const err = new Error(`Non-JSON response (${resp.status}): ${text.slice(0, 500)}`);
|
|
606
639
|
err.status = resp.status;
|
|
640
|
+
const retryAfter = resp.headers?.get?.("retry-after");
|
|
641
|
+
if (retryAfter) err.retryAfter = retryAfter;
|
|
607
642
|
throw err;
|
|
608
643
|
}
|
|
609
644
|
return { resp, body };
|
|
@@ -1708,6 +1743,208 @@ async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mod
|
|
|
1708
1743
|
return body.data || {};
|
|
1709
1744
|
}
|
|
1710
1745
|
|
|
1746
|
+
async function fetchFeedbackReviewRequests({ apiBase, key, timeoutMs, status, limit, offset }) {
|
|
1747
|
+
const fetchFn = await getFetch();
|
|
1748
|
+
const url = new URL(`${apiBase}/project-assistant/feedback-review-requests`);
|
|
1749
|
+
if (status) url.searchParams.set("status", String(status));
|
|
1750
|
+
if (limit !== undefined && limit !== null && String(limit).trim()) url.searchParams.set("limit", String(limit));
|
|
1751
|
+
if (offset !== undefined && offset !== null && String(offset).trim()) url.searchParams.set("offset", String(offset));
|
|
1752
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1753
|
+
fetchFn,
|
|
1754
|
+
url.toString(),
|
|
1755
|
+
{
|
|
1756
|
+
method: "GET",
|
|
1757
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
1758
|
+
},
|
|
1759
|
+
timeoutMs
|
|
1760
|
+
);
|
|
1761
|
+
|
|
1762
|
+
if (!resp.ok || body.status !== "success") {
|
|
1763
|
+
const msg = body?.message || `Feedback review requests failed (${resp.status})`;
|
|
1764
|
+
const err = new Error(msg);
|
|
1765
|
+
err.status = resp.status;
|
|
1766
|
+
throw err;
|
|
1767
|
+
}
|
|
1768
|
+
return body.data || {};
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
async function fetchFeedbackReviewRequest({ apiBase, key, timeoutMs, requestId }) {
|
|
1772
|
+
const fetchFn = await getFetch();
|
|
1773
|
+
const url = `${apiBase}/project-assistant/feedback-review-requests/${encodeURIComponent(String(requestId || ""))}`;
|
|
1774
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1775
|
+
fetchFn,
|
|
1776
|
+
url,
|
|
1777
|
+
{
|
|
1778
|
+
method: "GET",
|
|
1779
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
1780
|
+
},
|
|
1781
|
+
timeoutMs
|
|
1782
|
+
);
|
|
1783
|
+
|
|
1784
|
+
if (!resp.ok || body.status !== "success") {
|
|
1785
|
+
const msg = body?.message || `Feedback review request failed (${resp.status})`;
|
|
1786
|
+
const err = new Error(msg);
|
|
1787
|
+
err.status = resp.status;
|
|
1788
|
+
throw err;
|
|
1789
|
+
}
|
|
1790
|
+
return body.data || {};
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
async function fetchFeedbackEvents({ apiBase, key, timeoutMs, feedbackId, limit }) {
|
|
1794
|
+
const fetchFn = await getFetch();
|
|
1795
|
+
const url = new URL(`${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/events`);
|
|
1796
|
+
if (limit !== undefined && limit !== null && String(limit).trim()) url.searchParams.set("limit", String(limit));
|
|
1797
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1798
|
+
fetchFn,
|
|
1799
|
+
url.toString(),
|
|
1800
|
+
{
|
|
1801
|
+
method: "GET",
|
|
1802
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
1803
|
+
},
|
|
1804
|
+
timeoutMs
|
|
1805
|
+
);
|
|
1806
|
+
|
|
1807
|
+
if (!resp.ok || body.status !== "success") {
|
|
1808
|
+
const msg = body?.message || `Feedback events request failed (${resp.status})`;
|
|
1809
|
+
const err = new Error(msg);
|
|
1810
|
+
err.status = resp.status;
|
|
1811
|
+
throw err;
|
|
1812
|
+
}
|
|
1813
|
+
return body.data || {};
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId }) {
|
|
1817
|
+
const fetchFn = await getFetch();
|
|
1818
|
+
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/refinement/requests`;
|
|
1819
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1820
|
+
fetchFn,
|
|
1821
|
+
url,
|
|
1822
|
+
{
|
|
1823
|
+
method: "POST",
|
|
1824
|
+
headers: {
|
|
1825
|
+
"Content-Type": "application/json",
|
|
1826
|
+
Authorization: `Bearer ${key}`,
|
|
1827
|
+
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1828
|
+
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
1829
|
+
},
|
|
1830
|
+
body: JSON.stringify(payload || {}),
|
|
1831
|
+
},
|
|
1832
|
+
timeoutMs
|
|
1833
|
+
);
|
|
1834
|
+
|
|
1835
|
+
if (!resp.ok || body.status !== "success") {
|
|
1836
|
+
const msg = body?.message || `Feedback review submission failed (${resp.status})`;
|
|
1837
|
+
const err = new Error(msg);
|
|
1838
|
+
err.status = resp.status;
|
|
1839
|
+
err.data = body?.data;
|
|
1840
|
+
throw err;
|
|
1841
|
+
}
|
|
1842
|
+
return body.data || {};
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requestId, endpointAction, payload, idempotencyKey, clientSessionId }) {
|
|
1846
|
+
const fetchFn = await getFetch();
|
|
1847
|
+
const url = `${apiBase}/project-assistant/feedback-review-requests/${encodeURIComponent(String(requestId || ""))}/${endpointAction}`;
|
|
1848
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1849
|
+
fetchFn,
|
|
1850
|
+
url,
|
|
1851
|
+
{
|
|
1852
|
+
method: "POST",
|
|
1853
|
+
headers: {
|
|
1854
|
+
"Content-Type": "application/json",
|
|
1855
|
+
Authorization: `Bearer ${key}`,
|
|
1856
|
+
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1857
|
+
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
1858
|
+
},
|
|
1859
|
+
body: JSON.stringify(payload || {}),
|
|
1860
|
+
},
|
|
1861
|
+
timeoutMs
|
|
1862
|
+
);
|
|
1863
|
+
|
|
1864
|
+
if (!resp.ok || body.status !== "success") {
|
|
1865
|
+
const msg = body?.message || `Feedback review ${endpointAction} failed (${resp.status})`;
|
|
1866
|
+
const err = new Error(msg);
|
|
1867
|
+
err.status = resp.status;
|
|
1868
|
+
err.data = body?.data;
|
|
1869
|
+
throw err;
|
|
1870
|
+
}
|
|
1871
|
+
return body.data || {};
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId, endpoint, payload, idempotencyKey, clientSessionId }) {
|
|
1875
|
+
const fetchFn = await getFetch();
|
|
1876
|
+
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/${endpoint}`;
|
|
1877
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1878
|
+
fetchFn,
|
|
1879
|
+
url,
|
|
1880
|
+
{
|
|
1881
|
+
method: "POST",
|
|
1882
|
+
headers: {
|
|
1883
|
+
"Content-Type": "application/json",
|
|
1884
|
+
Authorization: `Bearer ${key}`,
|
|
1885
|
+
"X-Idempotency-Key": String(idempotencyKey || "").trim(),
|
|
1886
|
+
...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
|
|
1887
|
+
},
|
|
1888
|
+
body: JSON.stringify(payload || {}),
|
|
1889
|
+
},
|
|
1890
|
+
timeoutMs
|
|
1891
|
+
);
|
|
1892
|
+
|
|
1893
|
+
if (!resp.ok || body.status !== "success") {
|
|
1894
|
+
const msg = body?.message || `Feedback board mutation failed (${resp.status})`;
|
|
1895
|
+
const err = new Error(msg);
|
|
1896
|
+
err.status = resp.status;
|
|
1897
|
+
err.data = body?.data;
|
|
1898
|
+
throw err;
|
|
1899
|
+
}
|
|
1900
|
+
return body.data || {};
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
async function fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId }) {
|
|
1904
|
+
const fetchFn = await getFetch();
|
|
1905
|
+
const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions`;
|
|
1906
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1907
|
+
fetchFn,
|
|
1908
|
+
url,
|
|
1909
|
+
{
|
|
1910
|
+
method: "GET",
|
|
1911
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
1912
|
+
},
|
|
1913
|
+
timeoutMs
|
|
1914
|
+
);
|
|
1915
|
+
|
|
1916
|
+
if (!resp.ok || body.status !== "success") {
|
|
1917
|
+
const msg = body?.message || `Feedback PRD versions request failed (${resp.status})`;
|
|
1918
|
+
const err = new Error(msg);
|
|
1919
|
+
err.status = resp.status;
|
|
1920
|
+
throw err;
|
|
1921
|
+
}
|
|
1922
|
+
return body.data || {};
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
async function fetchFeedbackPrdVersionDiff({ apiBase, key, timeoutMs, feedbackId, versionId, compareTo }) {
|
|
1926
|
+
const fetchFn = await getFetch();
|
|
1927
|
+
const url = new URL(`${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions/${encodeURIComponent(String(versionId || ""))}/diff`);
|
|
1928
|
+
if (compareTo) url.searchParams.set("compare_to", String(compareTo));
|
|
1929
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
1930
|
+
fetchFn,
|
|
1931
|
+
url.toString(),
|
|
1932
|
+
{
|
|
1933
|
+
method: "GET",
|
|
1934
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
1935
|
+
},
|
|
1936
|
+
timeoutMs
|
|
1937
|
+
);
|
|
1938
|
+
|
|
1939
|
+
if (!resp.ok || body.status !== "success") {
|
|
1940
|
+
const msg = body?.message || `Feedback PRD version diff failed (${resp.status})`;
|
|
1941
|
+
const err = new Error(msg);
|
|
1942
|
+
err.status = resp.status;
|
|
1943
|
+
throw err;
|
|
1944
|
+
}
|
|
1945
|
+
return body.data || {};
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1711
1948
|
async function fetchSuggestionsSyncSnapshot({ apiBase, key, timeoutMs, actorScope = "" }) {
|
|
1712
1949
|
const fetchFn = await getFetch();
|
|
1713
1950
|
const url = new URL(`${apiBase}/project-assistant/suggestions`);
|
|
@@ -1881,6 +2118,10 @@ function resolveRetryAfterMs(err, fallbackMs = 5_000) {
|
|
|
1881
2118
|
return fallbackMs;
|
|
1882
2119
|
}
|
|
1883
2120
|
|
|
2121
|
+
function isTransientQueryStatusError(err) {
|
|
2122
|
+
return [408, 429, 500, 502, 503, 504].includes(Number(err?.status));
|
|
2123
|
+
}
|
|
2124
|
+
|
|
1884
2125
|
async function createAssistantQueryJob({ apiBase, key, payload, timeoutMs, endpoint = "/project-assistant/query" }) {
|
|
1885
2126
|
const fetchFn = await getFetch();
|
|
1886
2127
|
const url = `${apiBase}${endpoint}`;
|
|
@@ -4261,7 +4502,7 @@ async function buildFeedbackDraftForCommand(args, subcommand) {
|
|
|
4261
4502
|
const reviewNote = firstNonEmptyString(args["review-note"], args.reviewNote, args.review_note);
|
|
4262
4503
|
if (reviewNote) changes.review_note = feedbackChange(null, reviewNote);
|
|
4263
4504
|
} else {
|
|
4264
|
-
console.error("Unknown feedback command. Use status, edit, assign, archive,
|
|
4505
|
+
console.error("Unknown feedback draft command. Use status, edit, assign, archive, or refine.");
|
|
4265
4506
|
process.exit(1);
|
|
4266
4507
|
}
|
|
4267
4508
|
|
|
@@ -4375,57 +4616,537 @@ async function runFeedbackValidateOrApply(args, mode) {
|
|
|
4375
4616
|
}
|
|
4376
4617
|
}
|
|
4377
4618
|
|
|
4378
|
-
|
|
4619
|
+
function resolveFeedbackRequestIdArg(args) {
|
|
4620
|
+
return firstNonEmptyString(args["request-id"], args.requestId, args.request_id, args._?.[1]);
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
function resolveFeedbackEventIdArg(args) {
|
|
4624
|
+
return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
|
|
4625
|
+
}
|
|
4626
|
+
|
|
4627
|
+
function resolveFeedbackVersionIdArg(args) {
|
|
4628
|
+
return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4631
|
+
function summarizeFeedbackReviewRequestPayload(data) {
|
|
4632
|
+
const request = data?.request || data || {};
|
|
4633
|
+
return {
|
|
4634
|
+
request_id: request.request_id || request._id || null,
|
|
4635
|
+
feedback_id: request.feedback_id || request.feedback?.feedback_id || null,
|
|
4636
|
+
status: request.status || null,
|
|
4637
|
+
title: request.title || request.feedback?.title || null,
|
|
4638
|
+
can_review: request.can_review === true,
|
|
4639
|
+
can_approve: request.can_approve === true,
|
|
4640
|
+
is_terminal: request.is_terminal === true,
|
|
4641
|
+
stale: request.stale === true,
|
|
4642
|
+
conflicted: request.conflicted === true,
|
|
4643
|
+
warnings: Array.isArray(request.warnings) ? request.warnings : [],
|
|
4644
|
+
};
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
function printFeedbackReviewRequestSummary(data, args) {
|
|
4648
|
+
const summary = summarizeFeedbackReviewRequestPayload(data);
|
|
4649
|
+
if (args.json) {
|
|
4650
|
+
console.log(JSON.stringify(data, null, 2));
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
console.log(`Request: ${summary.request_id || "(unknown)"}`);
|
|
4654
|
+
console.log(`Feedback: ${summary.feedback_id || "(unknown)"}`);
|
|
4655
|
+
console.log(`Status: ${summary.status || "unknown"}`);
|
|
4656
|
+
if (summary.title) console.log(`Title: ${summary.title}`);
|
|
4657
|
+
console.log(`Can approve: ${summary.can_approve ? "yes" : "no"}`);
|
|
4658
|
+
if (summary.stale || summary.conflicted) {
|
|
4659
|
+
console.log(`Conflict: stale=${summary.stale ? "yes" : "no"}, conflicted=${summary.conflicted ? "yes" : "no"}`);
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
|
|
4663
|
+
async function runFeedbackSubmit(args) {
|
|
4379
4664
|
const key = getProjectApiKey();
|
|
4380
4665
|
if (!key) {
|
|
4381
4666
|
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4382
4667
|
process.exit(1);
|
|
4383
4668
|
}
|
|
4384
|
-
const
|
|
4669
|
+
const { absPath, payload } = readFeedbackRefinementArtifact(args);
|
|
4670
|
+
const feedbackId = firstNonEmptyString(args["feedback-id"], args.feedbackId, args.feedback_id, payload.feedback_id);
|
|
4385
4671
|
if (!feedbackId) {
|
|
4386
|
-
console.error("
|
|
4672
|
+
console.error("Feedback refinement artifact is missing feedback_id.");
|
|
4387
4673
|
process.exit(1);
|
|
4388
4674
|
}
|
|
4389
4675
|
const timeoutMs = resolveTimeoutMs(args);
|
|
4390
4676
|
const apiBase = resolveApiBase(args);
|
|
4677
|
+
const clientSessionId = firstNonEmptyString(
|
|
4678
|
+
args["client-session-id"],
|
|
4679
|
+
args.clientSessionId,
|
|
4680
|
+
args.client_session_id,
|
|
4681
|
+
payload.client_session_id
|
|
4682
|
+
);
|
|
4683
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
4684
|
+
args,
|
|
4685
|
+
operation: `feedback_refinement_request_submit:${feedbackId}`,
|
|
4686
|
+
payload,
|
|
4687
|
+
});
|
|
4391
4688
|
|
|
4392
4689
|
let data;
|
|
4393
4690
|
try {
|
|
4394
|
-
data =
|
|
4395
|
-
|
|
4396
|
-
|
|
4691
|
+
data = await postFeedbackReviewSubmission({
|
|
4692
|
+
apiBase,
|
|
4693
|
+
key,
|
|
4694
|
+
timeoutMs,
|
|
4695
|
+
feedbackId,
|
|
4696
|
+
payload,
|
|
4697
|
+
idempotencyKey,
|
|
4698
|
+
clientSessionId,
|
|
4699
|
+
});
|
|
4397
4700
|
} catch (err) {
|
|
4398
|
-
|
|
4701
|
+
if (args.json) {
|
|
4702
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null, artifact_path: absPath }, null, 2));
|
|
4703
|
+
} else {
|
|
4704
|
+
console.error("Feedback submit failed:", err?.message || err);
|
|
4705
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
4706
|
+
}
|
|
4399
4707
|
process.exit(1);
|
|
4400
4708
|
}
|
|
4401
4709
|
|
|
4710
|
+
const output = {
|
|
4711
|
+
ok: true,
|
|
4712
|
+
artifact_path: absPath,
|
|
4713
|
+
feedback_id: data.feedback_id || feedbackId,
|
|
4714
|
+
project_id: data.project_id || null,
|
|
4715
|
+
pending_review_count: data.pending_review_count || null,
|
|
4716
|
+
request: data.request || null,
|
|
4717
|
+
permissions: data.permissions || null,
|
|
4718
|
+
};
|
|
4402
4719
|
if (args.json) {
|
|
4403
|
-
console.log(JSON.stringify(
|
|
4404
|
-
return;
|
|
4405
|
-
}
|
|
4406
|
-
if (mode === "history") {
|
|
4407
|
-
console.log(`Feedback: ${data.feedback_id || feedbackId}`);
|
|
4408
|
-
console.log(`Events: ${data.count || 0}`);
|
|
4409
|
-
for (const event of data.events || []) {
|
|
4410
|
-
console.log(`- ${event.created_at || "unknown"} ${event.action || "refine"} ${event.history_id || ""}`);
|
|
4411
|
-
}
|
|
4720
|
+
console.log(JSON.stringify(output, null, 2));
|
|
4412
4721
|
return;
|
|
4413
4722
|
}
|
|
4414
|
-
|
|
4415
|
-
console.log(`
|
|
4416
|
-
console.log(`
|
|
4417
|
-
console.log(
|
|
4418
|
-
console.log(`Priority: ${feedback.priority || "n/a"}`);
|
|
4419
|
-
console.log(`Snapshot: ${feedback.snapshot_hash || "n/a"}`);
|
|
4723
|
+
console.log(`Feedback: ${output.feedback_id}`);
|
|
4724
|
+
console.log(`Review request: ${output.request?.request_id || output.request?._id || "(unknown)"}`);
|
|
4725
|
+
console.log(`Status: ${output.request?.status || "pending_review"}`);
|
|
4726
|
+
console.log("Live feedback was not mutated. Owner/delegate approval is required.");
|
|
4420
4727
|
}
|
|
4421
4728
|
|
|
4422
|
-
async function
|
|
4423
|
-
const
|
|
4424
|
-
if (
|
|
4425
|
-
|
|
4426
|
-
|
|
4729
|
+
async function runFeedbackRevise(args) {
|
|
4730
|
+
const key = getProjectApiKey();
|
|
4731
|
+
if (!key) {
|
|
4732
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4733
|
+
process.exit(1);
|
|
4427
4734
|
}
|
|
4428
|
-
|
|
4735
|
+
const requestId = resolveFeedbackRequestIdArg(args);
|
|
4736
|
+
if (!requestId) {
|
|
4737
|
+
console.error("Missing --request-id.");
|
|
4738
|
+
process.exit(1);
|
|
4739
|
+
}
|
|
4740
|
+
const { absPath, payload } = readFeedbackRefinementArtifact(args);
|
|
4741
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
4742
|
+
const apiBase = resolveApiBase(args);
|
|
4743
|
+
const clientSessionId = firstNonEmptyString(
|
|
4744
|
+
args["client-session-id"],
|
|
4745
|
+
args.clientSessionId,
|
|
4746
|
+
args.client_session_id,
|
|
4747
|
+
payload.client_session_id
|
|
4748
|
+
);
|
|
4749
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
4750
|
+
args,
|
|
4751
|
+
operation: `feedback_refinement_request_revise:${requestId}`,
|
|
4752
|
+
payload,
|
|
4753
|
+
});
|
|
4754
|
+
|
|
4755
|
+
let data;
|
|
4756
|
+
try {
|
|
4757
|
+
data = await postFeedbackReviewRequestMutation({
|
|
4758
|
+
apiBase,
|
|
4759
|
+
key,
|
|
4760
|
+
timeoutMs,
|
|
4761
|
+
requestId,
|
|
4762
|
+
endpointAction: "revise",
|
|
4763
|
+
payload,
|
|
4764
|
+
idempotencyKey,
|
|
4765
|
+
clientSessionId,
|
|
4766
|
+
});
|
|
4767
|
+
} catch (err) {
|
|
4768
|
+
if (args.json) {
|
|
4769
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null, artifact_path: absPath }, null, 2));
|
|
4770
|
+
} else {
|
|
4771
|
+
console.error("Feedback revise failed:", err?.message || err);
|
|
4772
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
4773
|
+
}
|
|
4774
|
+
process.exit(1);
|
|
4775
|
+
}
|
|
4776
|
+
|
|
4777
|
+
const output = { ok: true, artifact_path: absPath, ...data };
|
|
4778
|
+
if (args.json) {
|
|
4779
|
+
console.log(JSON.stringify(output, null, 2));
|
|
4780
|
+
return;
|
|
4781
|
+
}
|
|
4782
|
+
printFeedbackReviewRequestSummary(data, args);
|
|
4783
|
+
}
|
|
4784
|
+
|
|
4785
|
+
async function runFeedbackReviews(args) {
|
|
4786
|
+
const key = getProjectApiKey();
|
|
4787
|
+
if (!key) {
|
|
4788
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4789
|
+
process.exit(1);
|
|
4790
|
+
}
|
|
4791
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
4792
|
+
const apiBase = resolveApiBase(args);
|
|
4793
|
+
const requestId = firstNonEmptyString(args["request-id"], args.requestId, args.request_id);
|
|
4794
|
+
|
|
4795
|
+
let data;
|
|
4796
|
+
try {
|
|
4797
|
+
data = requestId
|
|
4798
|
+
? await fetchFeedbackReviewRequest({ apiBase, key, timeoutMs, requestId })
|
|
4799
|
+
: await fetchFeedbackReviewRequests({
|
|
4800
|
+
apiBase,
|
|
4801
|
+
key,
|
|
4802
|
+
timeoutMs,
|
|
4803
|
+
status: firstNonEmptyString(args.status) || "open",
|
|
4804
|
+
limit: firstNonEmptyString(args.limit) || undefined,
|
|
4805
|
+
offset: firstNonEmptyString(args.offset) || undefined,
|
|
4806
|
+
});
|
|
4807
|
+
} catch (err) {
|
|
4808
|
+
console.error("Feedback reviews failed:", err?.message || err);
|
|
4809
|
+
process.exit(1);
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
if (args.json) {
|
|
4813
|
+
console.log(JSON.stringify(data, null, 2));
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
if (requestId) {
|
|
4817
|
+
printFeedbackReviewRequestSummary(data, args);
|
|
4818
|
+
return;
|
|
4819
|
+
}
|
|
4820
|
+
const requests = Array.isArray(data.requests) ? data.requests : [];
|
|
4821
|
+
console.log(`Review requests: ${requests.length}/${data.total ?? requests.length}`);
|
|
4822
|
+
for (const requestItem of requests) {
|
|
4823
|
+
const summary = summarizeFeedbackReviewRequestPayload(requestItem);
|
|
4824
|
+
console.log(`- ${summary.request_id || "(unknown)"} ${summary.status || "unknown"} feedback=${summary.feedback_id || "(unknown)"} title=${summary.title || "(untitled)"}`);
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
|
|
4828
|
+
function buildFeedbackReviewDecisionPayload(args, action) {
|
|
4829
|
+
const filePath = firstNonEmptyString(args.file);
|
|
4830
|
+
let payload = {};
|
|
4831
|
+
if (filePath) {
|
|
4832
|
+
const fromFile = readStructuredPayloadFile(filePath, "Feedback review");
|
|
4833
|
+
payload = Array.isArray(fromFile?.items) ? fromFile.items[0] || {} : fromFile;
|
|
4834
|
+
}
|
|
4835
|
+
if (!isPlainObject(payload)) {
|
|
4836
|
+
throw new Error("Feedback review payload must be an object.");
|
|
4837
|
+
}
|
|
4838
|
+
const nextPayload = { ...payload };
|
|
4839
|
+
const reason = firstNonEmptyString(args.reason, args["review-note"], args.reviewNote, args.review_note);
|
|
4840
|
+
if (reason && !nextPayload.reason && !nextPayload.review_reason) {
|
|
4841
|
+
nextPayload.reason = reason;
|
|
4842
|
+
}
|
|
4843
|
+
const finalFile = firstNonEmptyString(args["final-file"], args.finalFile, args.final_file);
|
|
4844
|
+
if (finalFile) {
|
|
4845
|
+
nextPayload.final_change_set = readStructuredPayloadFile(finalFile, "Feedback final change set");
|
|
4846
|
+
}
|
|
4847
|
+
if (action === "approve" || action === "reject") {
|
|
4848
|
+
nextPayload.decision = action;
|
|
4849
|
+
}
|
|
4850
|
+
return nextPayload;
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
async function runFeedbackReviewDecision(args) {
|
|
4854
|
+
const key = getProjectApiKey();
|
|
4855
|
+
if (!key) {
|
|
4856
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4857
|
+
process.exit(1);
|
|
4858
|
+
}
|
|
4859
|
+
const requestId = resolveFeedbackRequestIdArg(args);
|
|
4860
|
+
if (!requestId) {
|
|
4861
|
+
console.error("Missing --request-id.");
|
|
4862
|
+
process.exit(1);
|
|
4863
|
+
}
|
|
4864
|
+
const action = String(firstNonEmptyString(args.action, args.decision, args["review-action"], args.reviewAction, args.review_action, args._?.[1]) || "").trim().toLowerCase().replace(/-/g, "_");
|
|
4865
|
+
if (!["approve", "reject", "request_changes", "cancel"].includes(action)) {
|
|
4866
|
+
console.error("Missing or invalid review action. Use approve, reject, request_changes, or cancel.");
|
|
4867
|
+
process.exit(1);
|
|
4868
|
+
}
|
|
4869
|
+
|
|
4870
|
+
let payload;
|
|
4871
|
+
try {
|
|
4872
|
+
payload = buildFeedbackReviewDecisionPayload(args, action);
|
|
4873
|
+
} catch (err) {
|
|
4874
|
+
console.error(err?.message || err);
|
|
4875
|
+
process.exit(1);
|
|
4876
|
+
}
|
|
4877
|
+
const endpointAction = action === "request_changes" ? "request-changes" : action === "cancel" ? "cancel" : "review";
|
|
4878
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
4879
|
+
const apiBase = resolveApiBase(args);
|
|
4880
|
+
const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id, payload.client_session_id);
|
|
4881
|
+
const operationAction = action === "request_changes" ? "request_changes" : action;
|
|
4882
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
4883
|
+
args,
|
|
4884
|
+
operation: `feedback_review_request_${operationAction}:${requestId}`,
|
|
4885
|
+
payload,
|
|
4886
|
+
});
|
|
4887
|
+
|
|
4888
|
+
let data;
|
|
4889
|
+
try {
|
|
4890
|
+
data = await postFeedbackReviewRequestMutation({
|
|
4891
|
+
apiBase,
|
|
4892
|
+
key,
|
|
4893
|
+
timeoutMs,
|
|
4894
|
+
requestId,
|
|
4895
|
+
endpointAction,
|
|
4896
|
+
payload,
|
|
4897
|
+
idempotencyKey,
|
|
4898
|
+
clientSessionId,
|
|
4899
|
+
});
|
|
4900
|
+
} catch (err) {
|
|
4901
|
+
if (args.json) {
|
|
4902
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
|
|
4903
|
+
} else {
|
|
4904
|
+
console.error(`Feedback review ${action} failed:`, err?.message || err);
|
|
4905
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
4906
|
+
}
|
|
4907
|
+
process.exit(1);
|
|
4908
|
+
}
|
|
4909
|
+
|
|
4910
|
+
if (args.json) {
|
|
4911
|
+
console.log(JSON.stringify({ ok: true, action, ...data }, null, 2));
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
printFeedbackReviewRequestSummary(data, args);
|
|
4915
|
+
}
|
|
4916
|
+
|
|
4917
|
+
async function runFeedbackMove(args) {
|
|
4918
|
+
const key = getProjectApiKey();
|
|
4919
|
+
if (!key) {
|
|
4920
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4921
|
+
process.exit(1);
|
|
4922
|
+
}
|
|
4923
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
4924
|
+
if (!feedbackId) {
|
|
4925
|
+
console.error("Missing --feedback-id.");
|
|
4926
|
+
process.exit(1);
|
|
4927
|
+
}
|
|
4928
|
+
const toState = firstNonEmptyString(args["to-state"], args.toState, args.to_state, args.status, args._?.[1]);
|
|
4929
|
+
if (!toState) {
|
|
4930
|
+
console.error("Missing --to-state for feedback move.");
|
|
4931
|
+
process.exit(1);
|
|
4932
|
+
}
|
|
4933
|
+
const payload = {
|
|
4934
|
+
to_state: toState,
|
|
4935
|
+
from_state: firstNonEmptyString(args["from-state"], args.fromState, args.from_state) || undefined,
|
|
4936
|
+
reason: firstNonEmptyString(args.reason) || undefined,
|
|
4937
|
+
};
|
|
4938
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
4939
|
+
const apiBase = resolveApiBase(args);
|
|
4940
|
+
const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
|
|
4941
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
4942
|
+
args,
|
|
4943
|
+
operation: `feedback_board_move:${feedbackId}`,
|
|
4944
|
+
payload,
|
|
4945
|
+
});
|
|
4946
|
+
|
|
4947
|
+
let data;
|
|
4948
|
+
try {
|
|
4949
|
+
data = await postFeedbackBoardMutation({
|
|
4950
|
+
apiBase,
|
|
4951
|
+
key,
|
|
4952
|
+
timeoutMs,
|
|
4953
|
+
feedbackId,
|
|
4954
|
+
endpoint: "board-move",
|
|
4955
|
+
payload,
|
|
4956
|
+
idempotencyKey,
|
|
4957
|
+
clientSessionId,
|
|
4958
|
+
});
|
|
4959
|
+
} catch (err) {
|
|
4960
|
+
if (args.json) {
|
|
4961
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
|
|
4962
|
+
} else {
|
|
4963
|
+
console.error("Feedback move failed:", err?.message || err);
|
|
4964
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
4965
|
+
}
|
|
4966
|
+
process.exit(1);
|
|
4967
|
+
}
|
|
4968
|
+
|
|
4969
|
+
if (args.json) {
|
|
4970
|
+
console.log(JSON.stringify({ ok: true, ...data }, null, 2));
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4973
|
+
console.log(`Feedback: ${data.feedback_id || feedbackId}`);
|
|
4974
|
+
console.log(`State: ${data.feedback_state || toState}`);
|
|
4975
|
+
if (data.event?.event_id) console.log(`Event: ${data.event.event_id}`);
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
async function runFeedbackUndo(args) {
|
|
4979
|
+
const key = getProjectApiKey();
|
|
4980
|
+
if (!key) {
|
|
4981
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
4982
|
+
process.exit(1);
|
|
4983
|
+
}
|
|
4984
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
4985
|
+
const eventId = resolveFeedbackEventIdArg(args);
|
|
4986
|
+
if (!feedbackId) {
|
|
4987
|
+
console.error("Missing --feedback-id.");
|
|
4988
|
+
process.exit(1);
|
|
4989
|
+
}
|
|
4990
|
+
if (!eventId) {
|
|
4991
|
+
console.error("Missing --event-id.");
|
|
4992
|
+
process.exit(1);
|
|
4993
|
+
}
|
|
4994
|
+
const payload = { reason: firstNonEmptyString(args.reason) || undefined };
|
|
4995
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
4996
|
+
const apiBase = resolveApiBase(args);
|
|
4997
|
+
const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
|
|
4998
|
+
const idempotencyKey = resolveProjectMutationIdempotencyKey({
|
|
4999
|
+
args,
|
|
5000
|
+
operation: `feedback_event_undo:${feedbackId}:${eventId}`,
|
|
5001
|
+
payload,
|
|
5002
|
+
});
|
|
5003
|
+
|
|
5004
|
+
let data;
|
|
5005
|
+
try {
|
|
5006
|
+
data = await postFeedbackBoardMutation({
|
|
5007
|
+
apiBase,
|
|
5008
|
+
key,
|
|
5009
|
+
timeoutMs,
|
|
5010
|
+
feedbackId,
|
|
5011
|
+
endpoint: `events/${encodeURIComponent(String(eventId))}/undo`,
|
|
5012
|
+
payload,
|
|
5013
|
+
idempotencyKey,
|
|
5014
|
+
clientSessionId,
|
|
5015
|
+
});
|
|
5016
|
+
} catch (err) {
|
|
5017
|
+
if (args.json) {
|
|
5018
|
+
console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
|
|
5019
|
+
} else {
|
|
5020
|
+
console.error("Feedback undo failed:", err?.message || err);
|
|
5021
|
+
if (err?.data) console.error(JSON.stringify(err.data, null, 2));
|
|
5022
|
+
}
|
|
5023
|
+
process.exit(1);
|
|
5024
|
+
}
|
|
5025
|
+
|
|
5026
|
+
if (args.json) {
|
|
5027
|
+
console.log(JSON.stringify({ ok: true, ...data }, null, 2));
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
5030
|
+
console.log(`Feedback: ${data.feedback_id || feedbackId}`);
|
|
5031
|
+
console.log(`State: ${data.feedback_state || "unknown"}`);
|
|
5032
|
+
if (data.reverted_event_id) console.log(`Reverted: ${data.reverted_event_id}`);
|
|
5033
|
+
}
|
|
5034
|
+
|
|
5035
|
+
async function runFeedbackPrdVersions(args) {
|
|
5036
|
+
const key = getProjectApiKey();
|
|
5037
|
+
if (!key) {
|
|
5038
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
5039
|
+
process.exit(1);
|
|
5040
|
+
}
|
|
5041
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
5042
|
+
if (!feedbackId) {
|
|
5043
|
+
console.error("Missing --feedback-id.");
|
|
5044
|
+
process.exit(1);
|
|
5045
|
+
}
|
|
5046
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
5047
|
+
const apiBase = resolveApiBase(args);
|
|
5048
|
+
let data;
|
|
5049
|
+
try {
|
|
5050
|
+
data = await fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId });
|
|
5051
|
+
} catch (err) {
|
|
5052
|
+
console.error("Feedback PRD versions failed:", err?.message || err);
|
|
5053
|
+
process.exit(1);
|
|
5054
|
+
}
|
|
5055
|
+
if (args.json) {
|
|
5056
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
const versions = Array.isArray(data.versions) ? data.versions : [];
|
|
5060
|
+
console.log(`PRD versions: ${versions.length}`);
|
|
5061
|
+
for (const version of versions) {
|
|
5062
|
+
console.log(`- ${version.version_id || version._id || "(unknown)"} v${version.version_number || "?"} active=${String(version._id || version.version_id) === String(data.active_prd_version_id) ? "yes" : "no"}`);
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
|
|
5066
|
+
async function runFeedbackPrdDiff(args) {
|
|
5067
|
+
const key = getProjectApiKey();
|
|
5068
|
+
if (!key) {
|
|
5069
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
5070
|
+
process.exit(1);
|
|
5071
|
+
}
|
|
5072
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
5073
|
+
const versionId = resolveFeedbackVersionIdArg(args);
|
|
5074
|
+
if (!feedbackId) {
|
|
5075
|
+
console.error("Missing --feedback-id.");
|
|
5076
|
+
process.exit(1);
|
|
5077
|
+
}
|
|
5078
|
+
if (!versionId) {
|
|
5079
|
+
console.error("Missing --version-id.");
|
|
5080
|
+
process.exit(1);
|
|
5081
|
+
}
|
|
5082
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
5083
|
+
const apiBase = resolveApiBase(args);
|
|
5084
|
+
const compareTo = firstNonEmptyString(args["compare-to"], args.compareTo, args.compare_to);
|
|
5085
|
+
let data;
|
|
5086
|
+
try {
|
|
5087
|
+
data = await fetchFeedbackPrdVersionDiff({ apiBase, key, timeoutMs, feedbackId, versionId, compareTo });
|
|
5088
|
+
} catch (err) {
|
|
5089
|
+
console.error("Feedback PRD diff failed:", err?.message || err);
|
|
5090
|
+
process.exit(1);
|
|
5091
|
+
}
|
|
5092
|
+
if (args.json) {
|
|
5093
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5094
|
+
return;
|
|
5095
|
+
}
|
|
5096
|
+
console.log(data.diff || "");
|
|
5097
|
+
}
|
|
5098
|
+
|
|
5099
|
+
async function runFeedbackGetOrHistory(args, mode) {
|
|
5100
|
+
const key = getProjectApiKey();
|
|
5101
|
+
if (!key) {
|
|
5102
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
5103
|
+
process.exit(1);
|
|
5104
|
+
}
|
|
5105
|
+
const feedbackId = resolveFeedbackIdArg(args);
|
|
5106
|
+
if (!feedbackId) {
|
|
5107
|
+
console.error("Missing --feedback-id.");
|
|
5108
|
+
process.exit(1);
|
|
5109
|
+
}
|
|
5110
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
5111
|
+
const apiBase = resolveApiBase(args);
|
|
5112
|
+
|
|
5113
|
+
let data;
|
|
5114
|
+
try {
|
|
5115
|
+
data = mode === "history"
|
|
5116
|
+
? await fetchFeedbackEvents({ apiBase, key, timeoutMs, feedbackId, limit: firstNonEmptyString(args.limit) || undefined })
|
|
5117
|
+
: await fetchFeedbackReview({ apiBase, key, timeoutMs, feedbackId });
|
|
5118
|
+
} catch (err) {
|
|
5119
|
+
console.error(`Feedback ${mode} failed:`, err?.message || err);
|
|
5120
|
+
process.exit(1);
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
if (args.json) {
|
|
5124
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5125
|
+
return;
|
|
5126
|
+
}
|
|
5127
|
+
if (mode === "history") {
|
|
5128
|
+
console.log(`Feedback: ${data.feedback_id || feedbackId}`);
|
|
5129
|
+
console.log(`Events: ${data.count || 0}`);
|
|
5130
|
+
for (const event of data.events || []) {
|
|
5131
|
+
console.log(`- ${event.created_at || "unknown"} ${event.action || "refine"} ${event.history_id || ""}`);
|
|
5132
|
+
}
|
|
5133
|
+
return;
|
|
5134
|
+
}
|
|
5135
|
+
const feedback = data.feedback || {};
|
|
5136
|
+
console.log(`Feedback: ${feedback.feedback_id || feedbackId}`);
|
|
5137
|
+
console.log(`Title: ${feedback.title || "(untitled)"}`);
|
|
5138
|
+
console.log(`Status: ${feedback.feedback_state || feedback.status || "unknown"} (${feedback.status || "legacy unknown"})`);
|
|
5139
|
+
console.log(`Priority: ${feedback.priority || "n/a"}`);
|
|
5140
|
+
console.log(`Snapshot: ${feedback.snapshot_hash || "n/a"}`);
|
|
5141
|
+
}
|
|
5142
|
+
|
|
5143
|
+
async function runFeedback(args) {
|
|
5144
|
+
const subcommand = firstNonEmptyString(args._?.[0]) || "help";
|
|
5145
|
+
if (subcommand === "help") {
|
|
5146
|
+
printHelp();
|
|
5147
|
+
return;
|
|
5148
|
+
}
|
|
5149
|
+
if (["status", "edit", "assign", "archive", "refine"].includes(subcommand)) {
|
|
4429
5150
|
await buildFeedbackDraftForCommand(args, subcommand);
|
|
4430
5151
|
return;
|
|
4431
5152
|
}
|
|
@@ -4433,11 +5154,43 @@ async function runFeedback(args) {
|
|
|
4433
5154
|
await runFeedbackValidateOrApply(args, subcommand);
|
|
4434
5155
|
return;
|
|
4435
5156
|
}
|
|
5157
|
+
if (subcommand === "submit") {
|
|
5158
|
+
await runFeedbackSubmit(args);
|
|
5159
|
+
return;
|
|
5160
|
+
}
|
|
5161
|
+
if (subcommand === "revise") {
|
|
5162
|
+
await runFeedbackRevise(args);
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5165
|
+
if (subcommand === "reviews" || subcommand === "requests") {
|
|
5166
|
+
await runFeedbackReviews(args);
|
|
5167
|
+
return;
|
|
5168
|
+
}
|
|
5169
|
+
if (subcommand === "review") {
|
|
5170
|
+
await runFeedbackReviewDecision(args);
|
|
5171
|
+
return;
|
|
5172
|
+
}
|
|
5173
|
+
if (subcommand === "move") {
|
|
5174
|
+
await runFeedbackMove(args);
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
if (subcommand === "undo") {
|
|
5178
|
+
await runFeedbackUndo(args);
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5181
|
+
if (subcommand === "prd-versions") {
|
|
5182
|
+
await runFeedbackPrdVersions(args);
|
|
5183
|
+
return;
|
|
5184
|
+
}
|
|
5185
|
+
if (subcommand === "prd-diff") {
|
|
5186
|
+
await runFeedbackPrdDiff(args);
|
|
5187
|
+
return;
|
|
5188
|
+
}
|
|
4436
5189
|
if (subcommand === "get" || subcommand === "history") {
|
|
4437
5190
|
await runFeedbackGetOrHistory(args, subcommand);
|
|
4438
5191
|
return;
|
|
4439
5192
|
}
|
|
4440
|
-
console.error("Unknown feedback command. Use status, edit, assign, archive, get, history, validate, or apply.");
|
|
5193
|
+
console.error("Unknown feedback command. Use status, edit, assign, archive, submit, revise, reviews, review, move, undo, prd-versions, prd-diff, get, history, validate, or apply.");
|
|
4441
5194
|
process.exit(1);
|
|
4442
5195
|
}
|
|
4443
5196
|
|
|
@@ -5002,8 +5755,9 @@ async function runQuery(args) {
|
|
|
5002
5755
|
const timeoutMs = Number.isFinite(timeoutParsed) ? timeoutParsed : 300_000;
|
|
5003
5756
|
|
|
5004
5757
|
const charLimitRaw = args["diff-limit"] || args.diffLimit || args.diff_limit;
|
|
5005
|
-
const charLimitParsed =
|
|
5006
|
-
|
|
5758
|
+
const charLimitParsed =
|
|
5759
|
+
charLimitRaw !== undefined && charLimitRaw !== null ? Number(charLimitRaw) : DEFAULT_DIFF_LIMIT_CHARS;
|
|
5760
|
+
const diffLimit = Number.isFinite(charLimitParsed) ? charLimitParsed : DEFAULT_DIFF_LIMIT_CHARS;
|
|
5007
5761
|
|
|
5008
5762
|
const baseRaw = args["base-url"] || args.baseUrl || args.base_url || process.env.MYTE_API_BASE || DEFAULT_API_BASE;
|
|
5009
5763
|
const apiBase = normalizeApiBase(baseRaw);
|
|
@@ -5077,13 +5831,19 @@ async function runQuery(args) {
|
|
|
5077
5831
|
const pollTimeoutMs = Math.max(timeoutMs, 900_000);
|
|
5078
5832
|
const startedAt = Date.now();
|
|
5079
5833
|
let finalStatus = null;
|
|
5834
|
+
let pollDelayMs = 2_000;
|
|
5080
5835
|
do {
|
|
5081
|
-
await sleep(
|
|
5836
|
+
await sleep(pollDelayMs);
|
|
5082
5837
|
try {
|
|
5083
5838
|
finalStatus = await fetchAssistantQueryJobStatus({ apiBase, key, timeoutMs, jobId });
|
|
5839
|
+
pollDelayMs = Math.min(10_000, Math.ceil(pollDelayMs * 1.25));
|
|
5084
5840
|
} catch (err) {
|
|
5085
|
-
if (
|
|
5086
|
-
|
|
5841
|
+
if (isTransientQueryStatusError(err)) {
|
|
5842
|
+
const fallbackMs =
|
|
5843
|
+
Number(err?.status) === 429 ? 5_000 : Math.min(30_000, Math.max(5_000, pollDelayMs * 2));
|
|
5844
|
+
const waitMs = resolveRetryAfterMs(err, fallbackMs);
|
|
5845
|
+
await sleep(waitMs);
|
|
5846
|
+
pollDelayMs = Math.min(30_000, Math.max(pollDelayMs, waitMs));
|
|
5087
5847
|
continue;
|
|
5088
5848
|
}
|
|
5089
5849
|
throw err;
|