@mytegroupinc/myte-core 0.0.56 → 0.0.57
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 +8 -2
- package/cli.js +374 -140
- package/lib/build-harness.js +378 -0
- package/lib/package-update-check.js +228 -0
- package/mytecody-cli.js +44 -86
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,10 @@ This package exists so the public wrapper can stay small and versioned cleanly.
|
|
|
33
33
|
authorization, rate-limit, and server failures.
|
|
34
34
|
- Safe transient reads and query polling use bounded retries. Query creation
|
|
35
35
|
reuses one idempotency key across ambiguous retries.
|
|
36
|
-
- Snapshot-style commands such as `bootstrap`, `sync-qaqc`, `feedback-sync`, and `suggestions sync` write local `MyteCommandCenter` data.
|
|
36
|
+
- Snapshot-style commands such as `bootstrap`, `sync-qaqc`, `feedback-sync`, and `suggestions sync` write local `MyteCommandCenter` data.
|
|
37
|
+
- `bootstrap` also writes `MyteCommandCenter/MyteBuildHarness.md` and `MyteCommandCenter/data/build-topology.yml`; these capture the project execution protocol and normalized topology without granting write permission.
|
|
38
|
+
- `build verify --json` is a read-only local convergence check. It verifies the last bootstrap, active mission completion, actionable mission-operation state, per-mission terminal QA/QC coverage with no failed or pending cases, semantic/UI review evidence, and repository-gate evidence. It does not run QA/QC, repository tests, or semantic review itself.
|
|
39
|
+
- Successful `myte` commands perform a cached, best-effort check for a newer package version and print any notice to `stderr`; `--json` output on `stdout` remains valid JSON. Disable it with `--no-update-check` or `MYTE_PACKAGE_UPDATE_CHECK=0`.
|
|
37
40
|
- `feedback-sync` prefers stable ObjectId cursor pagination so concurrent
|
|
38
41
|
inserts cannot shift later pages. It remains compatible with older
|
|
39
42
|
offset-only API versions and rejects duplicate cross-page records instead of
|
|
@@ -106,7 +109,10 @@ Coding agents should treat `MYTE_API_KEY` as a project-scoped Project Assistant
|
|
|
106
109
|
|
|
107
110
|
- Load it from the workspace `.env` or environment. `MYTE_PROJECT_API_KEY` is accepted as a compatibility fallback.
|
|
108
111
|
- Call `myte config --json` first when project identity or local repo detection is uncertain.
|
|
109
|
-
- Hydrate mission context with `bootstrap` first instead of scraping the web UI. `bootstrap` writes mission cards, mission suggestion thread state, and `MyteCommandCenter/AgentsMyteAPI.md`.
|
|
112
|
+
- Hydrate mission context with `bootstrap` first instead of scraping the web UI. `bootstrap` writes mission cards, mission suggestion thread state, and `MyteCommandCenter/AgentsMyteAPI.md`.
|
|
113
|
+
- Read `MyteCommandCenter/MyteBuildHarness.md` and `MyteCommandCenter/data/build-topology.yml` before implementing a new project. Organize work by workflow/domain topology and map frontend/backend ownership before changing code.
|
|
114
|
+
- Run `myte build verify --json` only after the final bootstrap and preserve its QA/QC, semantic-review, and repository-gate evidence files for review.
|
|
115
|
+
- The build verifier does not run QAQC, repository tests, or semantic review; run those steps first and preserve their evidence for verification.
|
|
110
116
|
- Use `suggestions sync` only when you need to refresh mission review-thread state without refreshing the full board.
|
|
111
117
|
- Use `query --with-diff` for project-scoped code review, planning, and implementation questions.
|
|
112
118
|
- Use `create-prd` only with reviewed markdown files and `--confirm-write --approval-artifact <path>`. The markdown file body is the PRD document; `description` is only the short board/card summary.
|
package/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ const net = require("net");
|
|
|
14
14
|
const tls = require("tls");
|
|
15
15
|
const { createHash, randomUUID } = require("crypto");
|
|
16
16
|
const { spawnSync } = require("child_process");
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
18
|
DEFAULT_MYTEAI_BASE,
|
|
19
19
|
buildSimpleAiPayload,
|
|
20
20
|
callMyteAiChat,
|
|
@@ -22,10 +22,23 @@ const {
|
|
|
22
22
|
getMyteAiKey,
|
|
23
23
|
normalizeJsonAssistantText,
|
|
24
24
|
normalizeMyteAiBase,
|
|
25
|
-
} = require("./lib/ai-gateway");
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
} = require("./lib/ai-gateway");
|
|
26
|
+
const {
|
|
27
|
+
buildBuildHarnessMarkdown,
|
|
28
|
+
buildTopology,
|
|
29
|
+
qaqcMissionCoverage,
|
|
30
|
+
verifyTopology,
|
|
31
|
+
} = require("./lib/build-harness");
|
|
32
|
+
const {
|
|
33
|
+
checkPackageUpdate,
|
|
34
|
+
packageUpdateNotice,
|
|
35
|
+
} = require("./lib/package-update-check");
|
|
36
|
+
|
|
37
|
+
const DEFAULT_API_BASE = "https://api.myte.dev";
|
|
38
|
+
const PACKAGE_NAME = "myte";
|
|
39
|
+
const PACKAGE_VERSION = require("./package.json").version;
|
|
40
|
+
const DEFAULT_PACKAGE_LATEST_URL = "https://registry.npmjs.org/myte/latest";
|
|
41
|
+
const DEFAULT_DIFF_LIMIT_CHARS = 500_000;
|
|
29
42
|
const DEFAULT_FEEDBACK_SYNC_PAGE_SIZE = 50;
|
|
30
43
|
const MAX_PRD_DOCUMENT_MARKDOWN_CHARS = 250_000;
|
|
31
44
|
const REMOVED_COMMAND_MESSAGES = {
|
|
@@ -74,7 +87,10 @@ function loadEnv() {
|
|
|
74
87
|
return envPath;
|
|
75
88
|
}
|
|
76
89
|
|
|
77
|
-
function splitCommand(argv) {
|
|
90
|
+
function splitCommand(argv) {
|
|
91
|
+
if (argv[0] === "build" && argv[1] === "verify") {
|
|
92
|
+
return { command: "build-verify", rest: argv.slice(2) };
|
|
93
|
+
}
|
|
78
94
|
if (argv[0] === "mission") {
|
|
79
95
|
if (argv[1] === "status" && argv[2] === "update") {
|
|
80
96
|
return { command: "mission-status", rest: argv.slice(3) };
|
|
@@ -203,7 +219,8 @@ function printHelp() {
|
|
|
203
219
|
" myte doctor [--json] [--timeout-ms <ms>] [--base-url <url>]",
|
|
204
220
|
" myte info [--json]",
|
|
205
221
|
" myte --version [--verbose] [--json]",
|
|
206
|
-
" myte bootstrap [--output-dir ./MyteCommandCenter] [--json]",
|
|
222
|
+
" myte bootstrap [--output-dir ./MyteCommandCenter] [--json]",
|
|
223
|
+
" myte build verify [--output-dir ./MyteCommandCenter] [--semantic-review-file <path>] [--repository-gates-file <path>] [--qaqc-file <path>] [--json]",
|
|
207
224
|
" myte run-qaqc --mission-ids \"M001[,M002...]\" [--wait] [--sync] [--force] [--json]",
|
|
208
225
|
" myte mission status --mission-ids \"M001[,M002...]\" --status todo|in_progress|done [--no-sync] [--json]",
|
|
209
226
|
" myte mission archive --mission-ids \"M001[,M002...]\" [--reason \"...\"] [--no-sync] [--json]",
|
|
@@ -261,13 +278,26 @@ function printHelp() {
|
|
|
261
278
|
" - Set MYTE_API_KEY in a workspace .env (or env var)",
|
|
262
279
|
" - Set MYTEAI_API_KEY in a workspace .env (or env var) for `myte ai`",
|
|
263
280
|
"",
|
|
264
|
-
"bootstrap contract:",
|
|
281
|
+
"bootstrap contract:",
|
|
265
282
|
" - Run from any workspace where you want local MyteCommandCenter data written",
|
|
266
283
|
" - Writes MyteCommandCenter/data/project.yml plus phases, epics, stories, and missions locally",
|
|
267
284
|
" - Also refreshes mission review threads into MyteCommandCenter/data/mission-ops.yml by default",
|
|
268
285
|
" - Uses the project-scoped bootstrap snapshot from the Myte API",
|
|
269
286
|
" - Mission cards include richer execution context like complexity, estimated_hours, due_date, subtasks, technical_requirements, resources_needed, labels, and normalized test_cases",
|
|
270
|
-
" - Use --no-suggestions only when you intentionally want mission cards without mission review-thread state",
|
|
287
|
+
" - Use --no-suggestions only when you intentionally want mission cards without mission review-thread state",
|
|
288
|
+
" - Also writes MyteBuildHarness.md and data/build-topology.yml with the project execution protocol and convergence checks",
|
|
289
|
+
"",
|
|
290
|
+
"build verify contract:",
|
|
291
|
+
" - Verifies the local bootstrap topology and evidence without mutating Myte state",
|
|
292
|
+
" - Requires explicit semantic/UI review and repository-gate evidence files for a passing result",
|
|
293
|
+
" - Reports missing acceptance criteria, test cases, orphan missions, and missing intent topology",
|
|
294
|
+
" - Requires readable terminal QAQC coverage for every active mission, with no failed or pending cases",
|
|
295
|
+
" - Does not run QAQC, repository tests, or semantic review; it verifies the evidence those steps produced",
|
|
296
|
+
"",
|
|
297
|
+
"package update notices:",
|
|
298
|
+
" - Successful commands perform a cached, best-effort check for a newer myte package",
|
|
299
|
+
" - Notices go to stderr so --json stdout remains machine-readable",
|
|
300
|
+
" - Disable with --no-update-check or MYTE_PACKAGE_UPDATE_CHECK=0",
|
|
271
301
|
"",
|
|
272
302
|
"sync-qaqc contract:",
|
|
273
303
|
" - Run from any workspace where you want local MyteCommandCenter data written",
|
|
@@ -1009,7 +1039,7 @@ function normalizeApiBase(baseRaw) {
|
|
|
1009
1039
|
return base.endsWith("/api") ? base : `${base}/api`;
|
|
1010
1040
|
}
|
|
1011
1041
|
|
|
1012
|
-
async function fetchJsonWithTimeout(fetchFn, url, options, timeoutMs) {
|
|
1042
|
+
async function fetchJsonWithTimeout(fetchFn, url, options, timeoutMs) {
|
|
1013
1043
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : undefined;
|
|
1014
1044
|
let timedOut = false;
|
|
1015
1045
|
const timeoutId =
|
|
@@ -4402,7 +4432,7 @@ function pruneLegacyCommandCenterArtifacts(dataRoot, options = {}) {
|
|
|
4402
4432
|
}
|
|
4403
4433
|
}
|
|
4404
4434
|
|
|
4405
|
-
function writeBootstrapSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
4435
|
+
function writeBootstrapSnapshot({ snapshot, wrapperRoot, outputDir, local }) {
|
|
4406
4436
|
const { targetRoot, dataRoot } = resolveCommandCenterRoots(wrapperRoot, outputDir);
|
|
4407
4437
|
const phasesDir = path.join(dataRoot, "phases");
|
|
4408
4438
|
const epicsDir = path.join(dataRoot, "epics");
|
|
@@ -4460,10 +4490,19 @@ function writeBootstrapSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
|
4460
4490
|
writeYamlFile(path.join(missionsDir, `${missionPathId}.yml`), mission);
|
|
4461
4491
|
});
|
|
4462
4492
|
|
|
4463
|
-
if (snapshot.project && typeof snapshot.project === "object") {
|
|
4464
|
-
writeYamlFile(path.join(dataRoot, "project.yml"), scrubBootstrapValue(snapshot.project));
|
|
4465
|
-
}
|
|
4466
|
-
const agentsMyteApiPath = writeAgentsMyteApiGuide({ wrapperRoot, outputDir });
|
|
4493
|
+
if (snapshot.project && typeof snapshot.project === "object") {
|
|
4494
|
+
writeYamlFile(path.join(dataRoot, "project.yml"), scrubBootstrapValue(snapshot.project));
|
|
4495
|
+
}
|
|
4496
|
+
const agentsMyteApiPath = writeAgentsMyteApiGuide({ wrapperRoot, outputDir });
|
|
4497
|
+
const topology = buildTopology(snapshot, {
|
|
4498
|
+
mode: local?.mode,
|
|
4499
|
+
found: local?.found,
|
|
4500
|
+
missing: local?.missing,
|
|
4501
|
+
});
|
|
4502
|
+
const buildTopologyPath = path.join(dataRoot, "build-topology.yml");
|
|
4503
|
+
const buildHarnessPath = path.join(targetRoot, "MyteBuildHarness.md");
|
|
4504
|
+
writeYamlFile(buildTopologyPath, topology);
|
|
4505
|
+
writeTextFile(buildHarnessPath, buildBuildHarnessMarkdown(topology));
|
|
4467
4506
|
|
|
4468
4507
|
const manifest = {
|
|
4469
4508
|
schema_version: snapshot.schema_version || 1,
|
|
@@ -4480,11 +4519,73 @@ function writeBootstrapSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
|
4480
4519
|
};
|
|
4481
4520
|
return {
|
|
4482
4521
|
targetRoot,
|
|
4483
|
-
dataRoot,
|
|
4484
|
-
agentsMyteApiPath,
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4522
|
+
dataRoot,
|
|
4523
|
+
agentsMyteApiPath,
|
|
4524
|
+
buildTopologyPath,
|
|
4525
|
+
buildHarnessPath,
|
|
4526
|
+
topology,
|
|
4527
|
+
manifest,
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
|
|
4531
|
+
function packageUpdateCheckEnabled(args = {}) {
|
|
4532
|
+
if (args["update-check"] === false) return false;
|
|
4533
|
+
return process.env.MYTE_PACKAGE_UPDATE_CHECK !== "0";
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
function packageLatestUrl(args = {}) {
|
|
4537
|
+
return String(
|
|
4538
|
+
args["package-latest-url"] ||
|
|
4539
|
+
process.env.MYTE_PACKAGE_LATEST_URL ||
|
|
4540
|
+
DEFAULT_PACKAGE_LATEST_URL,
|
|
4541
|
+
);
|
|
4542
|
+
}
|
|
4543
|
+
|
|
4544
|
+
function packageUpdateTimeoutMs(args = {}) {
|
|
4545
|
+
const value = Number(
|
|
4546
|
+
args["package-update-timeout-ms"] ||
|
|
4547
|
+
process.env.MYTE_PACKAGE_UPDATE_TIMEOUT_MS ||
|
|
4548
|
+
1500,
|
|
4549
|
+
);
|
|
4550
|
+
return Number.isFinite(value) && value > 0 ? value : 1500;
|
|
4551
|
+
}
|
|
4552
|
+
|
|
4553
|
+
async function checkMytePackageUpdate(args = {}) {
|
|
4554
|
+
const fetchFn = await getFetch();
|
|
4555
|
+
return checkPackageUpdate({
|
|
4556
|
+
packageName: PACKAGE_NAME,
|
|
4557
|
+
installedVersion: PACKAGE_VERSION,
|
|
4558
|
+
latestUrl: packageLatestUrl(args),
|
|
4559
|
+
enabled: packageUpdateCheckEnabled(args),
|
|
4560
|
+
timeoutMs: packageUpdateTimeoutMs(args),
|
|
4561
|
+
cacheDir: process.env.MYTE_PACKAGE_UPDATE_CACHE_DIR || undefined,
|
|
4562
|
+
fetchJson: async (url, { timeoutMs } = {}) => {
|
|
4563
|
+
const { resp, body } = await fetchJsonWithTimeout(
|
|
4564
|
+
fetchFn,
|
|
4565
|
+
url,
|
|
4566
|
+
{ method: "GET", headers: { Accept: "application/json" } },
|
|
4567
|
+
timeoutMs,
|
|
4568
|
+
);
|
|
4569
|
+
return { ok: Boolean(resp.ok), status: resp.status, body };
|
|
4570
|
+
},
|
|
4571
|
+
});
|
|
4572
|
+
}
|
|
4573
|
+
|
|
4574
|
+
async function runWithPackageUpdateNotice(args, run) {
|
|
4575
|
+
const updatePromise = checkMytePackageUpdate(args).catch(() => null);
|
|
4576
|
+
let completed = false;
|
|
4577
|
+
try {
|
|
4578
|
+
const result = await run();
|
|
4579
|
+
completed = true;
|
|
4580
|
+
return result;
|
|
4581
|
+
} finally {
|
|
4582
|
+
if (completed && !process.exitCode) {
|
|
4583
|
+
const status = await updatePromise;
|
|
4584
|
+
const notice = packageUpdateNotice(status);
|
|
4585
|
+
if (notice) console.error(`[myte] ${notice}`);
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4488
4589
|
|
|
4489
4590
|
function writeQaqcSnapshot({ snapshot, wrapperRoot, outputDir }) {
|
|
4490
4591
|
const { targetRoot, dataRoot } = resolveCommandCenterRoots(wrapperRoot, outputDir);
|
|
@@ -6331,7 +6432,7 @@ async function runConfig(args) {
|
|
|
6331
6432
|
}
|
|
6332
6433
|
}
|
|
6333
6434
|
|
|
6334
|
-
async function runBootstrap(args) {
|
|
6435
|
+
async function runBootstrap(args) {
|
|
6335
6436
|
const key = (process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY || "").trim();
|
|
6336
6437
|
if (!key) {
|
|
6337
6438
|
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
@@ -6419,9 +6520,38 @@ async function runBootstrap(args) {
|
|
|
6419
6520
|
return;
|
|
6420
6521
|
}
|
|
6421
6522
|
|
|
6422
|
-
const writeResult = writeBootstrapSnapshot({
|
|
6423
|
-
|
|
6424
|
-
|
|
6523
|
+
const writeResult = writeBootstrapSnapshot({
|
|
6524
|
+
snapshot,
|
|
6525
|
+
wrapperRoot,
|
|
6526
|
+
outputDir,
|
|
6527
|
+
local: {
|
|
6528
|
+
mode: resolved.mode,
|
|
6529
|
+
found: (resolved.repos || []).map((repo) => repo.name),
|
|
6530
|
+
missing: resolved.missing || [],
|
|
6531
|
+
},
|
|
6532
|
+
});
|
|
6533
|
+
summary.data_root = writeResult.dataRoot;
|
|
6534
|
+
summary.agents_myte_api_path = writeResult.agentsMyteApiPath;
|
|
6535
|
+
summary.build_harness_path = writeResult.buildHarnessPath;
|
|
6536
|
+
summary.build_topology_path = writeResult.buildTopologyPath;
|
|
6537
|
+
const topologyWarnings = [];
|
|
6538
|
+
if (writeResult.topology.intent.source === "mission_snapshot_only") {
|
|
6539
|
+
topologyWarnings.push("API intent topology was not returned; workflow grouping is fallback-only");
|
|
6540
|
+
}
|
|
6541
|
+
if (!writeResult.topology.intent.workflows.length) {
|
|
6542
|
+
topologyWarnings.push("No explicit workflows were returned; validate workflow boundaries from features and entities");
|
|
6543
|
+
}
|
|
6544
|
+
if (writeResult.topology.mission_groups.some((group) => group.source === "fallback")) {
|
|
6545
|
+
topologyWarnings.push("One or more mission groups use fallback metadata and require semantic review");
|
|
6546
|
+
}
|
|
6547
|
+
summary.topology = {
|
|
6548
|
+
intent_source: writeResult.topology.intent.source,
|
|
6549
|
+
workflow_count: writeResult.topology.intent.workflows.length,
|
|
6550
|
+
feature_count: writeResult.topology.intent.features.length,
|
|
6551
|
+
entity_count: writeResult.topology.intent.entity_graph.entities.length,
|
|
6552
|
+
relationship_count: writeResult.topology.intent.entity_graph.relationships.length,
|
|
6553
|
+
warnings: topologyWarnings,
|
|
6554
|
+
};
|
|
6425
6555
|
|
|
6426
6556
|
if (includeSuggestions) {
|
|
6427
6557
|
let missionOpsSnapshot;
|
|
@@ -6456,16 +6586,103 @@ async function runBootstrap(args) {
|
|
|
6456
6586
|
console.log(`Found locally: ${summary.local.found.join(", ") || "(none)"}`);
|
|
6457
6587
|
if (summary.local.missing.length) console.log(`Missing locally: ${summary.local.missing.join(", ")}`);
|
|
6458
6588
|
console.log(`Wrote: phases=${summary.counts.phases}, epics=${summary.counts.epics}, stories=${summary.counts.stories}, missions=${summary.counts.missions}`);
|
|
6459
|
-
if (summary.agents_myte_api_path) console.log(`Wrote AgentsMyteAPI: ${summary.agents_myte_api_path}`);
|
|
6589
|
+
if (summary.agents_myte_api_path) console.log(`Wrote AgentsMyteAPI: ${summary.agents_myte_api_path}`);
|
|
6590
|
+
if (summary.build_harness_path) console.log(`Wrote MyteBuildHarness: ${summary.build_harness_path}`);
|
|
6591
|
+
if (summary.build_topology_path) console.log(`Wrote build topology: ${summary.build_topology_path}`);
|
|
6460
6592
|
if (summary.mission_ops?.included) {
|
|
6461
6593
|
console.log(`Wrote mission ops: total_threads=${summary.mission_ops.total_threads}, actionable_threads=${summary.mission_ops.actionable_threads}`);
|
|
6462
6594
|
} else {
|
|
6463
6595
|
console.log("Skipped mission ops sync.");
|
|
6464
6596
|
}
|
|
6465
|
-
console.log(`Snapshot: ${summary.snapshot_hash || "n/a"}`);
|
|
6466
|
-
}
|
|
6467
|
-
|
|
6468
|
-
|
|
6597
|
+
console.log(`Snapshot: ${summary.snapshot_hash || "n/a"}`);
|
|
6598
|
+
}
|
|
6599
|
+
|
|
6600
|
+
function resolveLocalEvidencePath(candidate, fallback, outputRoot) {
|
|
6601
|
+
const value = candidate ? path.resolve(process.cwd(), String(candidate)) : path.join(outputRoot, fallback);
|
|
6602
|
+
return value;
|
|
6603
|
+
}
|
|
6604
|
+
|
|
6605
|
+
function readEvidenceFile(filePath) {
|
|
6606
|
+
if (!fs.existsSync(filePath)) return null;
|
|
6607
|
+
const text = fs.readFileSync(filePath, "utf8").trim();
|
|
6608
|
+
if (!text) return null;
|
|
6609
|
+
try {
|
|
6610
|
+
return JSON.parse(text);
|
|
6611
|
+
} catch (_) {
|
|
6612
|
+
try {
|
|
6613
|
+
return parseYaml(text);
|
|
6614
|
+
} catch (_) {
|
|
6615
|
+
return { raw: text };
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
|
|
6620
|
+
async function runBuildVerify(args) {
|
|
6621
|
+
const outputDir = args["output-dir"] || args.outputDir || args.output_dir;
|
|
6622
|
+
const outputRoot = path.resolve(process.cwd(), String(outputDir || "MyteCommandCenter"));
|
|
6623
|
+
const dataRoot = path.join(outputRoot, "data");
|
|
6624
|
+
const topologyPath = path.join(dataRoot, "build-topology.yml");
|
|
6625
|
+
const harnessPath = path.join(outputRoot, "MyteBuildHarness.md");
|
|
6626
|
+
const topology = readEvidenceFile(topologyPath);
|
|
6627
|
+
const missionOps = readEvidenceFile(path.join(dataRoot, "mission-ops.yml"));
|
|
6628
|
+
const qaqc = readEvidenceFile(resolveLocalEvidencePath(args["qaqc-file"] || args.qaqcFile, path.join("data", "qaqc.yml"), outputRoot));
|
|
6629
|
+
const semanticReviewPath = resolveLocalEvidencePath(args["semantic-review-file"] || args.semanticReviewFile, "build-evidence/semantic-review.md", outputRoot);
|
|
6630
|
+
const repositoryGatesPath = resolveLocalEvidencePath(args["repository-gates-file"] || args.repositoryGatesFile, "build-evidence/repository-gates.yml", outputRoot);
|
|
6631
|
+
const semanticReview = Boolean(readEvidenceFile(semanticReviewPath));
|
|
6632
|
+
const repositoryGates = Boolean(readEvidenceFile(repositoryGatesPath));
|
|
6633
|
+
const qaqcEvidence = Boolean(qaqc);
|
|
6634
|
+
const qaqcCoverage = topology ? qaqcMissionCoverage(topology, qaqc) : null;
|
|
6635
|
+
|
|
6636
|
+
const result = {
|
|
6637
|
+
ok: false,
|
|
6638
|
+
output_root: outputRoot,
|
|
6639
|
+
topology_path: topologyPath,
|
|
6640
|
+
harness_path: harnessPath,
|
|
6641
|
+
evidence: {
|
|
6642
|
+
semantic_review: { path: semanticReviewPath, present: semanticReview },
|
|
6643
|
+
repository_gates: { path: repositoryGatesPath, present: repositoryGates },
|
|
6644
|
+
qaqc: {
|
|
6645
|
+
path: resolveLocalEvidencePath(args["qaqc-file"] || args.qaqcFile, path.join("data", "qaqc.yml"), outputRoot),
|
|
6646
|
+
present: qaqcEvidence,
|
|
6647
|
+
coverage: qaqcCoverage,
|
|
6648
|
+
},
|
|
6649
|
+
},
|
|
6650
|
+
errors: [],
|
|
6651
|
+
warnings: [],
|
|
6652
|
+
};
|
|
6653
|
+
if (!topology) result.errors.push(`missing ${topologyPath}`);
|
|
6654
|
+
if (!fs.existsSync(harnessPath)) result.errors.push(`missing ${harnessPath}`);
|
|
6655
|
+
if (topology) {
|
|
6656
|
+
const topologyResult = verifyTopology(topology, { semanticReview, repositoryGates, qaqcEvidence, qaqcCoverage });
|
|
6657
|
+
result.errors.push(...topologyResult.errors);
|
|
6658
|
+
result.warnings.push(...topologyResult.warnings);
|
|
6659
|
+
const statusCounts = topology.mission_coverage?.status_counts || {};
|
|
6660
|
+
const totalMissions = Number(topology.mission_coverage?.active_missions || 0);
|
|
6661
|
+
const done = Object.entries(statusCounts)
|
|
6662
|
+
.filter(([status]) => status.trim().toLowerCase() === "done")
|
|
6663
|
+
.reduce((sum, [, count]) => sum + Number(count || 0), 0);
|
|
6664
|
+
const incomplete = Math.max(0, totalMissions - done);
|
|
6665
|
+
result.completion = {
|
|
6666
|
+
active_missions: totalMissions,
|
|
6667
|
+
incomplete_missions: incomplete,
|
|
6668
|
+
all_active_missions_done: incomplete === 0,
|
|
6669
|
+
};
|
|
6670
|
+
if (incomplete > 0) result.errors.push(`${incomplete} active mission(s) are not Done in the last bootstrap`);
|
|
6671
|
+
}
|
|
6672
|
+
const actionable = Number(missionOps?.sync?.counts?.actionable_threads || missionOps?.counts?.actionable_threads || 0);
|
|
6673
|
+
result.mission_ops = { actionable_threads: actionable };
|
|
6674
|
+
if (actionable > 0) result.errors.push(`${actionable} actionable mission-operation thread(s) remain`);
|
|
6675
|
+
result.ok = result.errors.length === 0;
|
|
6676
|
+
if (args.json) console.log(JSON.stringify(result, null, 2));
|
|
6677
|
+
else {
|
|
6678
|
+
console.log(`Build verification: ${result.ok ? "passed" : "failed"}`);
|
|
6679
|
+
if (result.errors.length) console.log(`Errors: ${result.errors.join("; ")}`);
|
|
6680
|
+
if (result.warnings.length) console.log(`Warnings: ${result.warnings.join("; ")}`);
|
|
6681
|
+
}
|
|
6682
|
+
if (!result.ok) process.exitCode = 1;
|
|
6683
|
+
}
|
|
6684
|
+
|
|
6685
|
+
async function runSyncQaqc(args) {
|
|
6469
6686
|
const key = (process.env.MYTE_API_KEY || process.env.MYTE_PROJECT_API_KEY || "").trim();
|
|
6470
6687
|
if (!key) {
|
|
6471
6688
|
console.error("Missing MYTE_API_KEY (project key) in environment/.env");
|
|
@@ -8231,7 +8448,16 @@ async function resyncBootstrapAfterMissionMutation({ args, apiBase, key, timeout
|
|
|
8231
8448
|
const snapshot = await fetchBootstrapSnapshot({ apiBase, key, timeoutMs });
|
|
8232
8449
|
const resolved = resolvePortableWorkspace(snapshot.repo_names || []);
|
|
8233
8450
|
const wrapperRoot = !outputDir && existingPaths?.wrapperRoot ? existingPaths.wrapperRoot : resolved.root;
|
|
8234
|
-
const writeResult = writeBootstrapSnapshot({
|
|
8451
|
+
const writeResult = writeBootstrapSnapshot({
|
|
8452
|
+
snapshot,
|
|
8453
|
+
wrapperRoot,
|
|
8454
|
+
outputDir,
|
|
8455
|
+
local: {
|
|
8456
|
+
mode: resolved.mode,
|
|
8457
|
+
found: (resolved.repos || []).map((repo) => repo.name),
|
|
8458
|
+
missing: resolved.missing || [],
|
|
8459
|
+
},
|
|
8460
|
+
});
|
|
8235
8461
|
const missionOpsSnapshot = await fetchSuggestionsSyncSnapshot({ apiBase, key, timeoutMs, actorScope });
|
|
8236
8462
|
const missionOpsWrite = writeMissionOpsSnapshot({ snapshot: missionOpsSnapshot, wrapperRoot, outputDir });
|
|
8237
8463
|
const missionOpsCounts = missionOpsWrite?.manifest?.sync?.counts || {};
|
|
@@ -8740,10 +8966,10 @@ async function runQuery(args) {
|
|
|
8740
8966
|
};
|
|
8741
8967
|
if (diffDiagnostics) payload.diff_diagnostics = diffDiagnostics;
|
|
8742
8968
|
|
|
8743
|
-
if (printContext) {
|
|
8744
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
8745
|
-
|
|
8746
|
-
}
|
|
8969
|
+
if (printContext) {
|
|
8970
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
8971
|
+
return;
|
|
8972
|
+
}
|
|
8747
8973
|
|
|
8748
8974
|
let data;
|
|
8749
8975
|
const requestId = queryRequestId(args);
|
|
@@ -8855,112 +9081,120 @@ async function runChat(args) {
|
|
|
8855
9081
|
process.exit(1);
|
|
8856
9082
|
}
|
|
8857
9083
|
|
|
8858
|
-
async function
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
if (command
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
}
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
}
|
|
9084
|
+
async function dispatchCommand(command, args, envPath) {
|
|
9085
|
+
if (command === "version") {
|
|
9086
|
+
await runVersion(args);
|
|
9087
|
+
return;
|
|
9088
|
+
}
|
|
9089
|
+
|
|
9090
|
+
if (command === "info") {
|
|
9091
|
+
await runInfo(args, envPath);
|
|
9092
|
+
return;
|
|
9093
|
+
}
|
|
9094
|
+
|
|
9095
|
+
if (command === "doctor") {
|
|
9096
|
+
await runDoctor(args, envPath);
|
|
9097
|
+
return;
|
|
9098
|
+
}
|
|
9099
|
+
|
|
9100
|
+
if (command === "config") {
|
|
9101
|
+
await runConfig(args);
|
|
9102
|
+
return;
|
|
9103
|
+
}
|
|
9104
|
+
|
|
9105
|
+
if (command === "ai") {
|
|
9106
|
+
await runAi(args);
|
|
9107
|
+
return;
|
|
9108
|
+
}
|
|
9109
|
+
|
|
9110
|
+
if (command === "bootstrap") {
|
|
9111
|
+
await runBootstrap(args);
|
|
9112
|
+
return;
|
|
9113
|
+
}
|
|
9114
|
+
if (command === "build-verify") {
|
|
9115
|
+
await runBuildVerify(args);
|
|
9116
|
+
return;
|
|
9117
|
+
}
|
|
9118
|
+
|
|
9119
|
+
if (command === "run-qaqc") {
|
|
9120
|
+
await runRunQaqc(args);
|
|
9121
|
+
return;
|
|
9122
|
+
}
|
|
9123
|
+
|
|
9124
|
+
if (command === "mission-status") {
|
|
9125
|
+
await runMissionStatus(args);
|
|
9126
|
+
return;
|
|
9127
|
+
}
|
|
9128
|
+
|
|
9129
|
+
if (command === "mission-archive") {
|
|
9130
|
+
await runMissionArchiveCommand(args);
|
|
9131
|
+
return;
|
|
9132
|
+
}
|
|
9133
|
+
|
|
9134
|
+
if (command === "sync-qaqc" || command === "qaqc-sync") {
|
|
9135
|
+
await runSyncQaqc(args);
|
|
9136
|
+
return;
|
|
9137
|
+
}
|
|
9138
|
+
|
|
9139
|
+
if (command === "feedback-sync") {
|
|
9140
|
+
await runFeedbackSync(args);
|
|
9141
|
+
return;
|
|
9142
|
+
}
|
|
9143
|
+
|
|
9144
|
+
if (command === "feedback") {
|
|
9145
|
+
await runFeedback(args);
|
|
9146
|
+
return;
|
|
9147
|
+
}
|
|
9148
|
+
|
|
9149
|
+
if (command === "suggestions") {
|
|
9150
|
+
await runSuggestions(args);
|
|
9151
|
+
return;
|
|
9152
|
+
}
|
|
9153
|
+
|
|
9154
|
+
if (command === "create-prd") {
|
|
9155
|
+
await runCreatePrd(args);
|
|
9156
|
+
return;
|
|
9157
|
+
}
|
|
9158
|
+
|
|
9159
|
+
if (command === "update-team") {
|
|
9160
|
+
await runUpdateTeam(args);
|
|
9161
|
+
return;
|
|
9162
|
+
}
|
|
9163
|
+
|
|
9164
|
+
if (command === "update-owner") {
|
|
9165
|
+
await runUpdateOwner(args);
|
|
9166
|
+
return;
|
|
9167
|
+
}
|
|
9168
|
+
|
|
9169
|
+
if (command === "update-client") {
|
|
9170
|
+
await runUpdateClient(args);
|
|
9171
|
+
return;
|
|
9172
|
+
}
|
|
9173
|
+
|
|
9174
|
+
// query default
|
|
9175
|
+
await runQuery(args);
|
|
9176
|
+
}
|
|
9177
|
+
|
|
9178
|
+
async function main() {
|
|
9179
|
+
const envPath = loadEnv();
|
|
9180
|
+
|
|
9181
|
+
const { command, rest } = splitCommand(process.argv.slice(2));
|
|
9182
|
+
if (REMOVED_COMMAND_MESSAGES[command]) {
|
|
9183
|
+
console.error(REMOVED_COMMAND_MESSAGES[command]);
|
|
9184
|
+
process.exit(1);
|
|
9185
|
+
}
|
|
9186
|
+
if (command === "mission") {
|
|
9187
|
+
console.error("Unknown mission command. Use `myte mission status` or `myte mission archive`.");
|
|
9188
|
+
process.exit(1);
|
|
9189
|
+
}
|
|
9190
|
+
const args = parseArgs(rest);
|
|
9191
|
+
if (args.help || command === "help") {
|
|
9192
|
+
printHelp();
|
|
9193
|
+
return;
|
|
9194
|
+
}
|
|
9195
|
+
|
|
9196
|
+
await runWithPackageUpdateNotice(args, () => dispatchCommand(command, args, envPath));
|
|
9197
|
+
}
|
|
8964
9198
|
|
|
8965
9199
|
if (require.main === module) {
|
|
8966
9200
|
main().catch((err) => {
|