@magnusekdahl/parallix 1.2.1 → 1.3.0

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 (79) hide show
  1. package/index.js +190 -185
  2. package/lib/commands/active.js +502 -579
  3. package/lib/commands/active.ts +665 -0
  4. package/lib/commands/checkpoint.js +98 -74
  5. package/lib/commands/checkpoint.ts +79 -0
  6. package/lib/commands/config.js +61 -31
  7. package/lib/commands/config.ts +47 -0
  8. package/lib/commands/coverage-gate.js +271 -246
  9. package/lib/commands/coverage-gate.ts +362 -0
  10. package/lib/commands/diff.js +140 -119
  11. package/lib/commands/diff.ts +125 -0
  12. package/lib/commands/draft.js +770 -923
  13. package/lib/commands/draft.ts +1021 -0
  14. package/lib/commands/handoff.js +588 -608
  15. package/lib/commands/handoff.ts +694 -0
  16. package/lib/commands/integrate.js +1331 -1468
  17. package/lib/commands/integrate.ts +1688 -0
  18. package/lib/commands/mission-start.js +279 -237
  19. package/lib/commands/mission-start.ts +263 -0
  20. package/lib/commands/rebase.js +606 -582
  21. package/lib/commands/rebase.ts +635 -0
  22. package/lib/commands/repair-handoff.js +181 -179
  23. package/lib/commands/repair-handoff.ts +228 -0
  24. package/lib/commands/resolve-conflict.js +121 -102
  25. package/lib/commands/resolve-conflict.ts +112 -0
  26. package/lib/commands/review.js +3 -4
  27. package/lib/commands/review.ts +14 -0
  28. package/lib/commands/setup-review.js +4 -5
  29. package/lib/commands/setup-review.ts +14 -0
  30. package/lib/commands/setup.js +3 -3
  31. package/lib/commands/setup.ts +2 -0
  32. package/lib/commands/stats-backfill.js +394 -359
  33. package/lib/commands/stats-backfill.ts +418 -0
  34. package/lib/commands/stats.js +1424 -1665
  35. package/lib/commands/stats.ts +2186 -0
  36. package/lib/commands/status.js +219 -200
  37. package/lib/commands/status.ts +219 -0
  38. package/lib/commands/verify.js +3 -1
  39. package/lib/commands/verify.ts +2 -0
  40. package/lib/core/nels.js +112 -127
  41. package/lib/core/nels.ts +189 -0
  42. package/lib/core/subagent-limit.js +15 -16
  43. package/lib/core/subagent-limit.ts +25 -0
  44. package/lib/index.js +142 -59
  45. package/lib/index.ts +134 -0
  46. package/lib/review/rebase.js +142 -145
  47. package/lib/review/rebase.ts +188 -0
  48. package/lib/review/review-adapter.js +99 -87
  49. package/lib/review/review-adapter.ts +198 -0
  50. package/lib/review/review-artifacts.js +486 -629
  51. package/lib/review/review-artifacts.ts +622 -0
  52. package/lib/review/review-commands.js +1220 -1373
  53. package/lib/review/review-commands.ts +1579 -0
  54. package/lib/review/review-events.js +544 -886
  55. package/lib/review/review-events.ts +954 -0
  56. package/lib/review/review-loop.js +945 -1020
  57. package/lib/review/review-loop.ts +1174 -0
  58. package/lib/review/review-polling.js +86 -115
  59. package/lib/review/review-polling.ts +194 -0
  60. package/lib/review/review-prompts.js +154 -189
  61. package/lib/review/review-prompts.ts +182 -0
  62. package/lib/review/review-state.js +246 -225
  63. package/lib/review/review-state.ts +353 -0
  64. package/lib/review/review.js +63 -84
  65. package/lib/review/review.ts +138 -0
  66. package/lib/tools/backlog.js +694 -680
  67. package/lib/tools/backlog.ts +835 -0
  68. package/lib/tools/forgejo.js +1270 -1405
  69. package/lib/tools/forgejo.ts +1793 -0
  70. package/lib/tools/gatekeeper.js +106 -96
  71. package/lib/tools/gatekeeper.ts +124 -0
  72. package/lib/tools/redgreen.js +200 -181
  73. package/lib/tools/redgreen.ts +221 -0
  74. package/lib/tools/sessions.js +58 -52
  75. package/lib/tools/sessions.ts +81 -0
  76. package/lib/tools/setup-review.js +902 -975
  77. package/lib/tools/setup-review.ts +1152 -0
  78. package/package.json +4 -4
  79. package/px.js +226 -208
@@ -1,78 +1,102 @@
1
- const { git, run, getCurrentBranch } = require('../core/git');
2
- const { findMissionDir, findMissionArea, inferSlug } = require('../core/mission-utils');
3
- const fmt = require('../core/fmt');
4
- const { formatVerificationCommand, runVerificationGate } = require('../core/verification');
5
-
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ // @ts-nocheck
37
+ const git_js_1 = require("../core/git.js");
38
+ const mission_utils_js_1 = require("../core/mission-utils.js");
39
+ const fmt = __importStar(require("../core/fmt.js"));
40
+ const verification_js_1 = require("../core/verification.js");
6
41
  /** @param {string[]} args */
7
42
  function checkpoint(args) {
8
- /** @param {string} a */
9
- const flags = args.filter(a => a.startsWith('--'));
10
- /** @param {string} a */
11
- const params = args.filter(a => !a.startsWith('--'));
12
-
13
- let [explicitSlug, cpName, nextAction] = params;
14
-
15
- // Shift if slug is inferred
16
- let slug = inferSlug(explicitSlug);
17
- if (slug && explicitSlug !== slug) {
18
- // Slug was inferred, so explicitSlug is actually cpName
19
- nextAction = cpName;
20
- cpName = explicitSlug;
21
- }
22
-
23
- if (!slug || !cpName || !nextAction) {
24
- fmt.log.fail('Usage: node parallix checkpoint [<slug>] <cp-name> "<next-action>" [--no-gate]');
25
- process.exit(1);
26
- }
27
-
28
- const missionDir = findMissionDir(slug);
29
- if (!missionDir) {
30
- fmt.log.fail(`Mission directory not found for slug: ${fmt.slug(slug)}`);
31
- process.exit(1);
32
- }
33
-
34
- const area = findMissionArea(missionDir);
35
- const skipGate = flags.includes('--no-gate');
36
-
37
- fmt.log.info(`Running checkpoint for mission: ${fmt.slug(slug)}, checkpoint: ${fmt.bold(cpName)}`);
38
-
39
- // Step 1: Verify
40
- if (skipGate) {
41
- fmt.log.warn('Step 1: Skipping verification gate (--no-gate)');
42
- } else {
43
- fmt.log.info(`Step 1: Running verification gate for area: ${fmt.bold(area)}...`);
44
- const verifyResult = runVerificationGate(area, { rootDir: process.cwd(), stdio: 'inherit', runFn: run });
45
- if (verifyResult.status !== 0) {
46
- fmt.log.fail(`Verification gate failed for area: ${fmt.bold(area)}. Fix errors and retry ${fmt.command(formatVerificationCommand(area))} or use --no-gate.`);
47
- process.exit(1);
43
+ /** @param {string} a */
44
+ const flags = args.filter(a => a.startsWith('--'));
45
+ /** @param {string} a */
46
+ const params = args.filter(a => !a.startsWith('--'));
47
+ let [explicitSlug, cpName, nextAction] = params;
48
+ // Shift if slug is inferred
49
+ let slug = (0, mission_utils_js_1.inferSlug)(explicitSlug);
50
+ if (slug && explicitSlug !== slug) {
51
+ // Slug was inferred, so explicitSlug is actually cpName
52
+ nextAction = cpName;
53
+ cpName = explicitSlug;
54
+ }
55
+ if (!slug || !cpName || !nextAction) {
56
+ fmt.log.fail('Usage: node parallix checkpoint [<slug>] <cp-name> "<next-action>" [--no-gate]');
57
+ process.exit(1);
58
+ }
59
+ const missionDir = (0, mission_utils_js_1.findMissionDir)(slug);
60
+ if (!missionDir) {
61
+ fmt.log.fail(`Mission directory not found for slug: ${fmt.slug(slug)}`);
62
+ process.exit(1);
63
+ }
64
+ const area = (0, mission_utils_js_1.findMissionArea)(missionDir);
65
+ const skipGate = flags.includes('--no-gate');
66
+ fmt.log.info(`Running checkpoint for mission: ${fmt.slug(slug)}, checkpoint: ${fmt.bold(cpName)}`);
67
+ // Step 1: Verify
68
+ if (skipGate) {
69
+ fmt.log.warn('Step 1: Skipping verification gate (--no-gate)');
70
+ }
71
+ else {
72
+ fmt.log.info(`Step 1: Running verification gate for area: ${fmt.bold(area)}...`);
73
+ const verifyResult = (0, verification_js_1.runVerificationGate)(area, { rootDir: process.cwd(), stdio: 'inherit', runFn: git_js_1.run });
74
+ if (verifyResult.status !== 0) {
75
+ fmt.log.fail(`Verification gate failed for area: ${fmt.bold(area)}. Fix errors and retry ${fmt.command((0, verification_js_1.formatVerificationCommand)(area))} or use --no-gate.`);
76
+ process.exit(1);
77
+ }
78
+ fmt.log.pass(`Verification gate passed for area: ${fmt.bold(area)}`);
79
+ }
80
+ // Step 2: Stage
81
+ fmt.log.info('Step 2: Staging all tracked changes...');
82
+ (0, git_js_1.git)(['add', '-A']);
83
+ // Step 3: Commit
84
+ fmt.log.info('Step 3: Committing checkpoint...');
85
+ const commitMsg = `checkpoint(${slug}): ${cpName}`;
86
+ const commitBody = `Next action: ${nextAction}`;
87
+ const commitResult = (0, git_js_1.git)(['commit', '-m', commitMsg, '-m', commitBody]);
88
+ if (commitResult.status !== 0) {
89
+ fmt.log.fail('Commit failed.');
90
+ process.exit(1);
91
+ }
92
+ // Step 4: Push
93
+ const branch = (0, git_js_1.getCurrentBranch)();
94
+ fmt.log.info(`Step 4: Pushing to origin/${fmt.branch(branch)}...`);
95
+ const pushResult = (0, git_js_1.git)(['push', 'origin', branch]);
96
+ if (pushResult.status !== 0) {
97
+ fmt.log.fail('Push failed.');
98
+ process.exit(1);
48
99
  }
49
- fmt.log.pass(`Verification gate passed for area: ${fmt.bold(area)}`);
50
- }
51
-
52
- // Step 2: Stage
53
- fmt.log.info('Step 2: Staging all tracked changes...');
54
- git(['add', '-A']);
55
-
56
- // Step 3: Commit
57
- fmt.log.info('Step 3: Committing checkpoint...');
58
- const commitMsg = `checkpoint(${slug}): ${cpName}`;
59
- const commitBody = `Next action: ${nextAction}`;
60
- const commitResult = git(['commit', '-m', commitMsg, '-m', commitBody]);
61
- if (commitResult.status !== 0) {
62
- fmt.log.fail('Commit failed.');
63
- process.exit(1);
64
- }
65
-
66
- // Step 4: Push
67
- const branch = getCurrentBranch();
68
- fmt.log.info(`Step 4: Pushing to origin/${fmt.branch(branch)}...`);
69
- const pushResult = git(['push', 'origin', branch]);
70
- if (pushResult.status !== 0) {
71
- fmt.log.fail('Push failed.');
72
- process.exit(1);
73
- }
74
-
75
- fmt.log.pass('Checkpoint complete and pushed.');
100
+ fmt.log.pass('Checkpoint complete and pushed.');
76
101
  }
77
-
78
- module.exports = checkpoint;
102
+ exports.default = checkpoint;
@@ -0,0 +1,79 @@
1
+ // @ts-nocheck
2
+ import { git, run, getCurrentBranch } from '../core/git.js';
3
+ import { findMissionDir, findMissionArea, inferSlug } from '../core/mission-utils.js';
4
+ import * as fmt from '../core/fmt.js';
5
+ import { formatVerificationCommand, runVerificationGate } from '../core/verification.js';
6
+
7
+ /** @param {string[]} args */
8
+ function checkpoint(args) {
9
+ /** @param {string} a */
10
+ const flags = args.filter(a => a.startsWith('--'));
11
+ /** @param {string} a */
12
+ const params = args.filter(a => !a.startsWith('--'));
13
+
14
+ let [explicitSlug, cpName, nextAction] = params;
15
+
16
+ // Shift if slug is inferred
17
+ let slug = inferSlug(explicitSlug);
18
+ if (slug && explicitSlug !== slug) {
19
+ // Slug was inferred, so explicitSlug is actually cpName
20
+ nextAction = cpName;
21
+ cpName = explicitSlug;
22
+ }
23
+
24
+ if (!slug || !cpName || !nextAction) {
25
+ fmt.log.fail('Usage: node parallix checkpoint [<slug>] <cp-name> "<next-action>" [--no-gate]');
26
+ process.exit(1);
27
+ }
28
+
29
+ const missionDir = findMissionDir(slug);
30
+ if (!missionDir) {
31
+ fmt.log.fail(`Mission directory not found for slug: ${fmt.slug(slug)}`);
32
+ process.exit(1);
33
+ }
34
+
35
+ const area = findMissionArea(missionDir);
36
+ const skipGate = flags.includes('--no-gate');
37
+
38
+ fmt.log.info(`Running checkpoint for mission: ${fmt.slug(slug)}, checkpoint: ${fmt.bold(cpName)}`);
39
+
40
+ // Step 1: Verify
41
+ if (skipGate) {
42
+ fmt.log.warn('Step 1: Skipping verification gate (--no-gate)');
43
+ } else {
44
+ fmt.log.info(`Step 1: Running verification gate for area: ${fmt.bold(area)}...`);
45
+ const verifyResult = runVerificationGate(area, { rootDir: process.cwd(), stdio: 'inherit', runFn: run });
46
+ if (verifyResult.status !== 0) {
47
+ fmt.log.fail(`Verification gate failed for area: ${fmt.bold(area)}. Fix errors and retry ${fmt.command(formatVerificationCommand(area))} or use --no-gate.`);
48
+ process.exit(1);
49
+ }
50
+ fmt.log.pass(`Verification gate passed for area: ${fmt.bold(area)}`);
51
+ }
52
+
53
+ // Step 2: Stage
54
+ fmt.log.info('Step 2: Staging all tracked changes...');
55
+ git(['add', '-A']);
56
+
57
+ // Step 3: Commit
58
+ fmt.log.info('Step 3: Committing checkpoint...');
59
+ const commitMsg = `checkpoint(${slug}): ${cpName}`;
60
+ const commitBody = `Next action: ${nextAction}`;
61
+ const commitResult = git(['commit', '-m', commitMsg, '-m', commitBody]);
62
+ if (commitResult.status !== 0) {
63
+ fmt.log.fail('Commit failed.');
64
+ process.exit(1);
65
+ }
66
+
67
+ // Step 4: Push
68
+ const branch = getCurrentBranch();
69
+ fmt.log.info(`Step 4: Pushing to origin/${fmt.branch(branch)}...`);
70
+ const pushResult = git(['push', 'origin', branch]);
71
+ if (pushResult.status !== 0) {
72
+ fmt.log.fail('Push failed.');
73
+ process.exit(1);
74
+ }
75
+
76
+ fmt.log.pass('Checkpoint complete and pushed.');
77
+ }
78
+
79
+ export default checkpoint;
@@ -1,41 +1,71 @@
1
1
  #!/usr/bin/env node
2
-
3
- const fmt = require('../core/fmt');
4
- const { loadEffectiveConfig, loadWorkflowConfig, validateWorkflowConfig } = require('../core/product-config');
5
-
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ const fmt = __importStar(require("../core/fmt.js"));
37
+ const product_config_js_1 = require("../core/product-config.js");
6
38
  // `node parallix config` — read-only. Prints the effective configuration:
7
39
  // code-owned defaults merged with the optional workflow.config.json override.
8
40
  // This replaces the deleted workflow.config.json.example as the way to discover
9
41
  // the configurable surface, with no copy-paste footgun and no second source of
10
42
  // truth to drift from (task-1233 Scope Amendment).
11
- /** @param {string[]} _args @param {{logFn?: Function, errorFn?: Function, exitFn?: Function, rootDir?: string}} opts */
12
43
  async function config(_args = [], opts = {}) {
13
- void _args; // intentionally unused — part of public API signature
14
- const logFn = opts.logFn || fmt.log.plain;
15
- const errorFn = opts.errorFn || fmt.log.plainError;
16
- const exitFn = opts.exitFn || /** @type{(code: number) => void} */ ((code) => { process.exitCode = code; });
17
- const rootDir = opts.rootDir || process.cwd();
18
-
19
- const loaded = loadWorkflowConfig(rootDir);
20
- if (!loaded.found) {
21
- logFn(fmt.status('INFO', 'No workflow.config.json found — showing built-in defaults.'));
22
- } else if (loaded.parseError) {
23
- errorFn(fmt.status('FAIL', `workflow.config.json is invalid JSON (${/** @type{Error} */(loaded.parseError).message}); showing fallback built-in defaults.`));
24
- logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
25
- exitFn(1);
26
- return;
27
- } else {
28
- const issues = validateWorkflowConfig(loaded.config);
29
- if (issues.length > 0) {
30
- errorFn(fmt.status('FAIL', `workflow.config.json is structurally invalid: ${issues.join('; ')}; showing fallback built-in defaults.`));
31
- logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
32
- exitFn(1);
33
- return;
44
+ void _args; // intentionally unused — part of public API signature
45
+ const logFn = opts.logFn || fmt.log.plain;
46
+ const errorFn = opts.errorFn || fmt.log.plainError;
47
+ const exitFn = opts.exitFn || ((code) => { process.exitCode = code; });
48
+ const rootDir = opts.rootDir || process.cwd();
49
+ const loaded = (0, product_config_js_1.loadWorkflowConfig)(rootDir);
50
+ if (!loaded.found) {
51
+ logFn(fmt.status('INFO', 'No workflow.config.json found — showing built-in defaults.'));
52
+ }
53
+ else if (loaded.parseError) {
54
+ errorFn(fmt.status('FAIL', `workflow.config.json is invalid JSON (${ /** @type{Error} */(loaded.parseError).message}); showing fallback built-in defaults.`));
55
+ logFn(JSON.stringify((0, product_config_js_1.loadEffectiveConfig)(rootDir), null, 2));
56
+ exitFn(1);
57
+ return;
58
+ }
59
+ else {
60
+ const issues = (0, product_config_js_1.validateWorkflowConfig)(loaded.config);
61
+ if (issues.length > 0) {
62
+ errorFn(fmt.status('FAIL', `workflow.config.json is structurally invalid: ${issues.join('; ')}; showing fallback built-in defaults.`));
63
+ logFn(JSON.stringify((0, product_config_js_1.loadEffectiveConfig)(rootDir), null, 2));
64
+ exitFn(1);
65
+ return;
66
+ }
67
+ logFn(fmt.status('INFO', `Effective config (built-in defaults + ${loaded.configPath}):`));
34
68
  }
35
- logFn(fmt.status('INFO', `Effective config (built-in defaults + ${loaded.configPath}):`));
36
- }
37
-
38
- logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
69
+ logFn(JSON.stringify((0, product_config_js_1.loadEffectiveConfig)(rootDir), null, 2));
39
70
  }
40
-
41
71
  module.exports = config;
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as fmt from '../core/fmt.js';
4
+ import { loadEffectiveConfig, loadWorkflowConfig, validateWorkflowConfig } from '../core/product-config.js';
5
+
6
+ interface ConfigOptions {
7
+ logFn?: (msg: string) => void;
8
+ errorFn?: (msg: string) => void;
9
+ exitFn?: (code: number) => void;
10
+ rootDir?: string;
11
+ }
12
+
13
+ // `node parallix config` — read-only. Prints the effective configuration:
14
+ // code-owned defaults merged with the optional workflow.config.json override.
15
+ // This replaces the deleted workflow.config.json.example as the way to discover
16
+ // the configurable surface, with no copy-paste footgun and no second source of
17
+ // truth to drift from (task-1233 Scope Amendment).
18
+ async function config(_args: string[] = [], opts: ConfigOptions = {}) {
19
+ void _args; // intentionally unused — part of public API signature
20
+ const logFn = opts.logFn || fmt.log.plain;
21
+ const errorFn = opts.errorFn || fmt.log.plainError;
22
+ const exitFn = opts.exitFn || ((code: number) => { process.exitCode = code; });
23
+ const rootDir = opts.rootDir || process.cwd();
24
+
25
+ const loaded = loadWorkflowConfig(rootDir);
26
+ if (!loaded.found) {
27
+ logFn(fmt.status('INFO', 'No workflow.config.json found — showing built-in defaults.'));
28
+ } else if (loaded.parseError) {
29
+ errorFn(fmt.status('FAIL', `workflow.config.json is invalid JSON (${/** @type{Error} */(loaded.parseError).message}); showing fallback built-in defaults.`));
30
+ logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
31
+ exitFn(1);
32
+ return;
33
+ } else {
34
+ const issues = validateWorkflowConfig(loaded.config);
35
+ if (issues.length > 0) {
36
+ errorFn(fmt.status('FAIL', `workflow.config.json is structurally invalid: ${issues.join('; ')}; showing fallback built-in defaults.`));
37
+ logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
38
+ exitFn(1);
39
+ return;
40
+ }
41
+ logFn(fmt.status('INFO', `Effective config (built-in defaults + ${loaded.configPath}):`));
42
+ }
43
+
44
+ logFn(JSON.stringify(loadEffectiveConfig(rootDir), null, 2));
45
+ }
46
+
47
+ export = config;