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