@mytegroupinc/myte-core 0.0.7 → 0.0.8
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 +13 -0
- package/cli.js +316 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,19 +5,25 @@ Internal implementation package for the `myte` CLI.
|
|
|
5
5
|
Most users should install the unscoped wrapper instead:
|
|
6
6
|
- `npm install myte` then `npx myte bootstrap`
|
|
7
7
|
- `npm install myte` then `npx myte sync-qaqc`
|
|
8
|
+
- `npm install myte` then `npx myte feedback-sync`
|
|
8
9
|
- `npm install myte` then `npx myte query "..." --with-diff`
|
|
9
10
|
- `npm install myte` then `npm exec myte -- query "..." --with-diff`
|
|
10
11
|
- `npm install myte` then `npx myte update-team "Backend deploy completed; QAQC rerun queued."`
|
|
12
|
+
- `npm install myte` then `npx myte update-owner --subject "QAQC progress" --body-file ./updates/owner.md`
|
|
11
13
|
- `npm install myte` then `npx myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
|
|
12
14
|
- `npm i -g myte` then `myte bootstrap`
|
|
13
15
|
- `npm i -g myte` then `myte sync-qaqc`
|
|
16
|
+
- `npm i -g myte` then `myte feedback-sync`
|
|
14
17
|
- `npm i -g myte` then `myte query "..." --with-diff`
|
|
15
18
|
- `npm i -g myte` then `myte update-team "Backend deploy completed; QAQC rerun queued."`
|
|
19
|
+
- `npm i -g myte` then `myte update-owner --subject "QAQC progress" --body-file ./updates/owner.md`
|
|
16
20
|
- `npm i -g myte` then `myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
|
|
17
21
|
- `npx myte@latest bootstrap`
|
|
18
22
|
- `npx myte@latest sync-qaqc`
|
|
23
|
+
- `npx myte@latest feedback-sync`
|
|
19
24
|
- `npx myte@latest query "..." --with-diff`
|
|
20
25
|
- `npx myte@latest update-team "Backend deploy completed; QAQC rerun queued."`
|
|
26
|
+
- `npx myte@latest update-owner --subject "QAQC progress" --body-file ./updates/owner.md`
|
|
21
27
|
- `npx myte@latest update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
|
|
22
28
|
- `npm install myte` then `npx myte create-prd ./drafts/auth-prd.md`
|
|
23
29
|
- `cat ./drafts/auth-prd.md | npx myte create-prd --stdin`
|
|
@@ -41,8 +47,13 @@ Notes:
|
|
|
41
47
|
- `sync-qaqc` writes active mission QAQC cards to `MyteCommandCenter/data/qaqc/active-missions` and refreshes matching `MyteCommandCenter/data/missions` cards.
|
|
42
48
|
- `sync-qaqc` only exports active `Todo` / `In Progress` missions plus a public QAQC summary and sanitized latest batch metadata.
|
|
43
49
|
- `sync-qaqc` removes previously QAQC-managed mission files from `MyteCommandCenter/data/missions` once they leave the active set.
|
|
50
|
+
- `feedback-sync` writes public feedback cards under `MyteCommandCenter/data/feedback/items`.
|
|
51
|
+
- `feedback-sync` writes readable PRD copies under `MyteCommandCenter/data/feedback/prds` when PRD text exists.
|
|
52
|
+
- `feedback-sync` fully replaces the feedback-owned sync folders to avoid stale local feedback noise.
|
|
44
53
|
- `create-prd` is a deterministic PRD upload path, not an LLM generation command.
|
|
45
54
|
- `update-team` creates a project comment through `/api/project-assistant/project-comment`.
|
|
55
|
+
- `update-owner` sends a direct owner email through `/api/project-assistant/update-owner`.
|
|
56
|
+
- `update-owner` requires `--subject` plus body markdown from `--body-markdown`, `--body-file`, positional input, or `--stdin`.
|
|
46
57
|
- `update-client` creates a client update draft through `/api/project-assistant/client-update-drafts`.
|
|
47
58
|
- `update-client` requires `--subject` plus body markdown from `--body-markdown`, `--body-file`, positional input, or `--stdin`.
|
|
48
59
|
- `update-client` accepts optional `--target-contact-id` repeats or `--target-contact-ids <id1,id2>`.
|
|
@@ -64,11 +75,13 @@ Deterministic `create-prd` contract:
|
|
|
64
75
|
Examples:
|
|
65
76
|
- `npx myte bootstrap`
|
|
66
77
|
- `npx myte sync-qaqc`
|
|
78
|
+
- `npx myte feedback-sync`
|
|
67
79
|
- `npx myte bootstrap --dry-run --json`
|
|
68
80
|
- `npx myte sync-qaqc --dry-run --json`
|
|
69
81
|
- `npx myte create-prd ./drafts/auth-prd.md --description "Short card summary"`
|
|
70
82
|
- `npx myte create-prd ./drafts/auth-prd.md --print-context`
|
|
71
83
|
- `npx myte update-team "Backend deploy completed; QAQC rerun queued."`
|
|
84
|
+
- `npx myte update-owner --subject "QAQC progress" --body-file ./updates/owner.md`
|
|
72
85
|
- `npx myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
|
|
73
86
|
|
|
74
87
|
This package is published under the org scope for governance; the public `myte` wrapper delegates here.
|
package/cli.js
CHANGED
|
@@ -61,7 +61,9 @@ function splitCommand(argv) {
|
|
|
61
61
|
"add-prd",
|
|
62
62
|
"prd",
|
|
63
63
|
"update-team",
|
|
64
|
+
"update-owner",
|
|
64
65
|
"update-client",
|
|
66
|
+
"feedback-sync",
|
|
65
67
|
"help",
|
|
66
68
|
"--help",
|
|
67
69
|
"-h",
|
|
@@ -78,7 +80,7 @@ function parseArgs(argv) {
|
|
|
78
80
|
try {
|
|
79
81
|
// eslint-disable-next-line global-require
|
|
80
82
|
return require("minimist")(argv, {
|
|
81
|
-
boolean: ["with-diff", "diff", "print-context", "dry-run", "fetch", "json", "stdin"],
|
|
83
|
+
boolean: ["with-diff", "diff", "print-context", "dry-run", "fetch", "json", "stdin", "with-prd-text"],
|
|
82
84
|
string: [
|
|
83
85
|
"query",
|
|
84
86
|
"q",
|
|
@@ -97,6 +99,8 @@ function parseArgs(argv) {
|
|
|
97
99
|
"body-file",
|
|
98
100
|
"target-contact-id",
|
|
99
101
|
"target-contact-ids",
|
|
102
|
+
"status",
|
|
103
|
+
"source",
|
|
100
104
|
],
|
|
101
105
|
alias: {
|
|
102
106
|
q: "query",
|
|
@@ -153,11 +157,14 @@ function printHelp() {
|
|
|
153
157
|
" myte bootstrap [--output-dir ./MyteCommandCenter] [--json]",
|
|
154
158
|
" myte sync-qaqc [--output-dir ./MyteCommandCenter] [--json]",
|
|
155
159
|
" myte update-team \"<content>\" [--json]",
|
|
160
|
+
" myte update-owner --subject \"<text>\" [--body-markdown \"...\"] [--body-file ./update.md] [--json]",
|
|
156
161
|
" myte update-client --subject \"<text>\" [--body-markdown \"...\"] [--body-file ./update.md] [--target-contact-ids <id1,id2>] [--json]",
|
|
162
|
+
" myte feedback-sync [--status Pending] [--source User] [--output-dir ./MyteCommandCenter] [--json]",
|
|
157
163
|
" myte chat",
|
|
158
164
|
" myte create-prd <file.md> [--json] [--title \"...\"] [--description \"...\"]",
|
|
159
165
|
" myte add-prd <file.md> [--json]",
|
|
160
166
|
" cat file.md | myte create-prd --stdin [--title \"...\"] [--description \"...\"]",
|
|
167
|
+
" cat update.md | myte update-owner --stdin --subject \"Owner update\"",
|
|
161
168
|
" cat update.md | myte update-client --stdin --subject \"Weekly client update\"",
|
|
162
169
|
"",
|
|
163
170
|
"Run forms:",
|
|
@@ -190,11 +197,21 @@ function printHelp() {
|
|
|
190
197
|
" - Creates a project team comment through /api/project-assistant/project-comment",
|
|
191
198
|
" - Required: content (inline, --content, or --stdin)",
|
|
192
199
|
"",
|
|
200
|
+
"update-owner contract:",
|
|
201
|
+
" - Sends a direct owner email through /api/project-assistant/update-owner",
|
|
202
|
+
" - Required: --subject and body markdown (via --body-markdown, --body-file, positional file/text, or --stdin)",
|
|
203
|
+
" - Uses Myte SMTP, resolves the owner from the project, and stores a durable send record",
|
|
204
|
+
"",
|
|
193
205
|
"update-client contract:",
|
|
194
206
|
" - Creates a client update draft through /api/project-assistant/client-update-drafts",
|
|
195
207
|
" - Required: --subject and body markdown (via --body-markdown, --body-file, positional file/text, or --stdin)",
|
|
196
208
|
" - Optional: --target-contact-id <id> (repeatable) or --target-contact-ids <id1,id2>",
|
|
197
209
|
"",
|
|
210
|
+
"feedback-sync contract:",
|
|
211
|
+
" - Runs from the wrapper root that contains the project's configured repo folders",
|
|
212
|
+
" - Writes project feedback cards under MyteCommandCenter/data/feedback/items",
|
|
213
|
+
" - Writes PRD text copies under MyteCommandCenter/data/feedback/prds when available",
|
|
214
|
+
"",
|
|
198
215
|
"Options:",
|
|
199
216
|
" --with-diff Include deterministic git diffs (project-scoped)",
|
|
200
217
|
" --diff-limit <chars> Truncate diff context to N chars (default: 200000)",
|
|
@@ -205,11 +222,14 @@ function printHelp() {
|
|
|
205
222
|
" --title <text> Override PRD title for raw markdown uploads",
|
|
206
223
|
" --description <text> Set feedback description/card summary for raw markdown uploads",
|
|
207
224
|
" --content <text> Team update content for update-team",
|
|
208
|
-
" --subject <text>
|
|
209
|
-
" --body-markdown <md>
|
|
210
|
-
" --body-file <path> Read
|
|
225
|
+
" --subject <text> Subject for update-owner or update-client",
|
|
226
|
+
" --body-markdown <md> Markdown body for update-owner or update-client",
|
|
227
|
+
" --body-file <path> Read update-owner or update-client markdown body from a file",
|
|
211
228
|
" --target-contact-id Add one client contact ObjectId (repeatable)",
|
|
212
229
|
" --target-contact-ids Comma-separated client contact ObjectIds",
|
|
230
|
+
" --status <value> Feedback status filter for feedback-sync (default: Pending)",
|
|
231
|
+
" --source <value> Feedback source filter for feedback-sync",
|
|
232
|
+
" --with-prd-text Include extracted PRD text in feedback-sync (default: on)",
|
|
213
233
|
" --print-context Print JSON payload and exit (no query call)",
|
|
214
234
|
" --no-fetch Don't git fetch origin main/master before diff",
|
|
215
235
|
"",
|
|
@@ -219,7 +239,9 @@ function printHelp() {
|
|
|
219
239
|
" myte bootstrap --output-dir ./MyteCommandCenter",
|
|
220
240
|
" myte sync-qaqc",
|
|
221
241
|
" myte update-team \"Backend deploy completed; QAQC rerun queued.\"",
|
|
242
|
+
" myte update-owner --subject \"QAQC progress\" --body-file ./updates/owner.md",
|
|
222
243
|
" myte update-client --subject \"Weekly client update\" --body-file ./updates/week-12.md",
|
|
244
|
+
" myte feedback-sync --status Pending --source User",
|
|
223
245
|
" myte update-client --subject \"Weekly client update\" --body-markdown \"## Progress\\n- Login complete\" --target-contact-ids 507f1f77bcf86cd799439011,507f1f77bcf86cd799439012",
|
|
224
246
|
" myte create-prd ./drafts/auth-prd.md --description \"Short card summary\"",
|
|
225
247
|
" cat ./drafts/auth-prd.md | myte create-prd --stdin",
|
|
@@ -377,6 +399,15 @@ async function resolveClientUpdateBody(args) {
|
|
|
377
399
|
process.exit(1);
|
|
378
400
|
}
|
|
379
401
|
|
|
402
|
+
async function resolveOwnerUpdateBody(args) {
|
|
403
|
+
const body = await resolveClientUpdateBody(args);
|
|
404
|
+
if (!body) {
|
|
405
|
+
console.error("Owner update body_markdown is empty.");
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
return body;
|
|
409
|
+
}
|
|
410
|
+
|
|
380
411
|
function resolveTargetContactIds(args) {
|
|
381
412
|
return dedupeStrings(
|
|
382
413
|
parseCsvValues(
|
|
@@ -951,6 +982,33 @@ async function fetchQaqcSyncSnapshot({ apiBase, key, timeoutMs }) {
|
|
|
951
982
|
return body.data || {};
|
|
952
983
|
}
|
|
953
984
|
|
|
985
|
+
async function fetchFeedbackSyncSnapshot({ apiBase, key, timeoutMs, filters = {} }) {
|
|
986
|
+
const fetchFn = await getFetch();
|
|
987
|
+
const url = new URL(`${apiBase}/project-assistant/feedback-sync`);
|
|
988
|
+
if (filters.status) url.searchParams.set("status", String(filters.status));
|
|
989
|
+
if (filters.source) url.searchParams.set("source", String(filters.source));
|
|
990
|
+
if (filters.includePrdText !== undefined) {
|
|
991
|
+
url.searchParams.set("include_prd_text", filters.includePrdText ? "true" : "false");
|
|
992
|
+
}
|
|
993
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
994
|
+
fetchFn,
|
|
995
|
+
url.toString(),
|
|
996
|
+
{
|
|
997
|
+
method: "GET",
|
|
998
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
999
|
+
},
|
|
1000
|
+
timeoutMs
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
if (!resp.ok || body.status !== "success") {
|
|
1004
|
+
const msg = body?.message || `Feedback sync request failed (${resp.status})`;
|
|
1005
|
+
const err = new Error(msg);
|
|
1006
|
+
err.status = resp.status;
|
|
1007
|
+
throw err;
|
|
1008
|
+
}
|
|
1009
|
+
return body.data || {};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
954
1012
|
async function callAssistantQuery({ apiBase, key, payload, timeoutMs, endpoint = "/project-assistant/query" }) {
|
|
955
1013
|
const fetchFn = await getFetch();
|
|
956
1014
|
const url = `${apiBase}${endpoint}`;
|
|
@@ -1042,6 +1100,74 @@ async function runUpdateTeam(args) {
|
|
|
1042
1100
|
}
|
|
1043
1101
|
}
|
|
1044
1102
|
|
|
1103
|
+
async function runUpdateOwner(args) {
|
|
1104
|
+
const key = getProjectApiKey();
|
|
1105
|
+
if (!key) {
|
|
1106
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
1107
|
+
process.exit(1);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const subject = firstNonEmptyString(args.subject);
|
|
1111
|
+
if (!subject) {
|
|
1112
|
+
console.error("Missing --subject for owner update.");
|
|
1113
|
+
printHelp();
|
|
1114
|
+
process.exit(1);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const payload = {
|
|
1118
|
+
subject,
|
|
1119
|
+
body_markdown: await resolveOwnerUpdateBody(args),
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
|
|
1123
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
1128
|
+
const apiBase = resolveApiBase(args);
|
|
1129
|
+
|
|
1130
|
+
let data;
|
|
1131
|
+
try {
|
|
1132
|
+
data = await callAssistantQuery({
|
|
1133
|
+
apiBase,
|
|
1134
|
+
key,
|
|
1135
|
+
payload,
|
|
1136
|
+
timeoutMs,
|
|
1137
|
+
endpoint: "/project-assistant/update-owner",
|
|
1138
|
+
});
|
|
1139
|
+
} catch (err) {
|
|
1140
|
+
if (err?.name === "AbortError") {
|
|
1141
|
+
console.error(`Request timed out after ${timeoutMs}ms`);
|
|
1142
|
+
} else {
|
|
1143
|
+
console.error("Owner update failed:", err?.message || err);
|
|
1144
|
+
}
|
|
1145
|
+
process.exit(1);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
if (args.json) {
|
|
1149
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if (data.update_id) console.log(`Update ID: ${data.update_id}`);
|
|
1154
|
+
if (data.project_title) console.log(`Project: ${data.project_title}`);
|
|
1155
|
+
if (data.project_id) console.log(`Project ID: ${data.project_id}`);
|
|
1156
|
+
if (data.status) console.log(`Status: ${data.status}`);
|
|
1157
|
+
if (data.subject) console.log(`Subject: ${data.subject}`);
|
|
1158
|
+
if (data.owner_name || data.owner_email) {
|
|
1159
|
+
console.log(`Owner: ${data.owner_name || data.owner_email}${data.owner_email && data.owner_name ? ` <${data.owner_email}>` : ""}`);
|
|
1160
|
+
}
|
|
1161
|
+
if (data.sender_name || data.sender_email) {
|
|
1162
|
+
console.log(`Sender: ${data.sender_name || data.sender_email}${data.sender_email && data.sender_name ? ` <${data.sender_email}>` : ""}`);
|
|
1163
|
+
}
|
|
1164
|
+
if (data.sent_at) console.log(`Sent At: ${data.sent_at}`);
|
|
1165
|
+
if (data.snippet) {
|
|
1166
|
+
console.log("Snippet:");
|
|
1167
|
+
console.log(data.snippet);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1045
1171
|
async function runUpdateClient(args) {
|
|
1046
1172
|
const key = getProjectApiKey();
|
|
1047
1173
|
if (!key) {
|
|
@@ -1125,6 +1251,20 @@ function clearYamlDirectory(dirPath) {
|
|
|
1125
1251
|
}
|
|
1126
1252
|
}
|
|
1127
1253
|
|
|
1254
|
+
function clearFileDirectory(dirPath, extensions) {
|
|
1255
|
+
const normalized = new Set((Array.isArray(extensions) ? extensions : []).map((ext) => String(ext || "").toLowerCase()));
|
|
1256
|
+
if (!fs.existsSync(dirPath)) {
|
|
1257
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
1261
|
+
if (!entry.isFile()) continue;
|
|
1262
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
1263
|
+
if (normalized.size && !normalized.has(ext)) continue;
|
|
1264
|
+
fs.rmSync(path.join(dirPath, entry.name), { force: true });
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1128
1268
|
function stableItemId(item, keys, fallback) {
|
|
1129
1269
|
for (const key of keys) {
|
|
1130
1270
|
const value = String(item?.[key] || "").trim();
|
|
@@ -1149,6 +1289,11 @@ function writeJsonFile(filePath, value) {
|
|
|
1149
1289
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
1150
1290
|
}
|
|
1151
1291
|
|
|
1292
|
+
function writeTextFile(filePath, value) {
|
|
1293
|
+
ensureDir(path.dirname(filePath));
|
|
1294
|
+
fs.writeFileSync(filePath, String(value || ""), "utf8");
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1152
1297
|
function readJsonFile(filePath) {
|
|
1153
1298
|
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return null;
|
|
1154
1299
|
try {
|
|
@@ -1324,6 +1469,66 @@ function writeQaqcSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
|
1324
1469
|
};
|
|
1325
1470
|
}
|
|
1326
1471
|
|
|
1472
|
+
function writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
1473
|
+
const targetRoot = outputDir
|
|
1474
|
+
? path.resolve(process.cwd(), String(outputDir))
|
|
1475
|
+
: path.join(wrapperRoot, "MyteCommandCenter");
|
|
1476
|
+
const dataRoot = path.join(targetRoot, "data");
|
|
1477
|
+
const feedbackRoot = path.join(dataRoot, "feedback");
|
|
1478
|
+
const itemsDir = path.join(feedbackRoot, "items");
|
|
1479
|
+
const prdsDir = path.join(feedbackRoot, "prds");
|
|
1480
|
+
|
|
1481
|
+
ensureDir(dataRoot);
|
|
1482
|
+
ensureDir(feedbackRoot);
|
|
1483
|
+
clearYamlDirectory(itemsDir);
|
|
1484
|
+
clearFileDirectory(prdsDir, [".md"]);
|
|
1485
|
+
|
|
1486
|
+
const items = Array.isArray(snapshot.items) ? snapshot.items.map((item) => scrubBootstrapValue(item)) : [];
|
|
1487
|
+
const itemIds = [];
|
|
1488
|
+
let prdCount = 0;
|
|
1489
|
+
|
|
1490
|
+
if (snapshot.project && typeof snapshot.project === "object") {
|
|
1491
|
+
writeYamlFile(path.join(dataRoot, "project.yml"), scrubBootstrapValue(snapshot.project));
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
items.forEach((item, index) => {
|
|
1495
|
+
const feedbackId = stableItemId(item, ["feedback_id", "id"], `F${String(index + 1).padStart(3, "0")}`);
|
|
1496
|
+
itemIds.push(feedbackId);
|
|
1497
|
+
const card = scrubBootstrapValue(item);
|
|
1498
|
+
const prdText = String(card.prd_text || "").trim();
|
|
1499
|
+
if (prdText) {
|
|
1500
|
+
delete card.prd_text;
|
|
1501
|
+
card.prd_file = `prds/${feedbackId}.md`;
|
|
1502
|
+
writeTextFile(path.join(prdsDir, `${feedbackId}.md`), `${prdText}\n`);
|
|
1503
|
+
prdCount += 1;
|
|
1504
|
+
}
|
|
1505
|
+
writeYamlFile(path.join(itemsDir, `${feedbackId}.yml`), card);
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
const manifest = {
|
|
1509
|
+
schema_version: snapshot.schema_version || 1,
|
|
1510
|
+
generated_at: snapshot.generated_at || null,
|
|
1511
|
+
snapshot_hash: snapshot.snapshot_hash || null,
|
|
1512
|
+
project: snapshot.project ? scrubBootstrapValue(snapshot.project) : null,
|
|
1513
|
+
repo_names: Array.isArray(snapshot.repo_names) ? snapshot.repo_names : [],
|
|
1514
|
+
filters: snapshot.filters && typeof snapshot.filters === "object" ? scrubBootstrapValue(snapshot.filters) : {},
|
|
1515
|
+
item_ids: itemIds,
|
|
1516
|
+
counts: snapshot.counts && typeof snapshot.counts === "object"
|
|
1517
|
+
? scrubBootstrapValue(snapshot.counts)
|
|
1518
|
+
: { total_feedback: items.length, with_prd_text: prdCount },
|
|
1519
|
+
};
|
|
1520
|
+
if (snapshot.pagination && typeof snapshot.pagination === "object") {
|
|
1521
|
+
manifest.pagination = scrubBootstrapValue(snapshot.pagination);
|
|
1522
|
+
}
|
|
1523
|
+
writeJsonFile(path.join(feedbackRoot, "manifest.json"), manifest);
|
|
1524
|
+
|
|
1525
|
+
return {
|
|
1526
|
+
targetRoot,
|
|
1527
|
+
dataRoot,
|
|
1528
|
+
manifest,
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1327
1532
|
async function runCreatePrd(args) {
|
|
1328
1533
|
const key = (process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY || "").trim();
|
|
1329
1534
|
if (!key) {
|
|
@@ -1667,6 +1872,103 @@ async function runSyncQaqc(args) {
|
|
|
1667
1872
|
console.log(`Snapshot: ${summary.snapshot_hash || "n/a"}`);
|
|
1668
1873
|
}
|
|
1669
1874
|
|
|
1875
|
+
async function runFeedbackSync(args) {
|
|
1876
|
+
const key = getProjectApiKey();
|
|
1877
|
+
if (!key) {
|
|
1878
|
+
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
1879
|
+
process.exit(1);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
const timeoutMs = resolveTimeoutMs(args);
|
|
1883
|
+
const apiBase = resolveApiBase(args);
|
|
1884
|
+
const includePrdText = args["with-prd-text"] !== undefined
|
|
1885
|
+
? Boolean(args["with-prd-text"])
|
|
1886
|
+
: args.withPrdText !== undefined
|
|
1887
|
+
? Boolean(args.withPrdText)
|
|
1888
|
+
: true;
|
|
1889
|
+
const filters = {
|
|
1890
|
+
status: firstNonEmptyString(args.status) || "Pending",
|
|
1891
|
+
source: firstNonEmptyString(args.source) || "",
|
|
1892
|
+
includePrdText,
|
|
1893
|
+
};
|
|
1894
|
+
|
|
1895
|
+
let snapshot;
|
|
1896
|
+
try {
|
|
1897
|
+
snapshot = await fetchFeedbackSyncSnapshot({ apiBase, key, timeoutMs, filters });
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
console.error("Failed to fetch feedback sync snapshot:", err?.message || err);
|
|
1900
|
+
process.exit(1);
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
if (args["print-context"] || args.printContext) {
|
|
1904
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
let resolved;
|
|
1909
|
+
try {
|
|
1910
|
+
resolved = resolveBootstrapWorkspace(snapshot.repo_names || []);
|
|
1911
|
+
} catch (err) {
|
|
1912
|
+
console.error(err?.message || err);
|
|
1913
|
+
process.exit(1);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
const wrapperRoot = resolved.root;
|
|
1917
|
+
const outputDir = args["output-dir"] || args.outputDir || args.output_dir;
|
|
1918
|
+
const dryRun = Boolean(args["dry-run"] || args.dryRun);
|
|
1919
|
+
const counts = snapshot.counts && typeof snapshot.counts === "object" ? snapshot.counts : {};
|
|
1920
|
+
const summary = {
|
|
1921
|
+
api_base: apiBase,
|
|
1922
|
+
project_id: snapshot?.project?.id || null,
|
|
1923
|
+
wrapper_root: wrapperRoot,
|
|
1924
|
+
output_root: outputDir ? path.resolve(process.cwd(), String(outputDir)) : path.join(wrapperRoot, "MyteCommandCenter"),
|
|
1925
|
+
repo_names: Array.isArray(snapshot.repo_names) ? snapshot.repo_names : [],
|
|
1926
|
+
local: {
|
|
1927
|
+
mode: resolved.mode,
|
|
1928
|
+
found: (resolved.repos || []).map((repo) => repo.name),
|
|
1929
|
+
missing: resolved.missing || [],
|
|
1930
|
+
},
|
|
1931
|
+
filters: snapshot.filters && typeof snapshot.filters === "object" ? snapshot.filters : filters,
|
|
1932
|
+
counts,
|
|
1933
|
+
snapshot_hash: snapshot.snapshot_hash || null,
|
|
1934
|
+
generated_at: snapshot.generated_at || null,
|
|
1935
|
+
dry_run: dryRun,
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
if (dryRun) {
|
|
1939
|
+
if (args.json) {
|
|
1940
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
1941
|
+
} else {
|
|
1942
|
+
console.log(`Project: ${summary.project_id || "(unknown)"}`);
|
|
1943
|
+
console.log(`Wrapper root: ${summary.wrapper_root}`);
|
|
1944
|
+
console.log(`Output root: ${summary.output_root}`);
|
|
1945
|
+
console.log(`Configured repos: ${summary.repo_names.join(", ") || "(none)"}`);
|
|
1946
|
+
console.log(`Found locally: ${summary.local.found.join(", ") || "(none)"}`);
|
|
1947
|
+
if (summary.local.missing.length) console.log(`Missing locally: ${summary.local.missing.join(", ")}`);
|
|
1948
|
+
console.log(`Counts: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}`);
|
|
1949
|
+
console.log("Dry run only - no files written.");
|
|
1950
|
+
}
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
const writeResult = writeFeedbackSnapshot({ snapshot, wrapperRoot, outputDir });
|
|
1955
|
+
summary.data_root = writeResult.dataRoot;
|
|
1956
|
+
|
|
1957
|
+
if (args.json) {
|
|
1958
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
console.log(`Project: ${summary.project_id || "(unknown)"}`);
|
|
1963
|
+
console.log(`Wrapper root: ${summary.wrapper_root}`);
|
|
1964
|
+
console.log(`Output root: ${summary.output_root}`);
|
|
1965
|
+
console.log(`Configured repos: ${summary.repo_names.join(", ") || "(none)"}`);
|
|
1966
|
+
console.log(`Found locally: ${summary.local.found.join(", ") || "(none)"}`);
|
|
1967
|
+
if (summary.local.missing.length) console.log(`Missing locally: ${summary.local.missing.join(", ")}`);
|
|
1968
|
+
console.log(`Wrote feedback: total_feedback=${summary.counts.total_feedback || 0}, with_prd_text=${summary.counts.with_prd_text || 0}`);
|
|
1969
|
+
console.log(`Snapshot: ${summary.snapshot_hash || "n/a"}`);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1670
1972
|
async function runQuery(args) {
|
|
1671
1973
|
const key = (process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY || "").trim();
|
|
1672
1974
|
if (!key) {
|
|
@@ -1810,6 +2112,11 @@ async function main() {
|
|
|
1810
2112
|
return;
|
|
1811
2113
|
}
|
|
1812
2114
|
|
|
2115
|
+
if (command === "feedback-sync") {
|
|
2116
|
+
await runFeedbackSync(args);
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
1813
2120
|
if (command === "chat") {
|
|
1814
2121
|
await runChat(args);
|
|
1815
2122
|
return;
|
|
@@ -1825,6 +2132,11 @@ async function main() {
|
|
|
1825
2132
|
return;
|
|
1826
2133
|
}
|
|
1827
2134
|
|
|
2135
|
+
if (command === "update-owner") {
|
|
2136
|
+
await runUpdateOwner(args);
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
1828
2140
|
if (command === "update-client") {
|
|
1829
2141
|
await runUpdateClient(args);
|
|
1830
2142
|
return;
|