@magnusekdahl/parallix 1.2.0 → 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 (127) hide show
  1. package/index.js +190 -185
  2. package/lib/agents/agents.js +737 -838
  3. package/lib/agents/agents.ts +930 -0
  4. package/lib/agents/claude-telemetry.js +149 -154
  5. package/lib/agents/claude-telemetry.ts +233 -0
  6. package/lib/agents/claude.js +121 -131
  7. package/lib/agents/claude.ts +156 -0
  8. package/lib/agents/codex-telemetry.js +176 -164
  9. package/lib/agents/codex-telemetry.ts +205 -0
  10. package/lib/agents/codex.js +168 -201
  11. package/lib/agents/codex.ts +236 -0
  12. package/lib/agents/limit-hit.js +216 -226
  13. package/lib/agents/limit-hit.ts +262 -0
  14. package/lib/agents/mistral-telemetry.js +7 -10
  15. package/lib/agents/mistral-telemetry.ts +44 -0
  16. package/lib/agents/mistral.js +40 -58
  17. package/lib/agents/mistral.ts +84 -0
  18. package/lib/agents/opencode-export.js +126 -127
  19. package/lib/agents/opencode-export.ts +160 -0
  20. package/lib/agents/opencode-telemetry.js +282 -261
  21. package/lib/agents/opencode-telemetry.ts +350 -0
  22. package/lib/agents/opencode.js +263 -305
  23. package/lib/agents/opencode.ts +370 -0
  24. package/lib/agents/stage-telemetry.js +23 -24
  25. package/lib/agents/stage-telemetry.ts +47 -0
  26. package/lib/commands/active.js +502 -579
  27. package/lib/commands/active.ts +665 -0
  28. package/lib/commands/checkpoint.js +98 -74
  29. package/lib/commands/checkpoint.ts +79 -0
  30. package/lib/commands/config.js +61 -31
  31. package/lib/commands/config.ts +47 -0
  32. package/lib/commands/coverage-gate.js +271 -246
  33. package/lib/commands/coverage-gate.ts +362 -0
  34. package/lib/commands/diff.js +140 -119
  35. package/lib/commands/diff.ts +125 -0
  36. package/lib/commands/draft.js +770 -923
  37. package/lib/commands/draft.ts +1021 -0
  38. package/lib/commands/handoff.js +588 -608
  39. package/lib/commands/handoff.ts +694 -0
  40. package/lib/commands/integrate.js +1332 -1463
  41. package/lib/commands/integrate.ts +1688 -0
  42. package/lib/commands/mission-start.js +279 -237
  43. package/lib/commands/mission-start.ts +263 -0
  44. package/lib/commands/rebase.js +606 -582
  45. package/lib/commands/rebase.ts +635 -0
  46. package/lib/commands/repair-handoff.js +181 -179
  47. package/lib/commands/repair-handoff.ts +228 -0
  48. package/lib/commands/resolve-conflict.js +121 -102
  49. package/lib/commands/resolve-conflict.ts +112 -0
  50. package/lib/commands/review.js +3 -4
  51. package/lib/commands/review.ts +14 -0
  52. package/lib/commands/setup-review.js +4 -5
  53. package/lib/commands/setup-review.ts +14 -0
  54. package/lib/commands/setup.js +3 -3
  55. package/lib/commands/setup.ts +2 -0
  56. package/lib/commands/stats-backfill.js +394 -359
  57. package/lib/commands/stats-backfill.ts +418 -0
  58. package/lib/commands/stats.js +1424 -1665
  59. package/lib/commands/stats.ts +2186 -0
  60. package/lib/commands/status.js +219 -200
  61. package/lib/commands/status.ts +219 -0
  62. package/lib/commands/verify.js +3 -1
  63. package/lib/commands/verify.ts +2 -0
  64. package/lib/core/fmt.js +140 -211
  65. package/lib/core/fmt.ts +189 -0
  66. package/lib/core/git.js +121 -102
  67. package/lib/core/git.ts +146 -0
  68. package/lib/core/gitignore.js +59 -106
  69. package/lib/core/gitignore.ts +103 -0
  70. package/lib/core/mission-utils.js +833 -875
  71. package/lib/core/mission-utils.ts +998 -0
  72. package/lib/core/nels.js +112 -127
  73. package/lib/core/nels.ts +189 -0
  74. package/lib/core/persistent-data-migration.js +247 -205
  75. package/lib/core/persistent-data-migration.ts +255 -0
  76. package/lib/core/product-config.js +400 -476
  77. package/lib/core/product-config.ts +518 -0
  78. package/lib/core/runtime-matrix.js +41 -87
  79. package/lib/core/runtime-matrix.ts +84 -0
  80. package/lib/core/spawn-tee.js +152 -192
  81. package/lib/core/spawn-tee.ts +202 -0
  82. package/lib/core/state-map.js +92 -92
  83. package/lib/core/state-map.ts +115 -0
  84. package/lib/core/storage.js +101 -129
  85. package/lib/core/storage.ts +182 -0
  86. package/lib/core/subagent-limit.js +15 -16
  87. package/lib/core/subagent-limit.ts +25 -0
  88. package/lib/core/verification.js +148 -138
  89. package/lib/core/verification.ts +200 -0
  90. package/lib/index.js +142 -59
  91. package/lib/index.ts +134 -0
  92. package/lib/review/rebase.js +142 -145
  93. package/lib/review/rebase.ts +188 -0
  94. package/lib/review/review-adapter.js +99 -87
  95. package/lib/review/review-adapter.ts +198 -0
  96. package/lib/review/review-artifacts.js +486 -629
  97. package/lib/review/review-artifacts.ts +622 -0
  98. package/lib/review/review-commands.js +1220 -1373
  99. package/lib/review/review-commands.ts +1579 -0
  100. package/lib/review/review-events.js +544 -886
  101. package/lib/review/review-events.ts +954 -0
  102. package/lib/review/review-loop.js +945 -1020
  103. package/lib/review/review-loop.ts +1174 -0
  104. package/lib/review/review-polling.js +86 -115
  105. package/lib/review/review-polling.ts +194 -0
  106. package/lib/review/review-prompts.js +154 -189
  107. package/lib/review/review-prompts.ts +182 -0
  108. package/lib/review/review-state.js +246 -225
  109. package/lib/review/review-state.ts +353 -0
  110. package/lib/review/review.js +63 -84
  111. package/lib/review/review.ts +138 -0
  112. package/lib/tools/backlog.js +694 -680
  113. package/lib/tools/backlog.ts +835 -0
  114. package/lib/tools/forgejo.js +1270 -1405
  115. package/lib/tools/forgejo.ts +1793 -0
  116. package/lib/tools/gatekeeper.js +106 -96
  117. package/lib/tools/gatekeeper.ts +124 -0
  118. package/lib/tools/redgreen.js +200 -181
  119. package/lib/tools/redgreen.ts +221 -0
  120. package/lib/tools/sessions.js +58 -52
  121. package/lib/tools/sessions.ts +81 -0
  122. package/lib/tools/setup-review.js +902 -975
  123. package/lib/tools/setup-review.ts +1152 -0
  124. package/package.json +7 -5
  125. package/prompts/draft.md +1 -0
  126. package/prompts/portfolio.md +1 -1
  127. package/px.js +226 -210
@@ -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;