@mytegroupinc/myte-core 0.0.31 → 0.0.33

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 CHANGED
@@ -12,21 +12,30 @@ This package exists so the public wrapper can stay small and versioned cleanly.
12
12
  ## Requirements
13
13
 
14
14
  - Node `18+`
15
- - `MYTE_API_KEY` for project-scoped commands
16
- - `MYTEAI_API_KEY` for `myte ai`
17
- - `git` only when using `query --with-diff`
15
+ - `MYTE_API_KEY` for project-scoped commands
16
+ - `MYTEAI_API_KEY` for `myte ai`
17
+ - `MYTEAI_API_KEY` for `mytecody`
18
+ - `git` only when using `query --with-diff`
18
19
  - No runtime npm dependencies. The CLI uses Node 18+ built-ins for env loading, argument parsing, HTTP, and local context serialization.
19
20
 
20
21
  ## Behavior Summary
21
22
 
22
23
  - Snapshot-style commands such as `bootstrap`, `sync-qaqc`, `feedback-sync`, and `suggestions sync` write local `MyteCommandCenter` data.
23
24
  - `feedback status|edit|assign|archive` writes reviewable local YAML artifacts; `feedback submit|revise|reviews|review` routes them through the backend owner-review membrane.
24
- - `feedback move|undo|prd-versions|prd-diff|history` calls the project-key Feedback API while leaving lifecycle rules and permissions server-side. `feedback move --feedback-ids` sends one batch move request.
25
+ - `feedback move|comment|undo|prd-versions|prd-diff|history` calls the project-key Feedback API while leaving lifecycle rules and permissions server-side. `feedback move --feedback-ids` sends one batch move request.
25
26
  - `feedback validate|apply` remains available for validation and owner-direct apply paths; live authorization, stale checks, and history stay server-side.
26
27
  - `query --with-diff` requires project repos to be configured for diff collection and fails fast when no matching local project repo can be resolved.
27
- - Public package documentation is intentionally minimal. Internal rollout and design notes are not part of the npm package contract.
28
-
29
- ## Agent Usage Contract
28
+ - Public package documentation is intentionally minimal. Internal rollout and design notes are not part of the npm package contract.
29
+ - `mytecody` is a model-agnostic MyteCody team launcher. It supports `doctor`,
30
+ `doctor --probe-gateway`, `update --dry-run`, and `update`. On first coding
31
+ run, it installs the branded MyteCody engine from the Myte release manifest
32
+ into a user-local Myte cache after signature/hash checks. Coding execution
33
+ requires that engine, a reachable Myte AI Cody gateway, and `MYTEAI_API_KEY`.
34
+ - The package does not bundle the large MyteCody engine binary. See
35
+ `THIRD_PARTY_NOTICES.md` and `TRADEMARKS.md` for Codex lineage and Myte brand
36
+ notices.
37
+
38
+ ## Agent Usage Contract
30
39
 
31
40
  Coding agents should treat `MYTE_API_KEY` as a project-scoped Project Assistant key:
32
41
 
@@ -42,7 +51,8 @@ Coding agents should treat `MYTE_API_KEY` as a project-scoped Project Assistant
42
51
  - Use `suggestions revise` only against an existing `suggestion_id`; do not send `change_type` on revisions because the backend keeps the thread's original type.
43
52
  - Use `suggestions review` for owner/elevated mission-review decisions: `approve`, `request_changes`, or `reject`.
44
53
  - Use `mission archive` for project-key lifecycle archival. Do not send `Archived` through `mission status`; restore archived missions from the web archived-board view.
45
- - Use `feedback move` only when a direct audited state move is intended and include a concrete `--reason`. Use `--feedback-ids` for batch active-state moves or authorized batch archive.
54
+ - Use `feedback move` only when a direct audited state move is intended and include a concrete `--reason`. Use `--feedback-ids` for batch active-state moves or authorized batch archive.
55
+ - Use `feedback comment --feedback-id <id> --body-file ./comment.md` for text-only feedback-specific implementation notes. Attachments remain web UI only for this project-key endpoint.
46
56
  - Do not use the project-key API/CLI to unarchive Feedback. Normal `feedback-sync` excludes archived Feedback; unarchive from the web archived Feedback view.
47
57
  - Use `update-team` when an agent needs to leave a project-level implementation note or verification comment.
48
58
 
@@ -57,12 +67,16 @@ PRD document contract:
57
67
  - `myte-kanban` uploads send `{ ticket_markdown }`, where the leading JSON block contains metadata and the remaining markdown is the PRD body.
58
68
  - The backend stores the markdown as `prd_markdown`, mirrors it as PRD text for search/sync, generates a DOCX attachment from it, and marks `prd_format=markdown`.
59
69
  - The UI renders the PRD from stored PRD text/document content, not from `description`.
70
+ - Renderer-friendly PRDs should use one `# Title`, `##` sections, concise paragraphs, bullets, GitHub tables/checklists where useful, and fenced code blocks only for real code/config.
71
+ - Do not wrap the whole PRD in a ` ```markdown ` fence. Do not use raw HTML, inline style blocks, or decorative banners.
72
+ - PRD identity is explicit: `myte create-prd` creates a PRD asset with PRD metadata. Generic uploaded `.md`, `.docx`, `.pdf`, and `.txt` files remain normal attachments unless explicitly created as PRDs.
60
73
 
61
- Feedback comment support:
62
-
63
- - `feedback-sync` includes existing feedback comment turns in local context.
64
- - Feedback-specific comment creation exists in the web backend at `/api/feedbacks/<feedback_id>/comments`, protected by JWT/project assignment.
65
- - There is no project-key CLI command for `myte feedback comment` yet. That requires a Project Assistant feedback-comment route plus npm command before agents should rely on it.
74
+ Feedback comment support:
75
+
76
+ - `feedback-sync` includes existing feedback comment turns in local context.
77
+ - Feedback-specific comment creation exists in the web backend at `/api/feedbacks/<feedback_id>/comments`, protected by JWT/project assignment.
78
+ - Project-key comment creation is available through `myte feedback comment --feedback-id <id> --body "..."` or `--body-file ./comment.md`.
79
+ - The project-key feedback comment endpoint is text-only in this slice and caps content at 500,000 characters. Use the web UI when comment attachments are required.
66
80
 
67
81
  ## Mission Action Map
68
82
 
@@ -144,9 +158,10 @@ Agent rule: never guess `suggestion_id`. If `mission-ops.yml` is missing or stal
144
158
  | `feedback submit` | Sends a local proposal artifact to the backend as a review request. | Does not mutate live feedback before approval. |
145
159
  | `feedback revise` | Resubmits the original submitter's request after `request_changes`. | Only valid for the original submitter and `needs_changes` requests. |
146
160
  | `feedback reviews` | Lists review requests or fetches one request by `--request-id`. | Read-only; response includes backend permissions. |
147
- | `feedback review` | Approves, rejects, requests changes, or cancels one review request, or a batch with `--request-ids`. | Backend restricts approval/review decisions to Project Owner or elevated delegate for Feedback scope; batch review uses one grouped notification path. |
148
- | `feedback move` | Moves a card or batch of cards across allowed board states directly with an audit event. | `--feedback-ids` sends one batch request. Blocked with `pending_feedback_review` while an active linked review request exists; governed states such as archive/reject/deploy/freeze remain backend-authorized. Project-key unarchive is not supported. |
149
- | `feedback undo` | Reverses an audited board event when there is no conflict. | Backend validates event ownership/project scope and conflict state. |
161
+ | `feedback review` | Approves, rejects, requests changes, or cancels one review request, or a batch with `--request-ids`. | Backend restricts approval/review decisions to Project Owner or elevated delegate for Feedback scope; batch review uses one grouped notification path. |
162
+ | `feedback move` | Moves a card or batch of cards across allowed board states directly with an audit event. | `--feedback-ids` sends one batch request. Blocked with `pending_feedback_review` while an active linked review request exists; governed states such as archive/reject/deploy/freeze remain backend-authorized. Project-key unarchive is not supported. |
163
+ | `feedback comment` | Creates a text-only comment on one Feedback item. | Backend checks project-key access, owner/assigned permission, idempotency, and the 500,000-character content cap. Attachments remain web UI only. |
164
+ | `feedback undo` | Reverses an audited board event when there is no conflict. | Backend validates event ownership/project scope and conflict state. |
150
165
  | `feedback prd-versions` | Lists retained PRD baselines/revisions for a feedback item. | Read-only; old S3 objects are retained by reference. |
151
166
  | `feedback prd-diff` | Fetches backend-generated text diff for PRD versions. | Read-only; backend controls version access. |
152
167
  | `feedback history` | Lists audited feedback board/refinement events. | Read-only; backend checks project access. |
@@ -202,9 +217,10 @@ Mutation routes:
202
217
  - `POST /api/project-assistant/feedback-review-requests/batch-review`
203
218
  - `POST /api/project-assistant/feedback-review-requests/<request_id>/request-changes`
204
219
  - `POST /api/project-assistant/feedback-review-requests/<request_id>/cancel`
205
- - `POST /api/project-assistant/feedback/<feedback_id>/board-move`
206
- - `POST /api/project-assistant/feedback/batch-board-move`
207
- - `POST /api/project-assistant/feedback/<feedback_id>/events/<event_id>/undo`
220
+ - `POST /api/project-assistant/feedback/<feedback_id>/board-move`
221
+ - `POST /api/project-assistant/feedback/batch-board-move`
222
+ - `POST /api/project-assistant/feedback/<feedback_id>/comments`
223
+ - `POST /api/project-assistant/feedback/<feedback_id>/events/<event_id>/undo`
208
224
  - `POST /api/project-assistant/suggestions`
209
225
  - `POST /api/project-assistant/suggestions/revise`
210
226
  - `POST /api/project-assistant/suggestions/review`
@@ -0,0 +1,21 @@
1
+ # Third-Party Notices
2
+
3
+ This package includes the Myte CLI launcher and MyteCody distribution client.
4
+
5
+ The branded MyteCody engine installed by the launcher is derived from the
6
+ OpenAI Codex open-source project. OpenAI Codex is licensed under the
7
+ Apache License, Version 2.0.
8
+
9
+ Source lineage tracked for this slice:
10
+
11
+ - Project: OpenAI Codex
12
+ - Version baseline: `0.139.0`
13
+ - License: Apache-2.0
14
+ - Upstream: https://github.com/openai/codex
15
+
16
+ The MyteCody engine artifact is distributed separately from this npm package
17
+ through a Myte release manifest. This package verifies the release artifact
18
+ before installing it into the user's Myte-owned local cache.
19
+
20
+ Myte names, marks, logos, terminal branding, and product design are not part of
21
+ the OpenAI Codex project and remain Myte-owned brand assets.
package/TRADEMARKS.md ADDED
@@ -0,0 +1,10 @@
1
+ # Trademarks
2
+
3
+ Myte, Myte AI, MyteCody, "MYTE CODY - Your Tech Your Way", logo assets, terminal
4
+ animation assets, and related product branding are Myte-owned marks and brand
5
+ assets.
6
+
7
+ OpenAI and Codex are trademarks or project names of their respective owners.
8
+ MyteCody uses a Codex-derived open-source engine under the applicable open
9
+ source license, but MyteCody is a Myte product surface and is not presented as
10
+ an official OpenAI product.
package/cli.js CHANGED
@@ -208,9 +208,10 @@ function printHelp() {
208
208
  " myte feedback reviews [--status open|terminal|all] [--request-id <id>] [--json]",
209
209
  " myte feedback review --request-id <id> --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
210
210
  " myte feedback review --request-ids \"<id1,id2>\" --action approve|reject|request_changes|cancel [--reason \"...\"] [--json]",
211
- " myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
212
- " myte feedback move --feedback-ids \"<id1,id2>\" --to-state in_progress --reason \"...\" [--json]",
213
- " myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
211
+ " myte feedback move --feedback-id <id> --to-state in_progress --from-state todo --reason \"...\" [--json]",
212
+ " myte feedback move --feedback-ids \"<id1,id2>\" --to-state in_progress --reason \"...\" [--json]",
213
+ " myte feedback comment --feedback-id <id> --body \"...\" [--body-file ./comment.md] [--json]",
214
+ " myte feedback undo --feedback-id <id> --event-id <id> --reason \"...\" [--json]",
214
215
  " myte feedback prd-versions --feedback-id <id> [--json]",
215
216
  " myte feedback prd-diff --feedback-id <id> --version-id <id> [--compare-to <id>] [--json]",
216
217
  " myte feedback validate --file ./MyteCommandCenter/reviews/feedback/<id>-status.yml [--json]",
@@ -287,6 +288,8 @@ function printHelp() {
287
288
  " - Never put the full PRD in description. The complete PRD must live in the markdown file body",
288
289
  " - Each item carries the full PRD markdown blob as prd_markdown/ticket_markdown",
289
290
  " - Backend stores that blob as the renderable PRD document source, mirrors it for PRD text search/sync, and generates a DOCX attachment",
291
+ " - Renderer-friendly PRDs use one # title, ## sections, bullets, GitHub tables/checklists where useful, and code fences only for real code/config",
292
+ " - Do not wrap the whole PRD in a ```markdown fence; generic .md/.docx/.pdf/.txt uploads are normal attachments unless created as PRDs",
290
293
  " - PRD DOCX content: the markdown body is stored verbatim",
291
294
  "",
292
295
  "update-team contract:",
@@ -310,7 +313,7 @@ function printHelp() {
310
313
  " - Archived feedback is intentionally excluded from normal sync; returning it to active work is web UI only",
311
314
  " - Writes project feedback metadata and conversation turns into MyteCommandCenter/data/feedback.yml",
312
315
  " - Stores full PRD context in MyteCommandCenter/PRD/feedback-sync/*.md and points to those files from feedback.yml",
313
- " - Reads existing feedback comments; direct project-key creation of feedback-specific comments is not exposed yet",
316
+ " - Reads existing feedback comments and can create text-only feedback-specific comments through the project-key API",
314
317
  "",
315
318
  "feedback review contract:",
316
319
  " - Draft commands write review artifacts under MyteCommandCenter/reviews/feedback/*.yml for local IDE diff review",
@@ -319,8 +322,9 @@ function printHelp() {
319
322
  " - reviews/review expose owner/delegate approval, rejection, request-changes, and cancel decisions",
320
323
  " - review with --request-ids sends one batch review request; it does not loop single-review calls",
321
324
  " - live lifecycle moves return pending_feedback_review while an active linked review request exists",
322
- " - move/undo operate safe board states directly; governed states remain backend-authorized",
323
- " - move with --feedback-ids sends one batch board move request; it supports active-state moves and authorized archive",
325
+ " - move/undo operate safe board states directly; governed states remain backend-authorized",
326
+ " - move with --feedback-ids sends one batch board move request; it supports active-state moves and authorized archive",
327
+ " - comment creates text-only feedback-specific comments; attachments remain web UI only for this endpoint",
324
328
  " - project-key API/CLI cannot unarchive archived Feedback; use the web archived Feedback view",
325
329
  " - get/history read current feedback snapshots and audited event history through the project-key API",
326
330
  " - prd-versions/prd-diff expose retained PRD versions and backend-generated text diffs",
@@ -336,8 +340,9 @@ function printHelp() {
336
340
  " - revise: resubmit the original submitter's request after request_changes",
337
341
  " - reviews: list open/terminal/all requests or inspect one request",
338
342
  " - review: approve, reject, request_changes, or cancel; backend restricts this to owner/delegate capability",
339
- " - move: direct audited board movement for allowed states; governed transitions remain backend-authorized",
340
- " - undo: reverse an audited board event when conflict checks allow it",
343
+ " - move: direct audited board movement for allowed states; governed transitions remain backend-authorized",
344
+ " - comment: create a text-only comment on one Feedback item",
345
+ " - undo: reverse an audited board event when conflict checks allow it",
341
346
  " - prd-versions: list retained PRD baselines/revisions for a feedback item",
342
347
  " - prd-diff: fetch backend-generated text diff between PRD versions",
343
348
  " - history: list board/refinement events for audit review",
@@ -358,10 +363,11 @@ function printHelp() {
358
363
  " --stdin Read supported command content from stdin instead of inline text or a file path",
359
364
  " --title <text> Override PRD title for raw markdown uploads",
360
365
  " --description <text> Set feedback description/card summary for raw markdown uploads",
361
- " --content <text> Team update content for update-team",
362
- " --subject <text> Subject for update-owner or update-client",
366
+ " --content <text> Team update content for update-team",
367
+ " --body <text> Feedback comment body for feedback comment",
368
+ " --subject <text> Subject for update-owner or update-client",
363
369
  " --body-markdown <md> Markdown body for update-owner or update-client",
364
- " --body-file <path> Read update-owner or update-client markdown body from a file",
370
+ " --body-file <path> Read feedback comment, update-owner, or update-client markdown body from a file",
365
371
  " --target-contact-id Add one client contact ObjectId (repeatable)",
366
372
  " --target-contact-ids Comma-separated client contact ObjectIds",
367
373
  " --feedback-id <id> Feedback ObjectId for feedback review commands",
@@ -415,9 +421,10 @@ function printHelp() {
415
421
  " myte feedback status --feedback-id 507f1f77bcf86cd799439011 --status in_review --reason \"Ready for owner review\"",
416
422
  " myte feedback submit --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-edit.yml --json",
417
423
  " myte feedback reviews --status open --json",
418
- " myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
419
- " myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
420
- " myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
424
+ " myte feedback review --request-id 507f1f77bcf86cd799439022 --action approve --reason \"Looks correct\" --json",
425
+ " myte feedback move --feedback-id 507f1f77bcf86cd799439011 --to-state in_progress --reason \"Started\" --json",
426
+ " myte feedback comment --feedback-id 507f1f77bcf86cd799439011 --body \"Implementation notes are ready for review.\" --json",
427
+ " myte feedback validate --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
421
428
  " myte feedback apply --file ./MyteCommandCenter/reviews/feedback/507f1f77bcf86cd799439011-status.yml --json",
422
429
  " myte suggestions create --file ./suggestions/create.yml",
423
430
  " myte suggestions revise",
@@ -1968,7 +1975,7 @@ async function postFeedbackBoardMutation({ apiBase, key, timeoutMs, feedbackId,
1968
1975
  return body.data || {};
1969
1976
  }
1970
1977
 
1971
- async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
1978
+ async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload, idempotencyKey, clientSessionId }) {
1972
1979
  const fetchFn = await getFetch();
1973
1980
  const url = `${apiBase}/project-assistant/feedback/batch-board-move`;
1974
1981
  const { resp, body } = await fetchJsonWithTimeout(
@@ -1994,10 +2001,39 @@ async function postFeedbackBatchBoardMutation({ apiBase, key, timeoutMs, payload
1994
2001
  err.data = body?.data;
1995
2002
  throw err;
1996
2003
  }
1997
- return body.data || {};
1998
- }
1999
-
2000
- async function fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId }) {
2004
+ return body.data || {};
2005
+ }
2006
+
2007
+ async function postFeedbackComment({ apiBase, key, timeoutMs, feedbackId, payload, idempotencyKey, clientSessionId }) {
2008
+ const fetchFn = await getFetch();
2009
+ const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/comments`;
2010
+ const { resp, body } = await fetchJsonWithTimeout(
2011
+ fetchFn,
2012
+ url,
2013
+ {
2014
+ method: "POST",
2015
+ headers: {
2016
+ "Content-Type": "application/json",
2017
+ Authorization: `Bearer ${key}`,
2018
+ "X-Idempotency-Key": String(idempotencyKey || "").trim(),
2019
+ ...(String(clientSessionId || "").trim() ? { "X-Client-Session-Id": String(clientSessionId).trim() } : {}),
2020
+ },
2021
+ body: JSON.stringify(payload || {}),
2022
+ },
2023
+ timeoutMs
2024
+ );
2025
+
2026
+ if (!resp.ok || body.status !== "success") {
2027
+ const msg = body?.message || `Feedback comment failed (${resp.status})`;
2028
+ const err = new Error(msg);
2029
+ err.status = resp.status;
2030
+ err.data = body?.data;
2031
+ throw err;
2032
+ }
2033
+ return body.data || {};
2034
+ }
2035
+
2036
+ async function fetchFeedbackPrdVersions({ apiBase, key, timeoutMs, feedbackId }) {
2001
2037
  const fetchFn = await getFetch();
2002
2038
  const url = `${apiBase}/project-assistant/feedback/${encodeURIComponent(String(feedbackId || ""))}/prd/versions`;
2003
2039
  const { resp, body } = await fetchJsonWithTimeout(
@@ -3401,25 +3437,25 @@ function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
3401
3437
  let prdFileCount = 0;
3402
3438
  const materializedItems = items.map((rawItem, index) => {
3403
3439
  const item = isPlainObject(rawItem) ? { ...rawItem } : {};
3404
- const feedbackId = stableItemId(item, ["feedback_id", "id"], `F${String(index + 1).padStart(3, "0")}`);
3405
- const conversationTurns = normalizeFeedbackConversationTurns(item.conversation_turns);
3406
- const prdText = String(item.prd_text || "").trim();
3407
- const attachmentDocuments = Array.isArray(item.attachment_documents) ? item.attachment_documents : [];
3408
-
3409
- let contextSource = "description_only";
3410
- let contextNote = "No separate PRD. Use feedback_text as the context for this feedback item.";
3411
- let prdFile = null;
3440
+ const feedbackId = stableItemId(item, ["feedback_id", "id"], `F${String(index + 1).padStart(3, "0")}`);
3441
+ const conversationTurns = normalizeFeedbackConversationTurns(item.conversation_turns);
3442
+ const prdText = String(item.prd_text || "").trim();
3443
+ const attachmentDocuments = Array.isArray(item.attachment_documents) ? item.attachment_documents : [];
3444
+
3445
+ let contextSource = "description_only";
3446
+ let contextNote = "No separate PRD. Use feedback_text as the context for this feedback item.";
3447
+ let prdFile = null;
3412
3448
 
3413
3449
  if (prdText) {
3414
3450
  const prdFilename = `${sanitizeFileSegment(feedbackId, `feedback-${index + 1}`)}.md`;
3415
- const prdPath = path.join(prdSyncDir, prdFilename);
3416
- writeTextFile(prdPath, ensureTrailingNewline(prdText));
3417
- prdFile = toPosixRelativePath(targetRoot, prdPath);
3418
- contextSource = attachmentDocuments.length > 0 ? "attachment_file" : "prd_file";
3419
- contextNote = attachmentDocuments.length > 0
3420
- ? "Readable feedback attachment documents are stored in the linked file."
3421
- : "Full PRD context is stored in the linked file.";
3422
- prdFileCount += 1;
3451
+ const prdPath = path.join(prdSyncDir, prdFilename);
3452
+ writeTextFile(prdPath, ensureTrailingNewline(prdText));
3453
+ prdFile = toPosixRelativePath(targetRoot, prdPath);
3454
+ contextSource = attachmentDocuments.length > 0 ? "attachment_file" : "prd_file";
3455
+ contextNote = attachmentDocuments.length > 0
3456
+ ? "Readable feedback attachment documents are stored in the linked file."
3457
+ : "Full PRD context is stored in the linked file.";
3458
+ prdFileCount += 1;
3423
3459
  } else if (item.has_prd_text) {
3424
3460
  contextSource = "prd_declared_but_unavailable";
3425
3461
  contextNote = "A separate PRD exists for this feedback item, but readable PRD text was not included in this sync snapshot.";
@@ -5086,13 +5122,33 @@ function resolveFeedbackRequestIdArg(args) {
5086
5122
  return firstNonEmptyString(args["request-id"], args.requestId, args.request_id, args._?.[1]);
5087
5123
  }
5088
5124
 
5089
- function resolveFeedbackEventIdArg(args) {
5090
- return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
5091
- }
5125
+ function resolveFeedbackEventIdArg(args) {
5126
+ return firstNonEmptyString(args["event-id"], args.eventId, args.event_id, args._?.[1]);
5127
+ }
5092
5128
 
5093
- function resolveFeedbackVersionIdArg(args) {
5094
- return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
5095
- }
5129
+ function resolveFeedbackVersionIdArg(args) {
5130
+ return firstNonEmptyString(args["version-id"], args.versionId, args.version_id, args._?.[1]);
5131
+ }
5132
+
5133
+ async function resolveFeedbackCommentContent(args, feedbackId) {
5134
+ const explicit = firstNonEmptyString(args.body, args.content, args["body-markdown"], args.bodyMarkdown, args.body_markdown);
5135
+ if (explicit) return explicit;
5136
+
5137
+ const bodyFile = firstNonEmptyString(args["body-file"], args.bodyFile, args.body_file);
5138
+ if (bodyFile) {
5139
+ const bodyPath = resolveInputFile(bodyFile, "Feedback comment body");
5140
+ return fs.readFileSync(bodyPath, "utf8");
5141
+ }
5142
+
5143
+ if (args.stdin) {
5144
+ return readStdinText();
5145
+ }
5146
+
5147
+ const positionals = Array.isArray(args._) ? args._.slice(1) : [];
5148
+ const feedbackIdFromPositional = positionals[0] && String(positionals[0]) === String(feedbackId);
5149
+ const textParts = feedbackIdFromPositional ? positionals.slice(1) : positionals;
5150
+ return textParts.join(" ");
5151
+ }
5096
5152
 
5097
5153
  function summarizeFeedbackReviewRequestPayload(data) {
5098
5154
  const request = data?.request || data || {};
@@ -5451,7 +5507,7 @@ async function runFeedbackReviewDecision(args) {
5451
5507
  if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
5452
5508
  }
5453
5509
 
5454
- async function runFeedbackMove(args) {
5510
+ async function runFeedbackMove(args) {
5455
5511
  const key = getProjectApiKey();
5456
5512
  if (!key) {
5457
5513
  console.error("Missing MYTE_API_KEY (project key) in environment/.env");
@@ -5557,9 +5613,85 @@ async function runFeedbackMove(args) {
5557
5613
  console.log(`State: ${data.feedback_state || toState}`);
5558
5614
  if (data.event?.event_id) console.log(`Event: ${data.event.event_id}`);
5559
5615
  if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
5560
- }
5561
-
5562
- async function runFeedbackUndo(args) {
5616
+ }
5617
+
5618
+ async function runFeedbackComment(args) {
5619
+ const key = getProjectApiKey();
5620
+ if (!key) {
5621
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
5622
+ process.exit(1);
5623
+ }
5624
+ const feedbackId = resolveFeedbackIdArg(args);
5625
+ if (!feedbackId) {
5626
+ console.error("Missing --feedback-id.");
5627
+ process.exit(1);
5628
+ }
5629
+
5630
+ const content = String(await resolveFeedbackCommentContent(args, feedbackId) || "").trim();
5631
+ if (!content) {
5632
+ console.error("Missing feedback comment body. Use --body, --body-file, --stdin, or positional text.");
5633
+ process.exit(1);
5634
+ }
5635
+
5636
+ const payload = { content };
5637
+ const timeoutMs = resolveTimeoutMs(args);
5638
+ const apiBase = resolveApiBase(args);
5639
+ const clientSessionId = firstNonEmptyString(args["client-session-id"], args.clientSessionId, args.client_session_id);
5640
+ if (clientSessionId) payload.client_session_id = clientSessionId;
5641
+ const idempotencyKey = resolveProjectMutationIdempotencyKey({
5642
+ args,
5643
+ operation: `feedback_comment_create:${feedbackId}`,
5644
+ payload,
5645
+ });
5646
+
5647
+ if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
5648
+ console.log(JSON.stringify(payload, null, 2));
5649
+ return;
5650
+ }
5651
+
5652
+ let data;
5653
+ try {
5654
+ data = await postFeedbackComment({
5655
+ apiBase,
5656
+ key,
5657
+ timeoutMs,
5658
+ feedbackId,
5659
+ payload,
5660
+ idempotencyKey,
5661
+ clientSessionId,
5662
+ });
5663
+ } catch (err) {
5664
+ if (args.json) {
5665
+ console.log(JSON.stringify({ ok: false, status: err?.status || null, message: err?.message || String(err), data: err?.data || null }, null, 2));
5666
+ } else {
5667
+ console.error("Feedback comment failed:", err?.message || err);
5668
+ if (err?.data) console.error(JSON.stringify(err.data, null, 2));
5669
+ }
5670
+ process.exit(1);
5671
+ }
5672
+
5673
+ const postSync = await refreshFeedbackSnapshotAfterMutation(args, { apiBase, key, timeoutMs });
5674
+ if (args.json) {
5675
+ console.log(JSON.stringify({ ok: true, ...data, post_sync: postSync }, null, 2));
5676
+ return;
5677
+ }
5678
+
5679
+ if (data.comment_id) console.log(`Comment ID: ${data.comment_id}`);
5680
+ console.log(`Feedback: ${data.feedback_id || feedbackId}`);
5681
+ if (data.project_id) console.log(`Project ID: ${data.project_id}`);
5682
+ if (data.user_name) console.log(`Author: ${data.user_name}`);
5683
+ if (data.created_at) console.log(`Created At: ${data.created_at}`);
5684
+ if (data.content) {
5685
+ const body = String(data.content);
5686
+ const truncated = body.length > 2000 ? `${body.slice(0, 2000)}\n...[truncated]` : body;
5687
+ console.log("Content:");
5688
+ console.log(truncated);
5689
+ }
5690
+ if (postSync?.data_root) console.log(`Synced feedback: ${postSync.data_root}`);
5691
+ if (postSync?.error) console.error(`Feedback sync warning: ${postSync.error}`);
5692
+ }
5693
+
5694
+ async function runFeedbackUndo(args) {
5563
5695
  const key = getProjectApiKey();
5564
5696
  if (!key) {
5565
5697
  console.error("Missing MYTE_API_KEY (project key) in environment/.env");
@@ -5754,11 +5886,15 @@ async function runFeedback(args) {
5754
5886
  await runFeedbackReviewDecision(args);
5755
5887
  return;
5756
5888
  }
5757
- if (subcommand === "move") {
5758
- await runFeedbackMove(args);
5759
- return;
5760
- }
5761
- if (subcommand === "undo") {
5889
+ if (subcommand === "move") {
5890
+ await runFeedbackMove(args);
5891
+ return;
5892
+ }
5893
+ if (subcommand === "comment" || subcommand === "comments") {
5894
+ await runFeedbackComment(args);
5895
+ return;
5896
+ }
5897
+ if (subcommand === "undo") {
5762
5898
  await runFeedbackUndo(args);
5763
5899
  return;
5764
5900
  }
@@ -5774,7 +5910,7 @@ async function runFeedback(args) {
5774
5910
  await runFeedbackGetOrHistory(args, subcommand);
5775
5911
  return;
5776
5912
  }
5777
- 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.");
5913
+ console.error("Unknown feedback command. Use status, edit, assign, archive, submit, revise, reviews, review, move, comment, undo, prd-versions, prd-diff, get, history, validate, or apply.");
5778
5914
  process.exit(1);
5779
5915
  }
5780
5916
 
@@ -0,0 +1,11 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAi+YAnayqofu0kdLQlgO3
3
+ TWcyp6NC6U5O8/MPubmqv36Zg9yHca/PndK+yvIiFqi6ReYziM9x2xb7mPlfZ+ia
4
+ EBz7WPvd9OFUOxSngsHOAaM7jYwGzCdbJm+aJ/4NXw7kjv22Sj7FtOrJUFbA3ePW
5
+ WZIXiyl1MH144sHAG8hkPvTov/BIiEGh2G0IDUXH0n4Ksab+FQp/Z+MDRoxwhqEP
6
+ HlqJwhF6vQhfq1nFHFetaop8y0DoFe5AFXLOMdgPq90gYkVTe+9PFIx8ISi+0RyN
7
+ 6dS0sEb3Z0frLGyzDxgtJseGGCe5XWyxVfBCb/uztB395EtIB4ZWHi4Oyy9XcP3c
8
+ hPGJgJiX83tSgVO/bQzk43Yl/LIKBxh1mFa9zJACU7Gx0s4DvwBug5rZvpAUddgX
9
+ v7v/8kjN/YYLwSf+VsMZ+Vedy51dPkwyCVOUk8y1MW1vvwaLFYfqVAJ//FNjC+Jf
10
+ 51G2zxYxLXcDOFvvrra988Ng4ybfDMOy/qalWxHU0AOpAgMBAAE=
11
+ -----END PUBLIC KEY-----