@mytegroupinc/myte-core 0.0.6 → 0.0.7

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 +12 -0
  2. package/cli.js +333 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,12 +7,18 @@ Most users should install the unscoped wrapper instead:
7
7
  - `npm install myte` then `npx myte sync-qaqc`
8
8
  - `npm install myte` then `npx myte query "..." --with-diff`
9
9
  - `npm install myte` then `npm exec myte -- query "..." --with-diff`
10
+ - `npm install myte` then `npx myte update-team "Backend deploy completed; QAQC rerun queued."`
11
+ - `npm install myte` then `npx myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
10
12
  - `npm i -g myte` then `myte bootstrap`
11
13
  - `npm i -g myte` then `myte sync-qaqc`
12
14
  - `npm i -g myte` then `myte query "..." --with-diff`
15
+ - `npm i -g myte` then `myte update-team "Backend deploy completed; QAQC rerun queued."`
16
+ - `npm i -g myte` then `myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
13
17
  - `npx myte@latest bootstrap`
14
18
  - `npx myte@latest sync-qaqc`
15
19
  - `npx myte@latest query "..." --with-diff`
20
+ - `npx myte@latest update-team "Backend deploy completed; QAQC rerun queued."`
21
+ - `npx myte@latest update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
16
22
  - `npm install myte` then `npx myte create-prd ./drafts/auth-prd.md`
17
23
  - `cat ./drafts/auth-prd.md | npx myte create-prd --stdin`
18
24
 
@@ -36,6 +42,10 @@ Notes:
36
42
  - `sync-qaqc` only exports active `Todo` / `In Progress` missions plus a public QAQC summary and sanitized latest batch metadata.
37
43
  - `sync-qaqc` removes previously QAQC-managed mission files from `MyteCommandCenter/data/missions` once they leave the active set.
38
44
  - `create-prd` is a deterministic PRD upload path, not an LLM generation command.
45
+ - `update-team` creates a project comment through `/api/project-assistant/project-comment`.
46
+ - `update-client` creates a client update draft through `/api/project-assistant/client-update-drafts`.
47
+ - `update-client` requires `--subject` plus body markdown from `--body-markdown`, `--body-file`, positional input, or `--stdin`.
48
+ - `update-client` accepts optional `--target-contact-id` repeats or `--target-contact-ids <id1,id2>`.
39
49
  - `--with-diff` only searches repo folders whose names match the project repo names configured in Myte.
40
50
  - `--with-diff` includes per-repo diagnostics in `print-context` payload:
41
51
  - missing repo directories
@@ -58,5 +68,7 @@ Examples:
58
68
  - `npx myte sync-qaqc --dry-run --json`
59
69
  - `npx myte create-prd ./drafts/auth-prd.md --description "Short card summary"`
60
70
  - `npx myte create-prd ./drafts/auth-prd.md --print-context`
71
+ - `npx myte update-team "Backend deploy completed; QAQC rerun queued."`
72
+ - `npx myte update-client --subject "Weekly client update" --body-file ./updates/week-12.md`
61
73
 
62
74
  This package is published under the org scope for governance; the public `myte` wrapper delegates here.
package/cli.js CHANGED
@@ -49,7 +49,23 @@ function loadEnv() {
49
49
  }
50
50
 
51
51
  function splitCommand(argv) {
52
- const known = new Set(["query", "ask", "chat", "config", "bootstrap", "sync-qaqc", "qaqc-sync", "create-prd", "add-prd", "prd", "help", "--help", "-h"]);
52
+ const known = new Set([
53
+ "query",
54
+ "ask",
55
+ "chat",
56
+ "config",
57
+ "bootstrap",
58
+ "sync-qaqc",
59
+ "qaqc-sync",
60
+ "create-prd",
61
+ "add-prd",
62
+ "prd",
63
+ "update-team",
64
+ "update-client",
65
+ "help",
66
+ "--help",
67
+ "-h",
68
+ ]);
53
69
  const first = argv[0];
54
70
  if (first && known.has(first)) {
55
71
  const cmd = first === "--help" || first === "-h" ? "help" : first;
@@ -63,7 +79,25 @@ function parseArgs(argv) {
63
79
  // eslint-disable-next-line global-require
64
80
  return require("minimist")(argv, {
65
81
  boolean: ["with-diff", "diff", "print-context", "dry-run", "fetch", "json", "stdin"],
66
- string: ["query", "q", "context", "ctx", "base-url", "timeout-ms", "diff-limit", "title", "description", "feedback-text", "output-dir"],
82
+ string: [
83
+ "query",
84
+ "q",
85
+ "context",
86
+ "ctx",
87
+ "base-url",
88
+ "timeout-ms",
89
+ "diff-limit",
90
+ "title",
91
+ "description",
92
+ "feedback-text",
93
+ "output-dir",
94
+ "content",
95
+ "subject",
96
+ "body-markdown",
97
+ "body-file",
98
+ "target-contact-id",
99
+ "target-contact-ids",
100
+ ],
67
101
  alias: {
68
102
  q: "query",
69
103
  d: "with-diff",
@@ -118,10 +152,13 @@ function printHelp() {
118
152
  " myte config [--json]",
119
153
  " myte bootstrap [--output-dir ./MyteCommandCenter] [--json]",
120
154
  " myte sync-qaqc [--output-dir ./MyteCommandCenter] [--json]",
155
+ " myte update-team \"<content>\" [--json]",
156
+ " myte update-client --subject \"<text>\" [--body-markdown \"...\"] [--body-file ./update.md] [--target-contact-ids <id1,id2>] [--json]",
121
157
  " myte chat",
122
158
  " myte create-prd <file.md> [--json] [--title \"...\"] [--description \"...\"]",
123
159
  " myte add-prd <file.md> [--json]",
124
160
  " cat file.md | myte create-prd --stdin [--title \"...\"] [--description \"...\"]",
161
+ " cat update.md | myte update-client --stdin --subject \"Weekly client update\"",
125
162
  "",
126
163
  "Run forms:",
127
164
  " npm install myte then npx myte query \"...\" --with-diff",
@@ -149,15 +186,30 @@ function printHelp() {
149
186
  " - Description source: myte-kanban.description or --description",
150
187
  " - PRD DOCX content: the markdown body is stored verbatim",
151
188
  "",
189
+ "update-team contract:",
190
+ " - Creates a project team comment through /api/project-assistant/project-comment",
191
+ " - Required: content (inline, --content, or --stdin)",
192
+ "",
193
+ "update-client contract:",
194
+ " - Creates a client update draft through /api/project-assistant/client-update-drafts",
195
+ " - Required: --subject and body markdown (via --body-markdown, --body-file, positional file/text, or --stdin)",
196
+ " - Optional: --target-contact-id <id> (repeatable) or --target-contact-ids <id1,id2>",
197
+ "",
152
198
  "Options:",
153
199
  " --with-diff Include deterministic git diffs (project-scoped)",
154
200
  " --diff-limit <chars> Truncate diff context to N chars (default: 200000)",
155
201
  " --timeout-ms <ms> Request timeout (default: 300000)",
156
202
  " --base-url <url> API base (default: https://api.myte.dev)",
157
203
  " --output-dir <path> Bootstrap output directory (default: <wrapper-root>/MyteCommandCenter)",
158
- " --stdin Read PRD content from stdin instead of a file path",
204
+ " --stdin Read supported command content from stdin instead of inline text or a file path",
159
205
  " --title <text> Override PRD title for raw markdown uploads",
160
206
  " --description <text> Set feedback description/card summary for raw markdown uploads",
207
+ " --content <text> Team update content for update-team",
208
+ " --subject <text> Client update subject for update-client",
209
+ " --body-markdown <md> Client update markdown body for update-client",
210
+ " --body-file <path> Read client update markdown body from a file",
211
+ " --target-contact-id Add one client contact ObjectId (repeatable)",
212
+ " --target-contact-ids Comma-separated client contact ObjectIds",
161
213
  " --print-context Print JSON payload and exit (no query call)",
162
214
  " --no-fetch Don't git fetch origin main/master before diff",
163
215
  "",
@@ -166,6 +218,9 @@ function printHelp() {
166
218
  " myte bootstrap",
167
219
  " myte bootstrap --output-dir ./MyteCommandCenter",
168
220
  " myte sync-qaqc",
221
+ " myte update-team \"Backend deploy completed; QAQC rerun queued.\"",
222
+ " myte update-client --subject \"Weekly client update\" --body-file ./updates/week-12.md",
223
+ " myte update-client --subject \"Weekly client update\" --body-markdown \"## Progress\\n- Login complete\" --target-contact-ids 507f1f77bcf86cd799439011,507f1f77bcf86cd799439012",
169
224
  " myte create-prd ./drafts/auth-prd.md --description \"Short card summary\"",
170
225
  " cat ./drafts/auth-prd.md | myte create-prd --stdin",
171
226
  " myte config",
@@ -202,6 +257,139 @@ async function readStdinText() {
202
257
  });
203
258
  }
204
259
 
260
+ function firstNonEmptyString(...values) {
261
+ for (const value of values) {
262
+ if (value === undefined || value === null) continue;
263
+ if (Array.isArray(value)) {
264
+ const nested = firstNonEmptyString(...value);
265
+ if (nested) return nested;
266
+ continue;
267
+ }
268
+ const text = String(value).trim();
269
+ if (text) return text;
270
+ }
271
+ return "";
272
+ }
273
+
274
+ function toStringArray(value) {
275
+ if (value === undefined || value === null) return [];
276
+ return Array.isArray(value) ? value.flatMap((item) => toStringArray(item)) : [String(value)];
277
+ }
278
+
279
+ function parseCsvValues(...values) {
280
+ return values
281
+ .flatMap((value) => toStringArray(value))
282
+ .flatMap((value) => value.split(","))
283
+ .map((value) => value.trim())
284
+ .filter(Boolean);
285
+ }
286
+
287
+ function dedupeStrings(values) {
288
+ const seen = new Set();
289
+ const unique = [];
290
+ for (const value of values) {
291
+ const normalized = String(value || "").trim();
292
+ if (!normalized || seen.has(normalized)) continue;
293
+ seen.add(normalized);
294
+ unique.push(normalized);
295
+ }
296
+ return unique;
297
+ }
298
+
299
+ function resolveTimeoutMs(args) {
300
+ const timeoutRaw = args["timeout-ms"] || args.timeoutMs || args.timeout_ms;
301
+ const timeoutParsed = timeoutRaw !== undefined ? Number(timeoutRaw) : 300_000;
302
+ return Number.isFinite(timeoutParsed) ? timeoutParsed : 300_000;
303
+ }
304
+
305
+ function resolveApiBase(args) {
306
+ const baseRaw =
307
+ args["base-url"] || args.baseUrl || args.base_url || process.env.MYTE_API_BASE || DEFAULT_API_BASE;
308
+ return normalizeApiBase(baseRaw);
309
+ }
310
+
311
+ function getProjectApiKey() {
312
+ return (process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY || "").trim();
313
+ }
314
+
315
+ function resolveInputFile(candidatePath, label) {
316
+ const resolved = String(candidatePath || "").trim();
317
+ if (!resolved) return null;
318
+ const absPath = path.resolve(process.cwd(), resolved);
319
+ if (!fs.existsSync(absPath) || !fs.statSync(absPath).isFile()) {
320
+ console.error(`${label} file not found: ${absPath}`);
321
+ process.exit(1);
322
+ }
323
+ return absPath;
324
+ }
325
+
326
+ async function resolveTeamUpdateContent(args) {
327
+ const inlineContent = firstNonEmptyString(args.content, Array.isArray(args._) ? args._.join(" ") : args._);
328
+ const useStdin = Boolean(args.stdin || (!process.stdin.isTTY && !inlineContent));
329
+ if (useStdin) {
330
+ const stdinContent = String((await readStdinText()) || "").trim();
331
+ if (!stdinContent) {
332
+ console.error("Team update content is empty.");
333
+ process.exit(1);
334
+ }
335
+ return stdinContent;
336
+ }
337
+
338
+ const content = String(inlineContent || "").trim();
339
+ if (!content) {
340
+ console.error("Missing team update content.");
341
+ printHelp();
342
+ process.exit(1);
343
+ }
344
+ return content;
345
+ }
346
+
347
+ async function resolveClientUpdateBody(args) {
348
+ const inlineBody = firstNonEmptyString(
349
+ args["body-markdown"],
350
+ args.bodyMarkdown,
351
+ args.body_markdown
352
+ );
353
+ if (inlineBody) return inlineBody;
354
+
355
+ const explicitFile = firstNonEmptyString(args["body-file"], args.bodyFile, args.body_file);
356
+ const positionalInput = firstNonEmptyString(Array.isArray(args._) ? args._.join(" ") : args._);
357
+ const useStdin = Boolean(args.stdin || (!process.stdin.isTTY && !explicitFile && !positionalInput));
358
+ if (useStdin) {
359
+ const stdinBody = String((await readStdinText()) || "").trim();
360
+ if (!stdinBody) {
361
+ console.error("Client update body_markdown is empty.");
362
+ process.exit(1);
363
+ }
364
+ return stdinBody;
365
+ }
366
+
367
+ const fileCandidate = explicitFile || (positionalInput && fs.existsSync(path.resolve(process.cwd(), positionalInput)) ? positionalInput : "");
368
+ if (fileCandidate) {
369
+ const absPath = resolveInputFile(fileCandidate, "Client update body");
370
+ return String(fs.readFileSync(absPath, "utf8") || "").trim();
371
+ }
372
+
373
+ if (positionalInput) return positionalInput;
374
+
375
+ console.error("Missing client update body_markdown.");
376
+ printHelp();
377
+ process.exit(1);
378
+ }
379
+
380
+ function resolveTargetContactIds(args) {
381
+ return dedupeStrings(
382
+ parseCsvValues(
383
+ args["target-contact-id"],
384
+ args.targetContactId,
385
+ args.target_contact_id,
386
+ args["target-contact-ids"],
387
+ args.targetContactIds,
388
+ args.target_contact_ids
389
+ )
390
+ );
391
+ }
392
+
205
393
  async function getFetch() {
206
394
  if (typeof fetch !== "undefined") return fetch;
207
395
  const mod = await import("node-fetch");
@@ -789,6 +977,138 @@ async function callAssistantQuery({ apiBase, key, payload, timeoutMs, endpoint =
789
977
  return body.data || {};
790
978
  }
791
979
 
980
+ function formatTargetContacts(contacts) {
981
+ const items = Array.isArray(contacts) ? contacts : [];
982
+ const formatted = items
983
+ .map((contact) => {
984
+ const name = String(contact?.name || "").trim();
985
+ const email = String(contact?.email || "").trim();
986
+ if (name && email) return `${name} <${email}>`;
987
+ return name || email || String(contact?.contact_id || "").trim();
988
+ })
989
+ .filter(Boolean);
990
+ return formatted.join(", ");
991
+ }
992
+
993
+ async function runUpdateTeam(args) {
994
+ const key = getProjectApiKey();
995
+ if (!key) {
996
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
997
+ process.exit(1);
998
+ }
999
+
1000
+ const payload = {
1001
+ content: await resolveTeamUpdateContent(args),
1002
+ };
1003
+
1004
+ if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
1005
+ console.log(JSON.stringify(payload, null, 2));
1006
+ return;
1007
+ }
1008
+
1009
+ const timeoutMs = resolveTimeoutMs(args);
1010
+ const apiBase = resolveApiBase(args);
1011
+
1012
+ let data;
1013
+ try {
1014
+ data = await callAssistantQuery({
1015
+ apiBase,
1016
+ key,
1017
+ payload,
1018
+ timeoutMs,
1019
+ endpoint: "/project-assistant/project-comment",
1020
+ });
1021
+ } catch (err) {
1022
+ if (err?.name === "AbortError") {
1023
+ console.error(`Request timed out after ${timeoutMs}ms`);
1024
+ } else {
1025
+ console.error("Team update failed:", err?.message || err);
1026
+ }
1027
+ process.exit(1);
1028
+ }
1029
+
1030
+ if (args.json) {
1031
+ console.log(JSON.stringify(data, null, 2));
1032
+ return;
1033
+ }
1034
+
1035
+ if (data.comment_id) console.log(`Comment ID: ${data.comment_id}`);
1036
+ if (data.project_id) console.log(`Project ID: ${data.project_id}`);
1037
+ if (data.user?.name) console.log(`Author: ${data.user.name}`);
1038
+ if (data.created_at) console.log(`Created At: ${data.created_at}`);
1039
+ if (data.content) {
1040
+ console.log("Content:");
1041
+ console.log(data.content);
1042
+ }
1043
+ }
1044
+
1045
+ async function runUpdateClient(args) {
1046
+ const key = getProjectApiKey();
1047
+ if (!key) {
1048
+ console.error("Missing MYTE_API_KEY (project key) in environment/.env");
1049
+ process.exit(1);
1050
+ }
1051
+
1052
+ const subject = firstNonEmptyString(args.subject);
1053
+ if (!subject) {
1054
+ console.error("Missing --subject for client update.");
1055
+ printHelp();
1056
+ process.exit(1);
1057
+ }
1058
+
1059
+ const payload = {
1060
+ subject,
1061
+ body_markdown: await resolveClientUpdateBody(args),
1062
+ target_contact_ids: resolveTargetContactIds(args),
1063
+ };
1064
+
1065
+ if (args["print-context"] || args.printContext || args["dry-run"] || args.dryRun) {
1066
+ console.log(JSON.stringify(payload, null, 2));
1067
+ return;
1068
+ }
1069
+
1070
+ const timeoutMs = resolveTimeoutMs(args);
1071
+ const apiBase = resolveApiBase(args);
1072
+
1073
+ let data;
1074
+ try {
1075
+ data = await callAssistantQuery({
1076
+ apiBase,
1077
+ key,
1078
+ payload,
1079
+ timeoutMs,
1080
+ endpoint: "/project-assistant/client-update-drafts",
1081
+ });
1082
+ } catch (err) {
1083
+ if (err?.name === "AbortError") {
1084
+ console.error(`Request timed out after ${timeoutMs}ms`);
1085
+ } else {
1086
+ console.error("Client update draft failed:", err?.message || err);
1087
+ }
1088
+ process.exit(1);
1089
+ }
1090
+
1091
+ if (args.json) {
1092
+ console.log(JSON.stringify(data, null, 2));
1093
+ return;
1094
+ }
1095
+
1096
+ if (data.draft_id) console.log(`Draft ID: ${data.draft_id}`);
1097
+ if (data.project_title) console.log(`Project: ${data.project_title}`);
1098
+ if (data.project_id) console.log(`Project ID: ${data.project_id}`);
1099
+ if (data.status) console.log(`Status: ${data.status}`);
1100
+ if (data.subject) console.log(`Subject: ${data.subject}`);
1101
+ if (data.created_by_name) console.log(`Author: ${data.created_by_name}`);
1102
+ const targets = formatTargetContacts(data.target_contacts);
1103
+ if (targets) console.log(`Targets: ${targets}`);
1104
+ if (data.created_at) console.log(`Created At: ${data.created_at}`);
1105
+ if (data.updated_at) console.log(`Updated At: ${data.updated_at}`);
1106
+ if (data.snippet) {
1107
+ console.log("Snippet:");
1108
+ console.log(data.snippet);
1109
+ }
1110
+ }
1111
+
792
1112
  function ensureDir(dirPath) {
793
1113
  fs.mkdirSync(dirPath, { recursive: true });
794
1114
  }
@@ -1500,6 +1820,16 @@ async function main() {
1500
1820
  return;
1501
1821
  }
1502
1822
 
1823
+ if (command === "update-team") {
1824
+ await runUpdateTeam(args);
1825
+ return;
1826
+ }
1827
+
1828
+ if (command === "update-client") {
1829
+ await runUpdateClient(args);
1830
+ return;
1831
+ }
1832
+
1503
1833
  // query/ask default
1504
1834
  await runQuery(args);
1505
1835
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mytegroupinc/myte-core",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Myte CLI core implementation (Project Assistant + deterministic diffs).",
5
5
  "type": "commonjs",
6
6
  "main": "cli.js",