ma-agents 3.17.0-beta.1 → 3.17.0-beta.2
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/bin/cli.js
CHANGED
|
@@ -40,6 +40,7 @@ const { uninstallProfileArtifacts } = require('../lib/uninstall');
|
|
|
40
40
|
const bmad = require('../lib/bmad');
|
|
41
41
|
const { getBmadPlatformCode } = require('../lib/agents');
|
|
42
42
|
const { enumerateCustomSource, NFR71_WARNING, CustomMarketplaceError, reconcileCustomMarketplaceSelection, registryTrustLabel } = require('../lib/custom-marketplace');
|
|
43
|
+
const { setLogSink, diag, diagError } = require('../lib/diag');
|
|
43
44
|
const { handleCreateSkill, handleValidateSkill, handleSetMandatory, handleCustomizeAgent, handleCreateAgent } = require('../lib/skill-authoring');
|
|
44
45
|
|
|
45
46
|
const PKG = require('../package.json');
|
|
@@ -55,9 +56,21 @@ const VERSION = PKG.version;
|
|
|
55
56
|
*/
|
|
56
57
|
function setupLogging(logFilePath) {
|
|
57
58
|
const logFd = fs.openSync(logFilePath, 'w');
|
|
58
|
-
|
|
59
|
+
// Rich header — the environment facts needed to make sense of a report later:
|
|
60
|
+
// exact version, how it was invoked, runtime, platform, and where it ran.
|
|
61
|
+
const header =
|
|
62
|
+
`[${new Date().toISOString()}] ${NAME} v${VERSION} — log started\n` +
|
|
63
|
+
` argv: ${process.argv.slice(1).join(' ')}\n` +
|
|
64
|
+
` node: ${process.version}\n` +
|
|
65
|
+
` platform: ${process.platform} ${process.arch}\n` +
|
|
66
|
+
` cwd: ${process.cwd()}\n`;
|
|
59
67
|
fs.writeSync(logFd, header);
|
|
60
68
|
|
|
69
|
+
// Route log-file-only diagnostics (lib/diag.js) to this fd. These breadcrumbs
|
|
70
|
+
// (custom-marketplace phases, swallowed error causes, etc.) land ONLY in the
|
|
71
|
+
// log, never on the user's console.
|
|
72
|
+
setLogSink((s) => { try { fs.writeSync(logFd, s); } catch { /* ignore */ } });
|
|
73
|
+
|
|
61
74
|
const origStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
62
75
|
const origStderrWrite = process.stderr.write.bind(process.stderr);
|
|
63
76
|
|
|
@@ -81,6 +94,7 @@ function setupLogging(logFilePath) {
|
|
|
81
94
|
process.stderr.write = origStderrWrite;
|
|
82
95
|
const footer = `\n[${new Date().toISOString()}] log ended\n`;
|
|
83
96
|
try { fs.writeSync(logFd, footer); } catch { /* ignore */ }
|
|
97
|
+
setLogSink(null);
|
|
84
98
|
try { fs.closeSync(logFd); } catch { /* ignore */ }
|
|
85
99
|
delete process.env.MA_AGENTS_LOG_ACTIVE;
|
|
86
100
|
};
|
|
@@ -1025,13 +1039,16 @@ async function promptCustomMarketplace({ bmadCustomSourceFlag = '', yesFlag = fa
|
|
|
1025
1039
|
}
|
|
1026
1040
|
|
|
1027
1041
|
let enumerated;
|
|
1042
|
+
diag('custom-source: enumerate begin', { source });
|
|
1028
1043
|
try {
|
|
1029
1044
|
enumerated = await enumerateCustomSource(source);
|
|
1030
1045
|
} catch (err) {
|
|
1031
1046
|
// Fail clean (spike §3 contract) — a short, human-readable message only.
|
|
1032
1047
|
// Never print err.stack here: an unreachable/unauthorized custom source
|
|
1033
1048
|
// must not dump a raw upstream stack, and must never touch the base
|
|
1034
|
-
// install (the rest of installWizard proceeds unaffected).
|
|
1049
|
+
// install (the rest of installWizard proceeds unaffected). The full cause
|
|
1050
|
+
// (and stack) goes to the LOG FILE only, for diagnosis.
|
|
1051
|
+
diagError('custom-source: enumerate failed', err);
|
|
1035
1052
|
const message = err instanceof CustomMarketplaceError ? err.message : `Custom marketplace source error: ${err.message}`;
|
|
1036
1053
|
console.log(chalk.red(`\n ${message}`));
|
|
1037
1054
|
console.log(chalk.gray(' Skipping custom marketplace source; the rest of the install is unaffected.'));
|
|
@@ -1039,6 +1056,11 @@ async function promptCustomMarketplace({ bmadCustomSourceFlag = '', yesFlag = fa
|
|
|
1039
1056
|
}
|
|
1040
1057
|
|
|
1041
1058
|
const { plugins, mode, rootDir, sourceUrl } = enumerated;
|
|
1059
|
+
diag('custom-source: enumerated', {
|
|
1060
|
+
source, mode, rootDir, sourceUrl,
|
|
1061
|
+
pluginCount: (plugins || []).length,
|
|
1062
|
+
codes: (plugins || []).map((p) => p.code),
|
|
1063
|
+
});
|
|
1042
1064
|
if (!plugins || plugins.length === 0) {
|
|
1043
1065
|
console.log(chalk.yellow(` Custom marketplace source "${source}" declared no plugins — nothing to select.`));
|
|
1044
1066
|
return { source, mode, selectedCodes: [], plugins: [], rootDir, sourceUrl };
|
|
@@ -1117,12 +1139,16 @@ async function promptCustomMarketplace({ bmadCustomSourceFlag = '', yesFlag = fa
|
|
|
1117
1139
|
*/
|
|
1118
1140
|
async function reconcileCustomMarketplaceOnUpdate({ projectRoot }) {
|
|
1119
1141
|
const persisted = getCustomMarketplaceSelection(projectRoot);
|
|
1142
|
+
diag('custom-source: reconcile on update', persisted
|
|
1143
|
+
? { source: persisted.source, mode: persisted.mode, selectedCodes: persisted.selectedCodes }
|
|
1144
|
+
: { persisted: false });
|
|
1120
1145
|
if (!persisted) return undefined;
|
|
1121
1146
|
|
|
1122
1147
|
let enumerated;
|
|
1123
1148
|
try {
|
|
1124
1149
|
enumerated = await enumerateCustomSource(persisted.source);
|
|
1125
1150
|
} catch (err) {
|
|
1151
|
+
diagError('custom-source: reconcile re-enumerate failed', err);
|
|
1126
1152
|
const message = err instanceof CustomMarketplaceError ? err.message : `Custom marketplace source error: ${err.message}`;
|
|
1127
1153
|
console.log(chalk.red(`\n Custom marketplace update FAILED: ${message}`));
|
|
1128
1154
|
console.log(
|
|
@@ -1547,6 +1573,12 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
|
|
|
1547
1573
|
}
|
|
1548
1574
|
}
|
|
1549
1575
|
if (customMarketplaceSelection && customMarketplaceSelection.selectedCodes.length > 0) {
|
|
1576
|
+
diag('custom-source: install begin', {
|
|
1577
|
+
mode: customMarketplaceSelection.mode,
|
|
1578
|
+
source: customMarketplaceSelection.source,
|
|
1579
|
+
selectedCodes: customMarketplaceSelection.selectedCodes,
|
|
1580
|
+
baseBmadOk,
|
|
1581
|
+
});
|
|
1550
1582
|
if (!baseBmadOk) {
|
|
1551
1583
|
// AC 6 (30.6b) — never layer --custom-source onto a project with no
|
|
1552
1584
|
// (or a broken) base BMAD install. The rest of the install is
|
|
@@ -1565,10 +1597,14 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
|
|
|
1565
1597
|
tools: bmadTools,
|
|
1566
1598
|
selection: customMarketplaceSelection,
|
|
1567
1599
|
});
|
|
1600
|
+
diag('custom-source: install result', { success: !!success });
|
|
1568
1601
|
if (success) {
|
|
1569
1602
|
console.log(chalk.green(' Custom marketplace subset installed successfully!'));
|
|
1570
1603
|
}
|
|
1571
1604
|
} catch (err) {
|
|
1605
|
+
// User sees the one-liner; the full cause/stack (which entry, the
|
|
1606
|
+
// git/resolve error, the FR238 zero-module detail) goes to the log.
|
|
1607
|
+
diagError('custom-source: install failed', err);
|
|
1572
1608
|
console.log(chalk.red(`\n Custom marketplace install failed: ${err.message}`));
|
|
1573
1609
|
}
|
|
1574
1610
|
}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"name": "ma-skills",
|
|
38
38
|
"source": "./",
|
|
39
39
|
"description": "ma-agents extension module providing enterprise SDLC personas and operational workflow skills.",
|
|
40
|
-
"version": "3.17.0-beta.
|
|
40
|
+
"version": "3.17.0-beta.2",
|
|
41
41
|
"author": {
|
|
42
42
|
"name": "Alon Mayaffit"
|
|
43
43
|
},
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
const path = require('node:path');
|
|
41
41
|
const fs = require('fs-extra');
|
|
42
42
|
const yaml = require('js-yaml');
|
|
43
|
+
const { diag } = require('./diag');
|
|
43
44
|
|
|
44
45
|
/** Deep, pinned-internal require path (NFR17). Single call site. */
|
|
45
46
|
const CUSTOM_MODULE_MANAGER_PATH = 'bmad-method/tools/installer/modules/custom-module-manager.js';
|
|
@@ -554,6 +555,19 @@ async function resolveSelectedPlugins({ mode, rootDir, sourceUrl, plugins, selec
|
|
|
554
555
|
function assertResolutionNonEmptyAndUniqueLeaves(resolvedByPlugin, { source = '', mode = null } = {}) {
|
|
555
556
|
const allModules = resolvedByPlugin.flatMap((r) => r.resolved || []);
|
|
556
557
|
|
|
558
|
+
// Per-plugin resolution detail → log file only. When the guard below trips,
|
|
559
|
+
// this line pinpoints WHICH entry resolved to nothing (and how many
|
|
560
|
+
// skills each resolved module got) — the crux of a "zero modules" report.
|
|
561
|
+
diag('custom-source: resolution', {
|
|
562
|
+
source, mode,
|
|
563
|
+
perPlugin: resolvedByPlugin.map((r) => ({
|
|
564
|
+
plugin: (r.plugin && (r.plugin.code || r.plugin.name)) || (r.rawPlugin && r.rawPlugin.name) || '?',
|
|
565
|
+
modules: (r.resolved || []).length,
|
|
566
|
+
skills: (r.resolved || []).reduce((n, m) => n + ((m.skillPaths && m.skillPaths.length) || 0), 0),
|
|
567
|
+
})),
|
|
568
|
+
totalModules: allModules.length,
|
|
569
|
+
});
|
|
570
|
+
|
|
557
571
|
if (allModules.length === 0) {
|
|
558
572
|
// AC7 — a direct-mode source that resolves to nothing is the exact
|
|
559
573
|
// failure a mis-pointed registry URL used to produce. Name the likely
|
|
@@ -741,6 +755,13 @@ async function resolveRegistryEntry(plugin, resolveSourceFn, resolvePluginFn) {
|
|
|
741
755
|
// source string.
|
|
742
756
|
const safePinRef = pinRef && /^[\w.\-+/]+$/.test(pinRef) && !pinRef.includes(':') ? pinRef : null;
|
|
743
757
|
const cloneInput = safePinRef ? `${plugin.repository}@${safePinRef}` : plugin.repository;
|
|
758
|
+
diag('custom-source: registry entry clone', {
|
|
759
|
+
code: plugin.code,
|
|
760
|
+
repository: plugin.repository,
|
|
761
|
+
pinRef: safePinRef,
|
|
762
|
+
approvedSha: plugin.approvedSha || null,
|
|
763
|
+
moduleDefinition: plugin.moduleDefinition || '(none — repo root)',
|
|
764
|
+
});
|
|
744
765
|
|
|
745
766
|
let resolvedSource;
|
|
746
767
|
try {
|
|
@@ -781,6 +802,12 @@ async function resolveRegistryEntry(plugin, resolveSourceFn, resolvePluginFn) {
|
|
|
781
802
|
relModuleDirPosix === '.' ? entryRootDir : path.join(entryRootDir, ...relModuleDirPosix.split('/'));
|
|
782
803
|
const skillNames = await scanSkillDirs(moduleBaseAbs);
|
|
783
804
|
const skillRelPaths = skillNames.map((n) => (relModuleDirPosix === '.' ? n : `${relModuleDirPosix}/${n}`));
|
|
805
|
+
diag('custom-source: registry entry resolved', {
|
|
806
|
+
code: plugin.code,
|
|
807
|
+
moduleDir: relModuleDirPosix,
|
|
808
|
+
skillDirsFound: skillNames.length,
|
|
809
|
+
skills: skillNames,
|
|
810
|
+
});
|
|
784
811
|
|
|
785
812
|
const rawPlugin = {
|
|
786
813
|
name: plugin.code,
|
package/lib/diag.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Log-file-only diagnostics.
|
|
5
|
+
*
|
|
6
|
+
* When `--log` is active, bin/cli.js#setupLogging installs a sink that writes
|
|
7
|
+
* to the log file descriptor. `diag()`/`diagError()` then emit structured
|
|
8
|
+
* breadcrumbs to the LOG FILE ONLY — never to the user's console (the wizard's
|
|
9
|
+
* user-facing output intentionally stays clean; e.g. a failed custom-marketplace
|
|
10
|
+
* source shows a one-line message, not a raw stack — NFR71). Without a sink
|
|
11
|
+
* (no `--log`), every call is a cheap no-op.
|
|
12
|
+
*
|
|
13
|
+
* This is how the swallowed root cause of a custom-marketplace failure (the
|
|
14
|
+
* git/clone/resolve error behind a clean CustomMarketplaceError, the per-entry
|
|
15
|
+
* module_definition that was missing, the FR238 zero-module trigger, etc.)
|
|
16
|
+
* reaches the log for post-hoc diagnosis without leaking to the terminal.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
let sink = null;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {((text: string) => void)|null} fn - writer for log-file output, or null to disable.
|
|
23
|
+
*/
|
|
24
|
+
function setLogSink(fn) {
|
|
25
|
+
sink = typeof fn === 'function' ? fn : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @returns {boolean} whether diagnostics are being captured. */
|
|
29
|
+
function isActive() {
|
|
30
|
+
return sink !== null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function stamp() {
|
|
34
|
+
// new Date() is available in the CLI runtime (this is not a workflow script).
|
|
35
|
+
return new Date().toISOString();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stringify(detail) {
|
|
39
|
+
if (typeof detail === 'string') return detail;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.stringify(detail);
|
|
42
|
+
} catch {
|
|
43
|
+
return String(detail);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Emit one diagnostic breadcrumb to the log file (no-op without a sink).
|
|
49
|
+
* @param {string} label
|
|
50
|
+
* @param {*} [detail] - string or JSON-serializable value.
|
|
51
|
+
*/
|
|
52
|
+
function diag(label, detail) {
|
|
53
|
+
if (!sink) return;
|
|
54
|
+
let line = `[diag ${stamp()}] ${label}`;
|
|
55
|
+
if (detail !== undefined) line += `: ${stringify(detail)}`;
|
|
56
|
+
try {
|
|
57
|
+
sink(line + '\n');
|
|
58
|
+
} catch {
|
|
59
|
+
/* never let diagnostics break the install */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Emit an error — including its `stack` AND any wrapped `cause` (message +
|
|
65
|
+
* stack) — to the log file. This is the crucial bit for custom-marketplace
|
|
66
|
+
* failures, whose user-facing message deliberately hides the upstream cause.
|
|
67
|
+
* @param {string} label
|
|
68
|
+
* @param {Error|*} err
|
|
69
|
+
*/
|
|
70
|
+
function diagError(label, err) {
|
|
71
|
+
if (!sink || !err) return;
|
|
72
|
+
const parts = [`[diag ${stamp()}] ${label}: ${err.message || String(err)}`];
|
|
73
|
+
if (err.stack) parts.push(` stack: ${err.stack}`);
|
|
74
|
+
if (err.cause) {
|
|
75
|
+
const c = err.cause;
|
|
76
|
+
parts.push(` cause: ${(c && c.message) || String(c)}`);
|
|
77
|
+
if (c && c.stack) parts.push(` cause.stack: ${c.stack}`);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
sink(parts.join('\n') + '\n');
|
|
81
|
+
} catch {
|
|
82
|
+
/* never let diagnostics break the install */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { setLogSink, isActive, diag, diagError };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ma-agents",
|
|
3
|
-
"version": "3.17.0-beta.
|
|
3
|
+
"version": "3.17.0-beta.2",
|
|
4
4
|
"description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor, Roo Code)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/cli.js",
|
|
11
|
-
"test": "node test/yes-flag.test.js && node test/skill-authoring.test.js && node test/skill-validation.test.js && node test/skill-mandatory.test.js && node test/skill-customize-agent.test.js && node test/create-agent.test.js && node test/generate-project-context.test.js && node test/build-bmad-args.test.js && node test/build-bmad-cache-reader.test.js && node test/bundle-filter-set-equality.test.js && node test/bmad-version-bump.test.js && node test/staging-6-10-0-regression.test.js && node test/uv-transition-resilience.test.js && node test/passthrough-reconciliation-6-9-0.test.js && node test/extension-module-restructure.test.js && node test/convert-agents-to-skills.test.js && node test/migration.test.js && node test/migration-validation.test.js && node test/story-15-5-workflow-skills.test.js && node test/repo-layout.test.js && node test/config-storage.test.js && node test/cross-repo-validation.test.js && node test/config-lost-on-update.test.js && node test/portable-paths.test.js && node test/cicd-remote-mode.test.js && node test/config-layout.test.js && node test/roo-code-agent.test.js && node test/roo-code-injection.test.js && node test/profile.test.js && node test/instruction-block.test.js && node test/instruction-injection.test.js && node test/agents-md.test.js && node test/instruction-block-git.test.js && node test/onprem-injection.test.js && node test/onprem-layer.test.js && node test/copilot-instructions-path.test.js && node test/roomodes.test.js && node test/clinerules.test.js && node test/cline-workflow-reconciliation.test.js && node test/reconfigure.test.js && node test/experimental-warning.test.js && node test/bmad-persona-phase-prefix.test.js && node test/f1a-on-prem-prefix-post-install.test.js && node test/retired-skills-migration.test.js && node test/rename-migration.test.js && node test/uninstall.test.js && node test/offline-recompile.test.js && node test/plugin-manifests.test.js && node test/demerzel-ml-removal-drift.test.js && node test/demerzel-deprecation-notice.test.js && node test/build-plugin.test.js && node test/config-path-migration.test.js && node test/gitignore-plugin-stage.test.js && node test/customizations-translation.test.js && node test/routing-parity.test.js && node test/bmad-extension.test.js && node test/integration-verification.test.js && node test/agent-scope.test.js && node test/obsolete-tool-dirs.test.js && node test/bmad-empty-tools-guard.test.js && node test/v68-pluginresolver-enumeration-passthrough.test.js && node test/agents.test.js && node test/installer.test.js && node test/bmad.test.js && node test/cli.test.js && node test/knowledge-atlas-smoke.test.js && node test/knowledge-atlas-manifest.test.js && node test/knowledge-atlas-render.test.js && node test/knowledge-atlas-graph.test.js && node test/knowledge-atlas-packaging.test.js && node test/knowledge-atlas-offline-safety.test.js && node test/knowledge-atlas-reconcile.test.js && node test/knowledge-atlas-determinism.test.js && node test/knowledge-atlas-degradation.test.js && node test/knowledge-atlas-realproject.test.js && node test/knowledge-atlas-link-integrity.test.js && node test/knowledge-atlas-config-scan.test.js && node test/knowledge-atlas-references.test.js && node test/agent-injection-strategy.test.js && node test/bmad-module-selection.test.js && node test/custom-marketplace-surface-drift.test.js && node test/custom-marketplace-wizard.test.js && node test/custom-marketplace-live-clone.test.js && node test/custom-marketplace-stage-install.test.js && node test/custom-marketplace-persist-reconcile.test.js && node test/demerzel-nondestructive-invariant.test.js && node test/fr243-custom-source-allowlist-nonleak.test.js && node test/custom-marketplace-registry.test.js",
|
|
11
|
+
"test": "node test/yes-flag.test.js && node test/skill-authoring.test.js && node test/skill-validation.test.js && node test/skill-mandatory.test.js && node test/skill-customize-agent.test.js && node test/create-agent.test.js && node test/generate-project-context.test.js && node test/build-bmad-args.test.js && node test/build-bmad-cache-reader.test.js && node test/bundle-filter-set-equality.test.js && node test/bmad-version-bump.test.js && node test/staging-6-10-0-regression.test.js && node test/uv-transition-resilience.test.js && node test/passthrough-reconciliation-6-9-0.test.js && node test/extension-module-restructure.test.js && node test/convert-agents-to-skills.test.js && node test/migration.test.js && node test/migration-validation.test.js && node test/story-15-5-workflow-skills.test.js && node test/repo-layout.test.js && node test/config-storage.test.js && node test/cross-repo-validation.test.js && node test/config-lost-on-update.test.js && node test/portable-paths.test.js && node test/cicd-remote-mode.test.js && node test/config-layout.test.js && node test/roo-code-agent.test.js && node test/roo-code-injection.test.js && node test/profile.test.js && node test/instruction-block.test.js && node test/instruction-injection.test.js && node test/agents-md.test.js && node test/instruction-block-git.test.js && node test/onprem-injection.test.js && node test/onprem-layer.test.js && node test/copilot-instructions-path.test.js && node test/roomodes.test.js && node test/clinerules.test.js && node test/cline-workflow-reconciliation.test.js && node test/reconfigure.test.js && node test/experimental-warning.test.js && node test/bmad-persona-phase-prefix.test.js && node test/f1a-on-prem-prefix-post-install.test.js && node test/retired-skills-migration.test.js && node test/rename-migration.test.js && node test/uninstall.test.js && node test/offline-recompile.test.js && node test/plugin-manifests.test.js && node test/demerzel-ml-removal-drift.test.js && node test/demerzel-deprecation-notice.test.js && node test/build-plugin.test.js && node test/config-path-migration.test.js && node test/gitignore-plugin-stage.test.js && node test/customizations-translation.test.js && node test/routing-parity.test.js && node test/bmad-extension.test.js && node test/integration-verification.test.js && node test/agent-scope.test.js && node test/obsolete-tool-dirs.test.js && node test/bmad-empty-tools-guard.test.js && node test/v68-pluginresolver-enumeration-passthrough.test.js && node test/agents.test.js && node test/installer.test.js && node test/bmad.test.js && node test/cli.test.js && node test/knowledge-atlas-smoke.test.js && node test/knowledge-atlas-manifest.test.js && node test/knowledge-atlas-render.test.js && node test/knowledge-atlas-graph.test.js && node test/knowledge-atlas-packaging.test.js && node test/knowledge-atlas-offline-safety.test.js && node test/knowledge-atlas-reconcile.test.js && node test/knowledge-atlas-determinism.test.js && node test/knowledge-atlas-degradation.test.js && node test/knowledge-atlas-realproject.test.js && node test/knowledge-atlas-link-integrity.test.js && node test/knowledge-atlas-config-scan.test.js && node test/knowledge-atlas-references.test.js && node test/agent-injection-strategy.test.js && node test/bmad-module-selection.test.js && node test/custom-marketplace-surface-drift.test.js && node test/custom-marketplace-wizard.test.js && node test/custom-marketplace-live-clone.test.js && node test/custom-marketplace-stage-install.test.js && node test/custom-marketplace-persist-reconcile.test.js && node test/demerzel-nondestructive-invariant.test.js && node test/fr243-custom-source-allowlist-nonleak.test.js && node test/custom-marketplace-registry.test.js && node test/diag.test.js",
|
|
12
12
|
"build:bmad-cache": "node scripts/build-bmad-cache.js",
|
|
13
13
|
"build:plugin": "node scripts/build-plugin.js",
|
|
14
14
|
"build:atlas": "node scripts/build-atlas.js",
|