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
- const header = `[${new Date().toISOString()}] ${NAME} v${VERSION} log started\n`;
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.1",
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.1",
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",