@mytegroupinc/myte-core 0.0.15 → 0.0.16

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 +2 -1
  2. package/cli.js +108 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -76,7 +76,8 @@ Notes:
76
76
  - `sync-qaqc` keeps QAQC state in one deterministic file so the working set grows and shrinks with current active-mission reality.
77
77
  - `sync-qaqc` fully rewrites `MyteCommandCenter/data/qaqc.yml` on every sync and does not delete `MyteCommandCenter/data/missions/*.yml`.
78
78
  - `feedback-sync` writes one deterministic feedback snapshot under `MyteCommandCenter/data/feedback.yml`.
79
- - `feedback-sync` includes readable PRD text inline when PRD text exists.
79
+ - `feedback-sync` keeps feedback metadata plus comment turns in `MyteCommandCenter/data/feedback.yml`.
80
+ - `feedback-sync` writes full PRD context into `MyteCommandCenter/PRD/feedback-sync/*.md` and points to those files from `feedback.yml`.
80
81
  - `feedback-sync` fully replaces the feedback-owned sync file to avoid stale local feedback noise.
81
82
  - `feedback-sync` fully rewrites `MyteCommandCenter/data/feedback.yml` on every sync and does not delete `MyteCommandCenter/data/missions/*.yml`.
82
83
  - `suggestions sync` writes one merge-safe workflow file at `MyteCommandCenter/data/mission-ops.yml`.
package/cli.js CHANGED
@@ -266,7 +266,8 @@ function printHelp() {
266
266
  "",
267
267
  "feedback-sync contract:",
268
268
  " - Runs from the wrapper root that contains the project's configured repo folders",
269
- " - Writes open project feedback and inline PRD context into one deterministic file: MyteCommandCenter/data/feedback.yml",
269
+ " - Writes open project feedback metadata and conversation turns into MyteCommandCenter/data/feedback.yml",
270
+ " - Stores full PRD context in MyteCommandCenter/PRD/feedback-sync/*.md and points to those files from feedback.yml",
270
271
  "",
271
272
  "Options:",
272
273
  " --with-diff Include deterministic git diffs (project-scoped)",
@@ -290,7 +291,7 @@ function printHelp() {
290
291
  " --target-contact-ids Comma-separated client contact ObjectIds",
291
292
  " --status <value> Feedback status filter for feedback-sync (default: Pending)",
292
293
  " --source <value> Feedback source filter for feedback-sync",
293
- " --with-prd-text Include extracted PRD text in feedback-sync (default: on)",
294
+ " --with-prd-text Include extracted PRD text so local PRD files can be materialized during feedback-sync (default: on)",
294
295
  " --mission-ids <ids> Comma-separated mission business ids for run-qaqc (quote multi-id values on PowerShell)",
295
296
  " --actor-scope <id> Actor workspace key inside mission-ops.yml (defaults to machine-cwd slug)",
296
297
  " --wait Poll batch status until terminal completion for run-qaqc",
@@ -1082,6 +1083,9 @@ async function fetchFeedbackSyncSnapshot({ apiBase, key, timeoutMs, filters = {}
1082
1083
  if (filters.includePrdText !== undefined) {
1083
1084
  url.searchParams.set("include_prd_text", filters.includePrdText ? "true" : "false");
1084
1085
  }
1086
+ if (filters.includeCommentTurns !== undefined) {
1087
+ url.searchParams.set("include_comment_turns", filters.includeCommentTurns ? "true" : "false");
1088
+ }
1085
1089
  const { resp, body } = await fetchJsonWithTimeout(
1086
1090
  fetchFn,
1087
1091
  url.toString(),
@@ -1704,6 +1708,44 @@ function writeTextFile(filePath, value) {
1704
1708
  fs.writeFileSync(filePath, String(value || ""), "utf8");
1705
1709
  }
1706
1710
 
1711
+ function sanitizeFileSegment(value, fallback = "item") {
1712
+ const cleaned = String(value || "")
1713
+ .trim()
1714
+ .replace(/[<>:"/\\|?*\x00-\x1F]/g, "-")
1715
+ .replace(/\s+/g, "-")
1716
+ .replace(/-+/g, "-")
1717
+ .replace(/^\.+|\.+$/g, "")
1718
+ .replace(/^-+|-+$/g, "");
1719
+ return cleaned || fallback;
1720
+ }
1721
+
1722
+ function toPosixRelativePath(rootPath, targetPath) {
1723
+ return path.relative(rootPath, targetPath).split(path.sep).join("/");
1724
+ }
1725
+
1726
+ function ensureTrailingNewline(value) {
1727
+ const text = String(value || "");
1728
+ if (!text) return "";
1729
+ return text.endsWith("\n") ? text : `${text}\n`;
1730
+ }
1731
+
1732
+ function normalizeFeedbackConversationTurns(turns) {
1733
+ if (!Array.isArray(turns)) return [];
1734
+ return turns
1735
+ .map((turn) => {
1736
+ const content = String(turn?.content || "").trim();
1737
+ if (!content) return null;
1738
+ const normalized = {
1739
+ sender_name: firstNonEmptyString(turn?.sender_name, turn?.user_name, turn?.author_name) || "Unknown",
1740
+ content,
1741
+ };
1742
+ const createdAt = firstNonEmptyString(turn?.created_at, turn?.timestamp);
1743
+ if (createdAt) normalized.created_at = createdAt;
1744
+ return normalized;
1745
+ })
1746
+ .filter(Boolean);
1747
+ }
1748
+
1707
1749
  function readJsonFile(filePath) {
1708
1750
  if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return null;
1709
1751
  try {
@@ -1921,8 +1963,11 @@ function writeQaqcSnapshot({ snapshot, wrapperRoot, outputDir }) {
1921
1963
 
1922
1964
  function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
1923
1965
  const { targetRoot, dataRoot } = resolveCommandCenterRoots(wrapperRoot, outputDir);
1966
+ const prdSyncDir = path.join(targetRoot, "PRD", "feedback-sync");
1924
1967
 
1925
1968
  ensureDir(dataRoot);
1969
+ ensureDir(prdSyncDir);
1970
+ clearFileDirectory(prdSyncDir, [".md", ".markdown", ".txt"]);
1926
1971
  pruneLegacyCommandCenterArtifacts(dataRoot, { bootstrap: true, feedback: true });
1927
1972
 
1928
1973
  if (snapshot.project && typeof snapshot.project === "object") {
@@ -1930,15 +1975,58 @@ function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
1930
1975
  }
1931
1976
 
1932
1977
  const items = Array.isArray(snapshot.items) ? snapshot.items : [];
1978
+ let prdFileCount = 0;
1979
+ const materializedItems = items.map((rawItem, index) => {
1980
+ const item = isPlainObject(rawItem) ? { ...rawItem } : {};
1981
+ const feedbackId = stableItemId(item, ["feedback_id", "id"], `F${String(index + 1).padStart(3, "0")}`);
1982
+ const conversationTurns = normalizeFeedbackConversationTurns(item.conversation_turns);
1983
+ const prdText = String(item.prd_text || "").trim();
1984
+
1985
+ let contextSource = "description_only";
1986
+ let contextNote = "No separate PRD. Use feedback_text as the context for this feedback item.";
1987
+ let prdFile = null;
1988
+
1989
+ if (prdText) {
1990
+ const prdFilename = `${sanitizeFileSegment(feedbackId, `feedback-${index + 1}`)}.md`;
1991
+ const prdPath = path.join(prdSyncDir, prdFilename);
1992
+ writeTextFile(prdPath, ensureTrailingNewline(prdText));
1993
+ prdFile = toPosixRelativePath(targetRoot, prdPath);
1994
+ contextSource = "prd_file";
1995
+ contextNote = "Full PRD context is stored in the linked file.";
1996
+ prdFileCount += 1;
1997
+ } else if (item.has_prd_text) {
1998
+ contextSource = "prd_declared_but_unavailable";
1999
+ contextNote = "A separate PRD exists for this feedback item, but readable PRD text was not included in this sync snapshot.";
2000
+ }
2001
+
2002
+ return {
2003
+ ...item,
2004
+ feedback_id: feedbackId,
2005
+ conversation_turns: conversationTurns,
2006
+ context_source: contextSource,
2007
+ context_note: contextNote,
2008
+ prd_file: prdFile,
2009
+ };
2010
+ });
2011
+ const snapshotCounts = snapshot.counts && typeof snapshot.counts === "object"
2012
+ ? { ...snapshot.counts }
2013
+ : { total_feedback: materializedItems.length };
2014
+ snapshotCounts.with_prd_files = prdFileCount;
2015
+ if (snapshotCounts.with_conversation_turns === undefined) {
2016
+ snapshotCounts.with_conversation_turns = materializedItems.filter((item) => Array.isArray(item?.conversation_turns) && item.conversation_turns.length > 0).length;
2017
+ }
2018
+
1933
2019
  const payload = scrubBootstrapValue({
1934
- schema_version: snapshot.schema_version || 1,
2020
+ schema_version: snapshot.schema_version || 2,
1935
2021
  project: snapshot.project || null,
1936
2022
  repo_names: Array.isArray(snapshot.repo_names) ? snapshot.repo_names : [],
1937
2023
  filters: snapshot.filters && typeof snapshot.filters === "object" ? snapshot.filters : {},
1938
- counts: snapshot.counts && typeof snapshot.counts === "object"
1939
- ? snapshot.counts
1940
- : { total_feedback: items.length },
1941
- queue: items
2024
+ counts: snapshotCounts,
2025
+ artifacts: {
2026
+ feedback_prd_root: "PRD/feedback-sync",
2027
+ with_prd_files: prdFileCount,
2028
+ },
2029
+ queue: materializedItems
1942
2030
  .filter((item) => String(item?.status || "").trim().toLowerCase() !== "resolved")
1943
2031
  .map((item, index) => ({
1944
2032
  feedback_id: stableItemId(item, ["feedback_id", "id"], `F${String(index + 1).padStart(3, "0")}`),
@@ -1946,7 +2034,11 @@ function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
1946
2034
  priority: item?.priority || null,
1947
2035
  title: item?.title || null,
1948
2036
  })),
1949
- items,
2037
+ items: materializedItems.map((item) => {
2038
+ const nextItem = { ...item };
2039
+ delete nextItem.prd_text;
2040
+ return nextItem;
2041
+ }),
1950
2042
  pagination: snapshot.pagination && typeof snapshot.pagination === "object" ? snapshot.pagination : undefined,
1951
2043
  generated_at: snapshot.generated_at || null,
1952
2044
  snapshot_hash: snapshot.snapshot_hash || null,
@@ -1956,6 +2048,7 @@ function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
1956
2048
  return {
1957
2049
  targetRoot,
1958
2050
  dataRoot,
2051
+ prdSyncDir,
1959
2052
  manifest: payload,
1960
2053
  };
1961
2054
  }
@@ -3024,6 +3117,7 @@ async function runFeedbackSync(args) {
3024
3117
  status: firstNonEmptyString(args.status) || "Pending",
3025
3118
  source: firstNonEmptyString(args.source) || "",
3026
3119
  includePrdText,
3120
+ includeCommentTurns: true,
3027
3121
  };
3028
3122
 
3029
3123
  let snapshot;
@@ -3079,7 +3173,7 @@ async function runFeedbackSync(args) {
3079
3173
  console.log(`Configured repos: ${summary.repo_names.join(", ") || "(none)"}`);
3080
3174
  console.log(`Found locally: ${summary.local.found.join(", ") || "(none)"}`);
3081
3175
  if (summary.local.missing.length) console.log(`Missing locally: ${summary.local.missing.join(", ")}`);
3082
- console.log(`Counts: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}`);
3176
+ console.log(`Counts: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}, with_conversation_turns=${summary.counts.with_conversation_turns || 0}`);
3083
3177
  console.log("Dry run only - no files written.");
3084
3178
  }
3085
3179
  return;
@@ -3087,6 +3181,9 @@ async function runFeedbackSync(args) {
3087
3181
 
3088
3182
  const writeResult = writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir });
3089
3183
  summary.data_root = writeResult.dataRoot;
3184
+ summary.prd_root = writeResult.prdSyncDir;
3185
+ summary.counts = writeResult.manifest?.counts || summary.counts;
3186
+ summary.artifacts = writeResult.manifest?.artifacts || null;
3090
3187
 
3091
3188
  if (args.json) {
3092
3189
  console.log(JSON.stringify(summary, null, 2));
@@ -3099,7 +3196,8 @@ async function runFeedbackSync(args) {
3099
3196
  console.log(`Configured repos: ${summary.repo_names.join(", ") || "(none)"}`);
3100
3197
  console.log(`Found locally: ${summary.local.found.join(", ") || "(none)"}`);
3101
3198
  if (summary.local.missing.length) console.log(`Missing locally: ${summary.local.missing.join(", ")}`);
3102
- console.log(`Wrote feedback: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}`);
3199
+ console.log(`Wrote feedback: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}, with_conversation_turns=${summary.counts.with_conversation_turns || 0}`);
3200
+ console.log(`PRD root: ${summary.prd_root}`);
3103
3201
  console.log(`Snapshot: ${summary.snapshot_hash || "n/a"}`);
3104
3202
  }
3105
3203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytegroupinc/myte-core",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Myte CLI core implementation (Project Assistant + Myte AI gateway).",
5
5
  "type": "commonjs",
6
6
  "main": "cli.js",