@magnusekdahl/parallix 1.2.1 → 1.3.1

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 (81) hide show
  1. package/index.js +190 -185
  2. package/lib/agents/agents.js +34 -4
  3. package/lib/agents/agents.ts +40 -5
  4. package/lib/commands/active.js +502 -579
  5. package/lib/commands/active.ts +665 -0
  6. package/lib/commands/checkpoint.js +98 -74
  7. package/lib/commands/checkpoint.ts +79 -0
  8. package/lib/commands/config.js +61 -31
  9. package/lib/commands/config.ts +47 -0
  10. package/lib/commands/coverage-gate.js +271 -246
  11. package/lib/commands/coverage-gate.ts +362 -0
  12. package/lib/commands/diff.js +140 -119
  13. package/lib/commands/diff.ts +125 -0
  14. package/lib/commands/draft.js +770 -923
  15. package/lib/commands/draft.ts +1021 -0
  16. package/lib/commands/handoff.js +588 -608
  17. package/lib/commands/handoff.ts +694 -0
  18. package/lib/commands/integrate.js +1331 -1468
  19. package/lib/commands/integrate.ts +1688 -0
  20. package/lib/commands/mission-start.js +279 -237
  21. package/lib/commands/mission-start.ts +263 -0
  22. package/lib/commands/rebase.js +606 -582
  23. package/lib/commands/rebase.ts +635 -0
  24. package/lib/commands/repair-handoff.js +181 -179
  25. package/lib/commands/repair-handoff.ts +228 -0
  26. package/lib/commands/resolve-conflict.js +121 -102
  27. package/lib/commands/resolve-conflict.ts +112 -0
  28. package/lib/commands/review.js +3 -4
  29. package/lib/commands/review.ts +14 -0
  30. package/lib/commands/setup-review.js +4 -5
  31. package/lib/commands/setup-review.ts +14 -0
  32. package/lib/commands/setup.js +3 -3
  33. package/lib/commands/setup.ts +2 -0
  34. package/lib/commands/stats-backfill.js +394 -359
  35. package/lib/commands/stats-backfill.ts +418 -0
  36. package/lib/commands/stats.js +1424 -1665
  37. package/lib/commands/stats.ts +2186 -0
  38. package/lib/commands/status.js +219 -200
  39. package/lib/commands/status.ts +219 -0
  40. package/lib/commands/verify.js +3 -1
  41. package/lib/commands/verify.ts +2 -0
  42. package/lib/core/nels.js +112 -127
  43. package/lib/core/nels.ts +189 -0
  44. package/lib/core/subagent-limit.js +15 -16
  45. package/lib/core/subagent-limit.ts +25 -0
  46. package/lib/index.js +142 -59
  47. package/lib/index.ts +134 -0
  48. package/lib/review/rebase.js +142 -145
  49. package/lib/review/rebase.ts +188 -0
  50. package/lib/review/review-adapter.js +99 -87
  51. package/lib/review/review-adapter.ts +198 -0
  52. package/lib/review/review-artifacts.js +486 -629
  53. package/lib/review/review-artifacts.ts +622 -0
  54. package/lib/review/review-commands.js +1220 -1373
  55. package/lib/review/review-commands.ts +1579 -0
  56. package/lib/review/review-events.js +544 -886
  57. package/lib/review/review-events.ts +954 -0
  58. package/lib/review/review-loop.js +945 -1020
  59. package/lib/review/review-loop.ts +1174 -0
  60. package/lib/review/review-polling.js +86 -115
  61. package/lib/review/review-polling.ts +194 -0
  62. package/lib/review/review-prompts.js +154 -189
  63. package/lib/review/review-prompts.ts +182 -0
  64. package/lib/review/review-state.js +246 -225
  65. package/lib/review/review-state.ts +353 -0
  66. package/lib/review/review.js +63 -84
  67. package/lib/review/review.ts +138 -0
  68. package/lib/tools/backlog.js +694 -680
  69. package/lib/tools/backlog.ts +835 -0
  70. package/lib/tools/forgejo.js +1270 -1405
  71. package/lib/tools/forgejo.ts +1793 -0
  72. package/lib/tools/gatekeeper.js +106 -96
  73. package/lib/tools/gatekeeper.ts +124 -0
  74. package/lib/tools/redgreen.js +200 -181
  75. package/lib/tools/redgreen.ts +221 -0
  76. package/lib/tools/sessions.js +58 -52
  77. package/lib/tools/sessions.ts +81 -0
  78. package/lib/tools/setup-review.js +902 -975
  79. package/lib/tools/setup-review.ts +1152 -0
  80. package/package.json +4 -4
  81. 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;