@mytegroupinc/myte-core 0.0.22 → 0.0.24

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.
Files changed (3) hide show
  1. package/README.md +68 -1
  2. package/cli.js +800 -32
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,6 +19,73 @@ 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, while `feedback validate|apply` sends those artifacts to the backend so business rules stay server-side.
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.
27
+
28
+ ## Feedback Action Map
29
+
30
+ | Command | What It Does | Governance |
31
+ | --- | --- | --- |
32
+ | `feedback-sync` | Pulls feedback metadata, comments, and PRD context into `MyteCommandCenter`. | Read-only project-key API call. |
33
+ | `feedback get` | Reads one feedback item's current server snapshot and `snapshot_hash`. | Read-only; backend checks project access. |
34
+ | `feedback status` | Creates a local YAML proposal to change canonical lifecycle state. | No live mutation until `submit` and owner/delegate review, except owner-direct `apply`. |
35
+ | `feedback edit` / `feedback refine` | Creates a local YAML proposal for title, description/body, priority, due date, tags, or notes. | Content changes go through owner-review membrane. |
36
+ | `feedback assign` | Creates a local YAML proposal to change assignee. | Goes through review unless applied through owner-direct path. |
37
+ | `feedback archive` | Creates a local YAML proposal to archive. | Archive is governed by backend owner/delegate capability. |
38
+ | `feedback submit` | Sends a local proposal artifact to the backend as a review request. | Does not mutate live feedback before approval. |
39
+ | `feedback revise` | Resubmits the original submitter's request after `request_changes`. | Only valid for the original submitter and `needs_changes` requests. |
40
+ | `feedback reviews` | Lists review requests or fetches one request by `--request-id`. | Read-only; response includes backend permissions. |
41
+ | `feedback review` | Approves, rejects, requests changes, or cancels a review request. | Backend restricts approval/review decisions to Project Owner or elevated delegate for Feedback scope. |
42
+ | `feedback move` | Moves a card across allowed board states directly with an audit event. | Safe states are direct; governed states such as archive/reject/deploy remain backend-authorized. |
43
+ | `feedback undo` | Reverses an audited board event when there is no conflict. | Backend validates event ownership/project scope and conflict state. |
44
+ | `feedback prd-versions` | Lists retained PRD baselines/revisions for a feedback item. | Read-only; old S3 objects are retained by reference. |
45
+ | `feedback prd-diff` | Fetches backend-generated text diff for PRD versions. | Read-only; backend controls version access. |
46
+ | `feedback history` | Lists audited feedback board/refinement events. | Read-only; backend checks project access. |
47
+ | `feedback validate` | Sends an artifact to backend validation without mutation. | Useful before submit or owner-direct apply. |
48
+ | `feedback apply` | Applies an artifact through the owner-direct/emergency path. | Not the normal collaborator flow; normal flow is `submit` -> `review`. |
49
+
50
+ ## Direct Project API Surface
51
+
52
+ All routes use `Authorization: Bearer <MYTE_API_KEY>`. The CLI adds idempotency and client-session headers on mutation routes.
53
+
54
+ Read/sync routes:
55
+
56
+ - `GET /api/project-assistant/config`
57
+ - `GET /api/project-assistant/bootstrap`
58
+ - `GET /api/project-assistant/qaqc-sync`
59
+ - `GET /api/project-assistant/feedback-sync`
60
+ - `GET /api/project-assistant/feedback/<feedback_id>`
61
+ - `GET /api/project-assistant/feedback/<feedback_id>/refinement/history`
62
+ - `GET /api/project-assistant/feedback-review-requests`
63
+ - `GET /api/project-assistant/feedback-review-requests/<request_id>`
64
+ - `GET /api/project-assistant/feedback/<feedback_id>/events`
65
+ - `GET /api/project-assistant/feedback/<feedback_id>/prd/versions`
66
+ - `GET /api/project-assistant/feedback/<feedback_id>/prd/versions/<version_id>/diff`
67
+ - `GET /api/project-assistant/suggestions`
68
+ - `GET /api/project-assistant/run-qaqc/<batch_id>`
69
+
70
+ Mutation routes:
71
+
72
+ - `POST /api/project-assistant/query`
73
+ - `POST /api/project-assistant/run-qaqc`
74
+ - `POST /api/project-assistant/mission-status-update`
75
+ - `POST /api/project-assistant/project-comment`
76
+ - `POST /api/project-assistant/update-owner`
77
+ - `POST /api/project-assistant/client-update-drafts`
78
+ - `POST /api/project-assistant/create-prd`
79
+ - `POST /api/project-assistant/create-prds`
80
+ - `POST /api/project-assistant/feedback/<feedback_id>/refinement/validate`
81
+ - `POST /api/project-assistant/feedback/<feedback_id>/refinement/requests`
82
+ - `POST /api/project-assistant/feedback-review-requests/<request_id>/revise`
83
+ - `POST /api/project-assistant/feedback/<feedback_id>/refinement/apply`
84
+ - `POST /api/project-assistant/feedback-review-requests/<request_id>/review`
85
+ - `POST /api/project-assistant/feedback-review-requests/<request_id>/request-changes`
86
+ - `POST /api/project-assistant/feedback-review-requests/<request_id>/cancel`
87
+ - `POST /api/project-assistant/feedback/<feedback_id>/board-move`
88
+ - `POST /api/project-assistant/feedback/<feedback_id>/events/<event_id>/undo`
89
+ - `POST /api/project-assistant/suggestions`
90
+ - `POST /api/project-assistant/suggestions/revise`
91
+ - `POST /api/project-assistant/suggestions/review`
package/cli.js CHANGED
@@ -144,6 +144,14 @@ function parseArgs(argv) {
144
144
  "status",
145
145
  "source",
146
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",
147
155
  "reason",
148
156
  "review-action",
149
157
  "idempotency-key",
@@ -152,6 +160,7 @@ function parseArgs(argv) {
152
160
  "due-date",
153
161
  "priority",
154
162
  "review-note",
163
+ "final-file",
155
164
  "tags",
156
165
  "tag",
157
166
  "mission-ids",
@@ -225,8 +234,20 @@ function printHelp() {
225
234
  " myte update-owner --subject \"<text>\" [--body-markdown \"...\"] [--body-file ./update.md] [--json]",
226
235
  " myte update-client --subject \"<text>\" [--body-markdown \"...\"] [--body-file ./update.md] [--target-contact-ids <id1,id2>] [--json]",
227
236
  " myte feedback-sync [--status <value>] [--source <value>] [--with-prd-text|--no-with-prd-text] [--output-dir ./MyteCommandCenter] [--json]",
237
+ " myte feedback get --feedback-id <id> [--json]",
238
+ " myte feedback history --feedback-id <id> [--limit 50] [--json]",
228
239
  " myte feedback status --feedback-id <id> --status todo|in_progress|in_review|completed|deployed|rejected|archived --reason \"...\"",
229
240
  " myte feedback edit --feedback-id <id> [--title \"...\"] [--feedback-text \"...\"] [--priority High] [--reason \"...\"]",
241
+ " myte feedback assign --feedback-id <id> --user-id <id> [--reason \"...\"]",
242
+ " myte feedback archive --feedback-id <id> --reason \"...\"",
243
+ " myte feedback submit --file ./MyteCommandCenter/reviews/feedback/<id>-edit.yml [--json]",
244
+ " myte feedback revise --request-id <id> --file ./MyteCommandCenter/reviews/feedback/<id>-edit.yml [--json]",
245
+ " myte feedback reviews [--status open|terminal|all] [--request-id <id>] [--json]",
246
+ " myte feedback review --request-id <id> --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
247
+ " myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
248
+ " myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
249
+ " myte feedback prd-versions --feedback-id <id> [--json]",
250
+ " myte feedback prd-diff --feedback-id <id> --version-id <id> [--compare-to <id>] [--json]",
230
251
  " myte feedback validate --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
231
252
  " myte feedback apply --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
232
253
  " myte create-prd <file.md> [more.md ...] [--json] [--title \"...\"] [--description \"...\"]",
@@ -309,10 +330,32 @@ function printHelp() {
309
330
  "",
310
331
  "feedback review contract:",
311
332
  " - Draft commands write review artifacts under MyteCommandCenter/reviews/feedback/*.yml for local IDE diff review",
312
- " - validate/apply send those artifacts to /api/project-assistant/feedback/<id>/refinement/*",
333
+ " - status/edit/assign/archive/refine create local proposals only; they do not mutate live feedback by themselves",
334
+ " - submit/revise route artifacts through the owner-review membrane before live feedback mutation",
335
+ " - reviews/review expose owner/delegate approval, rejection, request-changes, and cancel decisions",
336
+ " - move/undo operate safe board states directly; governed states remain backend-authorized",
337
+ " - get/history read current feedback snapshots and audited event history through the project-key API",
338
+ " - prd-versions/prd-diff expose retained PRD versions and backend-generated text diffs",
339
+ " - validate/apply send artifacts to /api/project-assistant/feedback/<id>/refinement/* for validation or owner-direct apply",
313
340
  " - The backend owns authorization, stale snapshot checks, allowed field/transition rules, and history",
314
341
  " - apply is idempotent and does not rewrite local feedback.yml; run feedback-sync after apply to refresh local state",
315
342
  "",
343
+ "feedback action map:",
344
+ " - feedback-sync: pull board/PRD context into MyteCommandCenter for IDE work",
345
+ " - get: read one feedback item's current server snapshot and snapshot_hash",
346
+ " - status/edit/assign/archive/refine: create reviewable local YAML artifacts; no live mutation",
347
+ " - submit: submit a local artifact as a pending owner-review request",
348
+ " - revise: resubmit the original submitter's request after request_changes",
349
+ " - reviews: list open/terminal/all requests or inspect one request",
350
+ " - review: approve, reject, request_changes, or cancel; backend restricts this to owner/delegate capability",
351
+ " - move: direct audited board movement for allowed states; governed transitions remain backend-authorized",
352
+ " - undo: reverse an audited board event when conflict checks allow it",
353
+ " - prd-versions: list retained PRD baselines/revisions for a feedback item",
354
+ " - prd-diff: fetch backend-generated text diff between PRD versions",
355
+ " - history: list board/refinement events for audit review",
356
+ " - validate: server-side validation of an artifact without mutation",
357
+ " - apply: owner-direct/emergency apply path; normal collaborative flow is submit -> review",
358
+ "",
316
359
  "Options:",
317
360
  " --with-diff Include deterministic git diffs (project-scoped; fails fast if no project repos are configured or resolved)",
318
361
  " --diff-limit <chars> Truncate diff context to N chars (default: 500000)",
@@ -334,6 +377,13 @@ function printHelp() {
334
377
  " --target-contact-id Add one client contact ObjectId (repeatable)",
335
378
  " --target-contact-ids Comma-separated client contact ObjectIds",
336
379
  " --feedback-id <id> Feedback ObjectId for feedback review commands",
380
+ " --request-id <id> Feedback review request ObjectId for submit/revise/review flows",
381
+ " --event-id <id> Feedback board event ObjectId for undo",
382
+ " --version-id <id> Feedback PRD version ObjectId for prd-diff",
383
+ " --compare-to <id> Optional base PRD version ObjectId for prd-diff",
384
+ " --action <value> Feedback review action: approve, reject, request_changes, or cancel",
385
+ " --to-state <value> Target canonical feedback board state for feedback move",
386
+ " --from-state <value> Optional current-state guard for feedback move",
337
387
  " --reason <text> Human review reason included in feedback refinement artifacts",
338
388
  " --status <value> Mission status target, feedback-sync filter, or feedback review state depending on command",
339
389
  " --source <value> Feedback source filter for feedback-sync",
@@ -371,6 +421,10 @@ function printHelp() {
371
421
  " myte update-client --subject \"Weekly client update\" --body-file ./updates/week-12.md",
372
422
  " myte feedback-sync --json",
373
423
  " myte feedback status --feedback-id 507f1f77bcf86cd799439011 --status in_review --reason \"Ready for owner review\"",
424
+ " myte feedback submit --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-edit.yml --json",
425
+ " myte feedback reviews --status open --json",
426
+ " myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
427
+ " myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
374
428
  " myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
375
429
  " myte feedback apply --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
376
430
  " myte suggestions create --file ./suggestions/create.yml",
@@ -1711,6 +1765,208 @@ async function postFeedbackRefinement({ apiBase, key, timeoutMs, feedbackId, mod
1711
1765
  return body.data || {};
1712
1766
  }
1713
1767
 
1768
+ async function fetchFeedbackReviewRequests({ apiBase, key, timeoutMs, status, limit, offset }) {
1769
+ const fetchFn = await getFetch();
1770
+ const url = new URL(`${apiBase}/project-assistant/feedback-review-requests`);
1771
+ if (status) url.searchParams.set("status", String(status));
1772
+ if (limit !== undefined && limit !== null && String(limit).trim()) url.searchParams.set("limit", String(limit));
1773
+ if (offset !== undefined && offset !== null && String(offset).trim()) url.searchParams.set("offset", String(offset));
1774
+ const { resp, body } = await fetchJsonWithTimeout(
1775
+ fetchFn,
1776
+ url.toString(),
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 requests 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 fetchFeedbackReviewRequest({ apiBase, key, timeoutMs, requestId }) {
1794
+ const fetchFn = await getFetch();
1795
+ const url = `${apiBase}/project-assistant/feedback-review-requests/${encodeURIComponent(String(requestId || ""))}`;
1796
+ const { resp, body } = await fetchJsonWithTimeout(
1797
+ fetchFn,
1798
+ url,
1799
+ {
1800
+ method: "GET",
1801
+ headers: { Authorization: `Bearer ${key}` },
1802
+ },
1803
+ timeoutMs
1804
+ );
1805
+
1806
+ if (!resp.ok || body.status !== "success") {
1807
+ const msg = body?.message || `Feedback review request failed (${resp.status})`;
1808
+ const err = new Error(msg);
1809
+ err.status = resp.status;
1810
+ throw err;
1811
+ }
1812
+ return body.data || {};
1813
+ }
1814
+
1815
+ async function fetchFeedbackEvents({ apiBase, key, timeoutMs, feedbackId, limit }) {
1816
+ const fetchFn = await getFetch();
1817
+ const url = new URL(`${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/events`);
1818
+ if (limit !== undefined && limit !== null && String(limit).trim()) url.searchParams.set("limit", String(limit));
1819
+ const { resp, body } = await fetchJsonWithTimeout(
1820
+ fetchFn,
1821
+ url.toString(),
1822
+ {
1823
+ method: "GET",
1824
+ headers: { Authorization: `Bearer ${key}` },
1825
+ },
1826
+ timeoutMs
1827
+ );
1828
+
1829
+ if (!resp.ok || body.status !== "success") {
1830
+ const msg = body?.message || `Feedback events request failed (${resp.status})`;
1831
+ const err = new Error(msg);
1832
+ err.status = resp.status;
1833
+ throw err;
1834
+ }
1835
+ return body.data || {};
1836
+ }
1837
+
1838
+ async function postFeedbackReviewSubmission({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId }) {
1839
+ const fetchFn = await getFetch();
1840
+ const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/refinement/requests`;
1841
+ const { resp, body } = await fetchJsonWithTimeout(
1842
+ fetchFn,
1843
+ url,
1844
+ {
1845
+ method: "POST",
1846
+ headers: {
1847
+ "Content-Type": "application/json",
1848
+ Authorization: `Bearer ${key}`,
1849
+ "X-Idempotency-Key": String(idempotencyKey || "").trim(),
1850
+ ...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
1851
+ },
1852
+ body: JSON.stringify(payload || {}),
1853
+ },
1854
+ timeoutMs
1855
+ );
1856
+
1857
+ if (!resp.ok || body.status !== "success") {
1858
+ const msg = body?.message || `Feedback review submission failed (${resp.status})`;
1859
+ const err = new Error(msg);
1860
+ err.status = resp.status;
1861
+ err.data = body?.data;
1862
+ throw err;
1863
+ }
1864
+ return body.data || {};
1865
+ }
1866
+
1867
+ async function postFeedbackReviewRequestMutation({ apiBase, key, timeoutMs, requestId, endpointAction, payload, idempotencyKey, clientSessionId }) {
1868
+ const fetchFn = await getFetch();
1869
+ const url = `${apiBase}/project-assistant/feedback-review-requests/${encodeURIComponent(String(requestId || ""))}/${endpointAction}`;
1870
+ const { resp, body } = await fetchJsonWithTimeout(
1871
+ fetchFn,
1872
+ url,
1873
+ {
1874
+ method: "POST",
1875
+ headers: {
1876
+ "Content-Type": "application/json",
1877
+ Authorization: `Bearer ${key}`,
1878
+ "X-Idempotency-Key": String(idempotencyKey || "").trim(),
1879
+ ...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
1880
+ },
1881
+ body: JSON.stringify(payload || {}),
1882
+ },
1883
+ timeoutMs
1884
+ );
1885
+
1886
+ if (!resp.ok || body.status !== "success") {
1887
+ const msg = body?.message || `Feedback review ${endpointAction} failed (${resp.status})`;
1888
+ const err = new Error(msg);
1889
+ err.status = resp.status;
1890
+ err.data = body?.data;
1891
+ throw err;
1892
+ }
1893
+ return body.data || {};
1894
+ }
1895
+
1896
+ async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId, endpoint, payload, idempotencyKey, clientSessionId }) {
1897
+ const fetchFn = await getFetch();
1898
+ const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/${endpoint}`;
1899
+ const { resp, body } = await fetchJsonWithTimeout(
1900
+ fetchFn,
1901
+ url,
1902
+ {
1903
+ method: "POST",
1904
+ headers: {
1905
+ "Content-Type": "application/json",
1906
+ Authorization: `Bearer ${key}`,
1907
+ "X-Idempotency-Key": String(idempotencyKey || "").trim(),
1908
+ ...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
1909
+ },
1910
+ body: JSON.stringify(payload || {}),
1911
+ },
1912
+ timeoutMs
1913
+ );
1914
+
1915
+ if (!resp.ok || body.status !== "success") {
1916
+ const msg = body?.message || `Feedback board mutation failed (${resp.status})`;
1917
+ const err = new Error(msg);
1918
+ err.status = resp.status;
1919
+ err.data = body?.data;
1920
+ throw err;
1921
+ }
1922
+ return body.data || {};
1923
+ }
1924
+
1925
+ async function fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId }) {
1926
+ const fetchFn = await getFetch();
1927
+ const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions`;
1928
+ const { resp, body } = await fetchJsonWithTimeout(
1929
+ fetchFn,
1930
+ url,
1931
+ {
1932
+ method: "GET",
1933
+ headers: { Authorization: `Bearer ${key}` },
1934
+ },
1935
+ timeoutMs
1936
+ );
1937
+
1938
+ if (!resp.ok || body.status !== "success") {
1939
+ const msg = body?.message || `Feedback PRD versions request failed (${resp.status})`;
1940
+ const err = new Error(msg);
1941
+ err.status = resp.status;
1942
+ throw err;
1943
+ }
1944
+ return body.data || {};
1945
+ }
1946
+
1947
+ async function fetchFeedbackPrdVersionDiff({ apiBase, key, timeoutMs, feedbackId, versionId, compareTo }) {
1948
+ const fetchFn = await getFetch();
1949
+ const url = new URL(`${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions/${encodeURIComponent(String(versionId || ""))}/diff`);
1950
+ if (compareTo) url.searchParams.set("compare_to", String(compareTo));
1951
+ const { resp, body } = await fetchJsonWithTimeout(
1952
+ fetchFn,
1953
+ url.toString(),
1954
+ {
1955
+ method: "GET",
1956
+ headers: { Authorization: `Bearer ${key}` },
1957
+ },
1958
+ timeoutMs
1959
+ );
1960
+
1961
+ if (!resp.ok || body.status !== "success") {
1962
+ const msg = body?.message || `Feedback PRD version diff failed (${resp.status})`;
1963
+ const err = new Error(msg);
1964
+ err.status = resp.status;
1965
+ throw err;
1966
+ }
1967
+ return body.data || {};
1968
+ }
1969
+
1714
1970
  async function fetchSuggestionsSyncSnapshot({ apiBase, key, timeoutMs, actorScope = "" }) {
1715
1971
  const fetchFn = await getFetch();
1716
1972
  const url = new URL(`${apiBase}/project-assistant/suggestions`);
@@ -4268,7 +4524,7 @@ async function buildFeedbackDraftForCommand(args, subcommand) {
4268
4524
  const reviewNote = firstNonEmptyString(args["review-note"], args.reviewNote, args.review_note);
4269
4525
  if (reviewNote) changes.review_note = feedbackChange(null, reviewNote);
4270
4526
  } else {
4271
- console.error("Unknown feedback command. Use status, edit, assign, archive, get, history, validate, or apply.");
4527
+ console.error("Unknown feedback draft command. Use status, edit, assign, archive, or refine.");
4272
4528
  process.exit(1);
4273
4529
  }
4274
4530
 
@@ -4382,69 +4638,581 @@ async function runFeedbackValidateOrApply(args, mode) {
4382
4638
  }
4383
4639
  }
4384
4640
 
4385
- async function runFeedbackGetOrHistory(args, mode) {
4641
+ function resolveFeedbackRequestIdArg(args) {
4642
+ return firstNonEmptyString(args["request-id"], args.requestId, args.request_id, args._?.[1]);
4643
+ }
4644
+
4645
+ function resolveFeedbackEventIdArg(args) {
4646
+ return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
4647
+ }
4648
+
4649
+ function resolveFeedbackVersionIdArg(args) {
4650
+ return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
4651
+ }
4652
+
4653
+ function summarizeFeedbackReviewRequestPayload(data) {
4654
+ const request = data?.request || data || {};
4655
+ return {
4656
+ request_id: request.request_id || request._id || null,
4657
+ feedback_id: request.feedback_id || request.feedback?.feedback_id || null,
4658
+ status: request.status || null,
4659
+ title: request.title || request.feedback?.title || null,
4660
+ can_review: request.can_review === true,
4661
+ can_approve: request.can_approve === true,
4662
+ is_terminal: request.is_terminal === true,
4663
+ stale: request.stale === true,
4664
+ conflicted: request.conflicted === true,
4665
+ warnings: Array.isArray(request.warnings) ? request.warnings : [],
4666
+ };
4667
+ }
4668
+
4669
+ function printFeedbackReviewRequestSummary(data, args) {
4670
+ const summary = summarizeFeedbackReviewRequestPayload(data);
4671
+ if (args.json) {
4672
+ console.log(JSON.stringify(data, null, 2));
4673
+ return;
4674
+ }
4675
+ console.log(`Request: ${summary.request_id || "(unknown)"}`);
4676
+ console.log(`Feedback: ${summary.feedback_id || "(unknown)"}`);
4677
+ console.log(`Status: ${summary.status || "unknown"}`);
4678
+ if (summary.title) console.log(`Title: ${summary.title}`);
4679
+ console.log(`Can approve: ${summary.can_approve ? "yes" : "no"}`);
4680
+ if (summary.stale || summary.conflicted) {
4681
+ console.log(`Conflict: stale=${summary.stale ? "yes" : "no"}, conflicted=${summary.conflicted ? "yes" : "no"}`);
4682
+ }
4683
+ }
4684
+
4685
+ async function runFeedbackSubmit(args) {
4386
4686
  const key = getProjectApiKey();
4387
4687
  if (!key) {
4388
4688
  console.error("Missing MYTE_API_KEY (project key) in environment/.env");
4389
4689
  process.exit(1);
4390
4690
  }
4391
- const feedbackId = resolveFeedbackIdArg(args);
4691
+ const { absPath, payload } = readFeedbackRefinementArtifact(args);
4692
+ const feedbackId = firstNonEmptyString(args["feedback-id"], args.feedbackId, args.feedback_id, payload.feedback_id);
4392
4693
  if (!feedbackId) {
4393
- console.error("Missing --feedback-id.");
4694
+ console.error("Feedback refinement artifact is missing feedback_id.");
4394
4695
  process.exit(1);
4395
4696
  }
4396
4697
  const timeoutMs = resolveTimeoutMs(args);
4397
4698
  const apiBase = resolveApiBase(args);
4699
+ const clientSessionId = firstNonEmptyString(
4700
+ args["client-session-id"],
4701
+ args.clientSessionId,
4702
+ args.client_session_id,
4703
+ payload.client_session_id
4704
+ );
4705
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
4706
+ args,
4707
+ operation: `feedback_refinement_request_submit:${feedbackId}`,
4708
+ payload,
4709
+ });
4398
4710
 
4399
4711
  let data;
4400
4712
  try {
4401
- data = mode === "history"
4402
- ? await fetchFeedbackHistory({ apiBase, key, timeoutMs, feedbackId })
4403
- : await fetchFeedbackReview({ apiBase, key, timeoutMs, feedbackId });
4713
+ data = await postFeedbackReviewSubmission({
4714
+ apiBase,
4715
+ key,
4716
+ timeoutMs,
4717
+ feedbackId,
4718
+ payload,
4719
+ idempotencyKey,
4720
+ clientSessionId,
4721
+ });
4404
4722
  } catch (err) {
4405
- console.error(`Feedback ${mode} failed:`, err?.message || err);
4723
+ if (args.json) {
4724
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null, artifact_path: absPath }, null, 2));
4725
+ } else {
4726
+ console.error("Feedback submit failed:", err?.message || err);
4727
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
4728
+ }
4406
4729
  process.exit(1);
4407
4730
  }
4408
4731
 
4732
+ const output = {
4733
+ ok: true,
4734
+ artifact_path: absPath,
4735
+ feedback_id: data.feedback_id || feedbackId,
4736
+ project_id: data.project_id || null,
4737
+ pending_review_count: data.pending_review_count || null,
4738
+ request: data.request || null,
4739
+ permissions: data.permissions || null,
4740
+ };
4409
4741
  if (args.json) {
4410
- console.log(JSON.stringify(data, null, 2));
4411
- return;
4412
- }
4413
- if (mode === "history") {
4414
- console.log(`Feedback: ${data.feedback_id || feedbackId}`);
4415
- console.log(`Events: ${data.count || 0}`);
4416
- for (const event of data.events || []) {
4417
- console.log(`- ${event.created_at || "unknown"} ${event.action || "refine"} ${event.history_id || ""}`);
4418
- }
4742
+ console.log(JSON.stringify(output, null, 2));
4419
4743
  return;
4420
4744
  }
4421
- const feedback = data.feedback || {};
4422
- console.log(`Feedback: ${feedback.feedback_id || feedbackId}`);
4423
- console.log(`Title: ${feedback.title || "(untitled)"}`);
4424
- console.log(`Status: ${feedback.feedback_state || feedback.status || "unknown"} (${feedback.status || "legacy unknown"})`);
4425
- console.log(`Priority: ${feedback.priority || "n/a"}`);
4426
- console.log(`Snapshot: ${feedback.snapshot_hash || "n/a"}`);
4745
+ console.log(`Feedback: ${output.feedback_id}`);
4746
+ console.log(`Review request: ${output.request?.request_id || output.request?._id || "(unknown)"}`);
4747
+ console.log(`Status: ${output.request?.status || "pending_review"}`);
4748
+ console.log("Live feedback was not mutated. Owner/delegate approval is required.");
4427
4749
  }
4428
4750
 
4429
- async function runFeedback(args) {
4430
- const subcommand = firstNonEmptyString(args._?.[0]) || "help";
4431
- if (subcommand === "help") {
4432
- printHelp();
4433
- return;
4751
+ async function runFeedbackRevise(args) {
4752
+ const key = getProjectApiKey();
4753
+ if (!key) {
4754
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
4755
+ process.exit(1);
4434
4756
  }
4435
- if (["status", "edit", "assign", "archive", "refine"].includes(subcommand)) {
4436
- await buildFeedbackDraftForCommand(args, subcommand);
4757
+ const requestId = resolveFeedbackRequestIdArg(args);
4758
+ if (!requestId) {
4759
+ console.error("Missing --request-id.");
4760
+ process.exit(1);
4761
+ }
4762
+ const { absPath, payload } = readFeedbackRefinementArtifact(args);
4763
+ const timeoutMs = resolveTimeoutMs(args);
4764
+ const apiBase = resolveApiBase(args);
4765
+ const clientSessionId = firstNonEmptyString(
4766
+ args["client-session-id"],
4767
+ args.clientSessionId,
4768
+ args.client_session_id,
4769
+ payload.client_session_id
4770
+ );
4771
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
4772
+ args,
4773
+ operation: `feedback_refinement_request_revise:${requestId}`,
4774
+ payload,
4775
+ });
4776
+
4777
+ let data;
4778
+ try {
4779
+ data = await postFeedbackReviewRequestMutation({
4780
+ apiBase,
4781
+ key,
4782
+ timeoutMs,
4783
+ requestId,
4784
+ endpointAction: "revise",
4785
+ payload,
4786
+ idempotencyKey,
4787
+ clientSessionId,
4788
+ });
4789
+ } catch (err) {
4790
+ if (args.json) {
4791
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null, artifact_path: absPath }, null, 2));
4792
+ } else {
4793
+ console.error("Feedback revise failed:", err?.message || err);
4794
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
4795
+ }
4796
+ process.exit(1);
4797
+ }
4798
+
4799
+ const output = { ok: true, artifact_path: absPath, ...data };
4800
+ if (args.json) {
4801
+ console.log(JSON.stringify(output, null, 2));
4802
+ return;
4803
+ }
4804
+ printFeedbackReviewRequestSummary(data, args);
4805
+ }
4806
+
4807
+ async function runFeedbackReviews(args) {
4808
+ const key = getProjectApiKey();
4809
+ if (!key) {
4810
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
4811
+ process.exit(1);
4812
+ }
4813
+ const timeoutMs = resolveTimeoutMs(args);
4814
+ const apiBase = resolveApiBase(args);
4815
+ const requestId = firstNonEmptyString(args["request-id"], args.requestId, args.request_id);
4816
+
4817
+ let data;
4818
+ try {
4819
+ data = requestId
4820
+ ? await fetchFeedbackReviewRequest({ apiBase, key, timeoutMs, requestId })
4821
+ : await fetchFeedbackReviewRequests({
4822
+ apiBase,
4823
+ key,
4824
+ timeoutMs,
4825
+ status: firstNonEmptyString(args.status) || "open",
4826
+ limit: firstNonEmptyString(args.limit) || undefined,
4827
+ offset: firstNonEmptyString(args.offset) || undefined,
4828
+ });
4829
+ } catch (err) {
4830
+ console.error("Feedback reviews failed:", err?.message || err);
4831
+ process.exit(1);
4832
+ }
4833
+
4834
+ if (args.json) {
4835
+ console.log(JSON.stringify(data, null, 2));
4836
+ return;
4837
+ }
4838
+ if (requestId) {
4839
+ printFeedbackReviewRequestSummary(data, args);
4840
+ return;
4841
+ }
4842
+ const requests = Array.isArray(data.requests) ? data.requests : [];
4843
+ console.log(`Review requests: ${requests.length}/${data.total ?? requests.length}`);
4844
+ for (const requestItem of requests) {
4845
+ const summary = summarizeFeedbackReviewRequestPayload(requestItem);
4846
+ console.log(`- ${summary.request_id || "(unknown)"} ${summary.status || "unknown"} feedback=${summary.feedback_id || "(unknown)"} title=${summary.title || "(untitled)"}`);
4847
+ }
4848
+ }
4849
+
4850
+ function buildFeedbackReviewDecisionPayload(args, action) {
4851
+ const filePath = firstNonEmptyString(args.file);
4852
+ let payload = {};
4853
+ if (filePath) {
4854
+ const fromFile = readStructuredPayloadFile(filePath, "Feedback review");
4855
+ payload = Array.isArray(fromFile?.items) ? fromFile.items[0] || {} : fromFile;
4856
+ }
4857
+ if (!isPlainObject(payload)) {
4858
+ throw new Error("Feedback review payload must be an object.");
4859
+ }
4860
+ const nextPayload = { ...payload };
4861
+ const reason = firstNonEmptyString(args.reason, args["review-note"], args.reviewNote, args.review_note);
4862
+ if (reason && !nextPayload.reason && !nextPayload.review_reason) {
4863
+ nextPayload.reason = reason;
4864
+ }
4865
+ const finalFile = firstNonEmptyString(args["final-file"], args.finalFile, args.final_file);
4866
+ if (finalFile) {
4867
+ nextPayload.final_change_set = readStructuredPayloadFile(finalFile, "Feedback final change set");
4868
+ }
4869
+ if (action === "approve" || action === "reject") {
4870
+ nextPayload.decision = action;
4871
+ }
4872
+ return nextPayload;
4873
+ }
4874
+
4875
+ async function runFeedbackReviewDecision(args) {
4876
+ const key = getProjectApiKey();
4877
+ if (!key) {
4878
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
4879
+ process.exit(1);
4880
+ }
4881
+ const requestId = resolveFeedbackRequestIdArg(args);
4882
+ if (!requestId) {
4883
+ console.error("Missing --request-id.");
4884
+ process.exit(1);
4885
+ }
4886
+ const action = String(firstNonEmptyString(args.action, args.decision, args["review-action"], args.reviewAction, args.review_action, args._?.[1]) || "").trim().toLowerCase().replace(/-/g, "_");
4887
+ if (!["approve", "reject", "request_changes", "cancel"].includes(action)) {
4888
+ console.error("Missing or invalid review action. Use approve, reject, request_changes, or cancel.");
4889
+ process.exit(1);
4890
+ }
4891
+
4892
+ let payload;
4893
+ try {
4894
+ payload = buildFeedbackReviewDecisionPayload(args, action);
4895
+ } catch (err) {
4896
+ console.error(err?.message || err);
4897
+ process.exit(1);
4898
+ }
4899
+ const endpointAction = action === "request_changes" ? "request-changes" : action === "cancel" ? "cancel" : "review";
4900
+ const timeoutMs = resolveTimeoutMs(args);
4901
+ const apiBase = resolveApiBase(args);
4902
+ const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id, payload.client_session_id);
4903
+ const operationAction = action === "request_changes" ? "request_changes" : action;
4904
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
4905
+ args,
4906
+ operation: `feedback_review_request_${operationAction}:${requestId}`,
4907
+ payload,
4908
+ });
4909
+
4910
+ let data;
4911
+ try {
4912
+ data = await postFeedbackReviewRequestMutation({
4913
+ apiBase,
4914
+ key,
4915
+ timeoutMs,
4916
+ requestId,
4917
+ endpointAction,
4918
+ payload,
4919
+ idempotencyKey,
4920
+ clientSessionId,
4921
+ });
4922
+ } catch (err) {
4923
+ if (args.json) {
4924
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
4925
+ } else {
4926
+ console.error(`Feedback review ${action} failed:`, err?.message || err);
4927
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
4928
+ }
4929
+ process.exit(1);
4930
+ }
4931
+
4932
+ if (args.json) {
4933
+ console.log(JSON.stringify({ ok: true, action, ...data }, null, 2));
4934
+ return;
4935
+ }
4936
+ printFeedbackReviewRequestSummary(data, args);
4937
+ }
4938
+
4939
+ async function runFeedbackMove(args) {
4940
+ const key = getProjectApiKey();
4941
+ if (!key) {
4942
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
4943
+ process.exit(1);
4944
+ }
4945
+ const feedbackId = resolveFeedbackIdArg(args);
4946
+ if (!feedbackId) {
4947
+ console.error("Missing --feedback-id.");
4948
+ process.exit(1);
4949
+ }
4950
+ const toState = firstNonEmptyString(args["to-state"], args.toState, args.to_state, args.status, args._?.[1]);
4951
+ if (!toState) {
4952
+ console.error("Missing --to-state for feedback move.");
4953
+ process.exit(1);
4954
+ }
4955
+ const payload = {
4956
+ to_state: toState,
4957
+ from_state: firstNonEmptyString(args["from-state"], args.fromState, args.from_state) || undefined,
4958
+ reason: firstNonEmptyString(args.reason) || undefined,
4959
+ };
4960
+ const timeoutMs = resolveTimeoutMs(args);
4961
+ const apiBase = resolveApiBase(args);
4962
+ const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
4963
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
4964
+ args,
4965
+ operation: `feedback_board_move:${feedbackId}`,
4966
+ payload,
4967
+ });
4968
+
4969
+ let data;
4970
+ try {
4971
+ data = await postFeedbackBoardMutation({
4972
+ apiBase,
4973
+ key,
4974
+ timeoutMs,
4975
+ feedbackId,
4976
+ endpoint: "board-move",
4977
+ payload,
4978
+ idempotencyKey,
4979
+ clientSessionId,
4980
+ });
4981
+ } catch (err) {
4982
+ if (args.json) {
4983
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
4984
+ } else {
4985
+ console.error("Feedback move failed:", err?.message || err);
4986
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
4987
+ }
4988
+ process.exit(1);
4989
+ }
4990
+
4991
+ if (args.json) {
4992
+ console.log(JSON.stringify({ ok: true, ...data }, null, 2));
4993
+ return;
4994
+ }
4995
+ console.log(`Feedback: ${data.feedback_id || feedbackId}`);
4996
+ console.log(`State: ${data.feedback_state || toState}`);
4997
+ if (data.event?.event_id) console.log(`Event: ${data.event.event_id}`);
4998
+ }
4999
+
5000
+ async function runFeedbackUndo(args) {
5001
+ const key = getProjectApiKey();
5002
+ if (!key) {
5003
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
5004
+ process.exit(1);
5005
+ }
5006
+ const feedbackId = resolveFeedbackIdArg(args);
5007
+ const eventId = resolveFeedbackEventIdArg(args);
5008
+ if (!feedbackId) {
5009
+ console.error("Missing --feedback-id.");
5010
+ process.exit(1);
5011
+ }
5012
+ if (!eventId) {
5013
+ console.error("Missing --event-id.");
5014
+ process.exit(1);
5015
+ }
5016
+ const payload = { reason: firstNonEmptyString(args.reason) || undefined };
5017
+ const timeoutMs = resolveTimeoutMs(args);
5018
+ const apiBase = resolveApiBase(args);
5019
+ const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
5020
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
5021
+ args,
5022
+ operation: `feedback_event_undo:${feedbackId}:${eventId}`,
5023
+ payload,
5024
+ });
5025
+
5026
+ let data;
5027
+ try {
5028
+ data = await postFeedbackBoardMutation({
5029
+ apiBase,
5030
+ key,
5031
+ timeoutMs,
5032
+ feedbackId,
5033
+ endpoint: `events/${encodeURIComponent(String(eventId))}/undo`,
5034
+ payload,
5035
+ idempotencyKey,
5036
+ clientSessionId,
5037
+ });
5038
+ } catch (err) {
5039
+ if (args.json) {
5040
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
5041
+ } else {
5042
+ console.error("Feedback undo failed:", err?.message || err);
5043
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
5044
+ }
5045
+ process.exit(1);
5046
+ }
5047
+
5048
+ if (args.json) {
5049
+ console.log(JSON.stringify({ ok: true, ...data }, null, 2));
5050
+ return;
5051
+ }
5052
+ console.log(`Feedback: ${data.feedback_id || feedbackId}`);
5053
+ console.log(`State: ${data.feedback_state || "unknown"}`);
5054
+ if (data.reverted_event_id) console.log(`Reverted: ${data.reverted_event_id}`);
5055
+ }
5056
+
5057
+ async function runFeedbackPrdVersions(args) {
5058
+ const key = getProjectApiKey();
5059
+ if (!key) {
5060
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
5061
+ process.exit(1);
5062
+ }
5063
+ const feedbackId = resolveFeedbackIdArg(args);
5064
+ if (!feedbackId) {
5065
+ console.error("Missing --feedback-id.");
5066
+ process.exit(1);
5067
+ }
5068
+ const timeoutMs = resolveTimeoutMs(args);
5069
+ const apiBase = resolveApiBase(args);
5070
+ let data;
5071
+ try {
5072
+ data = await fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId });
5073
+ } catch (err) {
5074
+ console.error("Feedback PRD versions failed:", err?.message || err);
5075
+ process.exit(1);
5076
+ }
5077
+ if (args.json) {
5078
+ console.log(JSON.stringify(data, null, 2));
5079
+ return;
5080
+ }
5081
+ const versions = Array.isArray(data.versions) ? data.versions : [];
5082
+ console.log(`PRD versions: ${versions.length}`);
5083
+ for (const version of versions) {
5084
+ 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"}`);
5085
+ }
5086
+ }
5087
+
5088
+ async function runFeedbackPrdDiff(args) {
5089
+ const key = getProjectApiKey();
5090
+ if (!key) {
5091
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
5092
+ process.exit(1);
5093
+ }
5094
+ const feedbackId = resolveFeedbackIdArg(args);
5095
+ const versionId = resolveFeedbackVersionIdArg(args);
5096
+ if (!feedbackId) {
5097
+ console.error("Missing --feedback-id.");
5098
+ process.exit(1);
5099
+ }
5100
+ if (!versionId) {
5101
+ console.error("Missing --version-id.");
5102
+ process.exit(1);
5103
+ }
5104
+ const timeoutMs = resolveTimeoutMs(args);
5105
+ const apiBase = resolveApiBase(args);
5106
+ const compareTo = firstNonEmptyString(args["compare-to"], args.compareTo, args.compare_to);
5107
+ let data;
5108
+ try {
5109
+ data = await fetchFeedbackPrdVersionDiff({ apiBase, key, timeoutMs, feedbackId, versionId, compareTo });
5110
+ } catch (err) {
5111
+ console.error("Feedback PRD diff failed:", err?.message || err);
5112
+ process.exit(1);
5113
+ }
5114
+ if (args.json) {
5115
+ console.log(JSON.stringify(data, null, 2));
5116
+ return;
5117
+ }
5118
+ console.log(data.diff || "");
5119
+ }
5120
+
5121
+ async function runFeedbackGetOrHistory(args, mode) {
5122
+ const key = getProjectApiKey();
5123
+ if (!key) {
5124
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
5125
+ process.exit(1);
5126
+ }
5127
+ const feedbackId = resolveFeedbackIdArg(args);
5128
+ if (!feedbackId) {
5129
+ console.error("Missing --feedback-id.");
5130
+ process.exit(1);
5131
+ }
5132
+ const timeoutMs = resolveTimeoutMs(args);
5133
+ const apiBase = resolveApiBase(args);
5134
+
5135
+ let data;
5136
+ try {
5137
+ data = mode === "history"
5138
+ ? await fetchFeedbackEvents({ apiBase, key, timeoutMs, feedbackId, limit: firstNonEmptyString(args.limit) || undefined })
5139
+ : await fetchFeedbackReview({ apiBase, key, timeoutMs, feedbackId });
5140
+ } catch (err) {
5141
+ console.error(`Feedback ${mode} failed:`, err?.message || err);
5142
+ process.exit(1);
5143
+ }
5144
+
5145
+ if (args.json) {
5146
+ console.log(JSON.stringify(data, null, 2));
5147
+ return;
5148
+ }
5149
+ if (mode === "history") {
5150
+ console.log(`Feedback: ${data.feedback_id || feedbackId}`);
5151
+ console.log(`Events: ${data.count || 0}`);
5152
+ for (const event of data.events || []) {
5153
+ console.log(`- ${event.created_at || "unknown"} ${event.action || "refine"} ${event.history_id || ""}`);
5154
+ }
5155
+ return;
5156
+ }
5157
+ const feedback = data.feedback || {};
5158
+ console.log(`Feedback: ${feedback.feedback_id || feedbackId}`);
5159
+ console.log(`Title: ${feedback.title || "(untitled)"}`);
5160
+ console.log(`Status: ${feedback.feedback_state || feedback.status || "unknown"} (${feedback.status || "legacy unknown"})`);
5161
+ console.log(`Priority: ${feedback.priority || "n/a"}`);
5162
+ console.log(`Snapshot: ${feedback.snapshot_hash || "n/a"}`);
5163
+ }
5164
+
5165
+ async function runFeedback(args) {
5166
+ const subcommand = firstNonEmptyString(args._?.[0]) || "help";
5167
+ if (subcommand === "help") {
5168
+ printHelp();
5169
+ return;
5170
+ }
5171
+ if (["status", "edit", "assign", "archive", "refine"].includes(subcommand)) {
5172
+ await buildFeedbackDraftForCommand(args, subcommand);
4437
5173
  return;
4438
5174
  }
4439
5175
  if (subcommand === "validate" || subcommand === "apply") {
4440
5176
  await runFeedbackValidateOrApply(args, subcommand);
4441
5177
  return;
4442
5178
  }
5179
+ if (subcommand === "submit") {
5180
+ await runFeedbackSubmit(args);
5181
+ return;
5182
+ }
5183
+ if (subcommand === "revise") {
5184
+ await runFeedbackRevise(args);
5185
+ return;
5186
+ }
5187
+ if (subcommand === "reviews" || subcommand === "requests") {
5188
+ await runFeedbackReviews(args);
5189
+ return;
5190
+ }
5191
+ if (subcommand === "review") {
5192
+ await runFeedbackReviewDecision(args);
5193
+ return;
5194
+ }
5195
+ if (subcommand === "move") {
5196
+ await runFeedbackMove(args);
5197
+ return;
5198
+ }
5199
+ if (subcommand === "undo") {
5200
+ await runFeedbackUndo(args);
5201
+ return;
5202
+ }
5203
+ if (subcommand === "prd-versions") {
5204
+ await runFeedbackPrdVersions(args);
5205
+ return;
5206
+ }
5207
+ if (subcommand === "prd-diff") {
5208
+ await runFeedbackPrdDiff(args);
5209
+ return;
5210
+ }
4443
5211
  if (subcommand === "get" || subcommand === "history") {
4444
5212
  await runFeedbackGetOrHistory(args, subcommand);
4445
5213
  return;
4446
5214
  }
4447
- console.error("Unknown feedback command. Use status, edit, assign, archive, get, history, validate, or apply.");
5215
+ 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.");
4448
5216
  process.exit(1);
4449
5217
  }
4450
5218
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytegroupinc/myte-core",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Myte CLI core implementation.",
5
5
  "type": "commonjs",
6
6
  "main": "cli.js",