pumuki 6.3.39 → 6.3.40
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/README.md +21 -12
- package/VERSION +1 -1
- package/core/gate/evaluateRules.test.ts +40 -0
- package/core/gate/evaluateRules.ts +7 -1
- package/core/rules/Consequence.ts +1 -0
- package/docs/CONFIGURATION.md +50 -0
- package/docs/INSTALLATION.md +38 -11
- package/docs/MCP_SERVERS.md +1 -1
- package/docs/README.md +1 -0
- package/docs/RELEASE_NOTES.md +44 -0
- package/docs/USAGE.md +191 -9
- package/docs/registro-maestro-de-seguimiento.md +2 -2
- package/docs/seguimiento-activo-pumuki-saas-supermercados.md +1592 -1
- package/docs/validation/README.md +2 -1
- package/docs/validation/ast-intelligence-roadmap.md +96 -0
- package/integrations/config/skillsCustomRules.ts +14 -0
- package/integrations/config/skillsDetectorRegistry.ts +11 -1
- package/integrations/config/skillsLock.ts +30 -0
- package/integrations/config/skillsMarkdownRules.ts +14 -3
- package/integrations/config/skillsRuleSet.ts +25 -3
- package/integrations/evidence/readEvidence.test.ts +3 -2
- package/integrations/evidence/readEvidence.ts +14 -4
- package/integrations/evidence/repoState.ts +10 -2
- package/integrations/evidence/schema.test.ts +3 -2
- package/integrations/evidence/schema.ts +3 -0
- package/integrations/evidence/writeEvidence.test.ts +3 -2
- package/integrations/gate/evaluateAiGate.ts +511 -2
- package/integrations/git/GitService.ts +5 -1
- package/integrations/git/astIntelligenceDualValidation.ts +275 -0
- package/integrations/git/gitAtomicity.ts +42 -9
- package/integrations/git/resolveGitRefs.ts +37 -0
- package/integrations/git/runPlatformGate.ts +228 -1
- package/integrations/git/runPlatformGateEvaluation.ts +4 -0
- package/integrations/git/stageRunners.ts +116 -2
- package/integrations/lifecycle/cli.ts +759 -22
- package/integrations/lifecycle/doctor.ts +62 -0
- package/integrations/lifecycle/index.ts +1 -0
- package/integrations/lifecycle/packageInfo.ts +25 -3
- package/integrations/lifecycle/policyReconcile.ts +304 -0
- package/integrations/lifecycle/preWriteAutomation.ts +42 -2
- package/integrations/lifecycle/watch.ts +365 -0
- package/integrations/mcp/aiGateCheck.ts +59 -2
- package/integrations/mcp/autoExecuteAiStart.ts +25 -1
- package/integrations/mcp/preFlightCheck.ts +13 -0
- package/integrations/sdd/evidenceScaffold.ts +223 -0
- package/integrations/sdd/index.ts +2 -0
- package/integrations/sdd/stateSync.ts +400 -0
- package/integrations/sdd/syncDocs.ts +97 -2
- package/package.json +4 -1
- package/scripts/backlog-action-reasons-lib.ts +38 -0
- package/scripts/backlog-id-issue-map-lib.ts +69 -0
- package/scripts/backlog-json-contract-lib.ts +3 -0
- package/scripts/framework-menu-consumer-preflight-lib.ts +6 -0
- package/scripts/package-install-smoke-command-resolution-lib.ts +64 -0
- package/scripts/package-install-smoke-consumer-npm-lib.ts +43 -0
- package/scripts/package-install-smoke-consumer-repo-setup-lib.ts +2 -0
- package/scripts/package-install-smoke-execution-steps-lib.ts +27 -9
- package/scripts/package-install-smoke-lifecycle-lib.ts +15 -4
- package/scripts/package-install-smoke-workspace-factory-lib.ts +4 -1
- package/scripts/reconcile-consumer-backlog-issues-lib.ts +651 -0
- package/scripts/reconcile-consumer-backlog-issues.ts +348 -0
- package/scripts/watch-consumer-backlog-lib.ts +465 -0
- package/scripts/watch-consumer-backlog.ts +326 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
buildWatchActionRequiredReasons,
|
|
6
|
+
formatActionReasonsForHuman,
|
|
7
|
+
} from './backlog-action-reasons-lib';
|
|
8
|
+
import {
|
|
9
|
+
mergeIdIssueMapRecords,
|
|
10
|
+
parseIdIssueMapRecordFile,
|
|
11
|
+
} from './backlog-id-issue-map-lib';
|
|
12
|
+
import {
|
|
13
|
+
BACKLOG_JSON_COMPAT_CONTRACT_ID,
|
|
14
|
+
BACKLOG_JSON_COMPAT_MIN_READER_VERSION,
|
|
15
|
+
BACKLOG_JSON_SCHEMA_VERSION,
|
|
16
|
+
} from './backlog-json-contract-lib';
|
|
17
|
+
import {
|
|
18
|
+
collectBacklogIdIssueMap,
|
|
19
|
+
resolveIssueNumberByIdWithGh,
|
|
20
|
+
runBacklogWatch,
|
|
21
|
+
} from './watch-consumer-backlog-lib';
|
|
22
|
+
|
|
23
|
+
type ParsedArgs = {
|
|
24
|
+
filePath: string;
|
|
25
|
+
repo?: string;
|
|
26
|
+
idIssueMapPath?: string;
|
|
27
|
+
idIssueMapSourcePath?: string;
|
|
28
|
+
resolveMissingViaGh: boolean;
|
|
29
|
+
json: boolean;
|
|
30
|
+
failOnFindings: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const JSON_TOOL_NAME = 'backlog-watch';
|
|
34
|
+
|
|
35
|
+
const shellQuote = (value: string): string => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
36
|
+
|
|
37
|
+
const HELP_TEXT = `Usage:
|
|
38
|
+
npx --yes tsx@4.21.0 scripts/watch-consumer-backlog.ts --file=<markdown-path> [--repo=<owner/name>] [--id-issue-map=<json-path>] [--id-issue-map-from=<md-path>] [--resolve-missing-via-gh] [--json] [--no-fail]
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--file=<path> Ruta del backlog markdown consumidor a vigilar.
|
|
42
|
+
--repo=<owner/name> Repositorio GitHub a consultar con gh CLI (default: repo remoto actual).
|
|
43
|
+
--id-issue-map=<path> JSON con mapeo {"ID":"issueNumber"} para enlazar filas sin #issue.
|
|
44
|
+
--id-issue-map-from=<path> Markdown canónico desde el que extraer mapeo ID->issue automáticamente.
|
|
45
|
+
--resolve-missing-via-gh Enriquecer IDs sin issue buscando en GitHub por token (title/body).
|
|
46
|
+
--json Imprime resultado en JSON.
|
|
47
|
+
--no-fail No devuelve exit code 1 aunque existan findings accionables.`;
|
|
48
|
+
|
|
49
|
+
class HelpRequestedError extends Error {
|
|
50
|
+
constructor() {
|
|
51
|
+
super(HELP_TEXT);
|
|
52
|
+
this.name = 'HelpRequestedError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const parseArgs = (argv: ReadonlyArray<string>): ParsedArgs => {
|
|
57
|
+
let filePath: string | undefined;
|
|
58
|
+
let repo: string | undefined;
|
|
59
|
+
let idIssueMapPath: string | undefined;
|
|
60
|
+
let idIssueMapSourcePath: string | undefined;
|
|
61
|
+
let resolveMissingViaGh = false;
|
|
62
|
+
let json = false;
|
|
63
|
+
let failOnFindings = true;
|
|
64
|
+
|
|
65
|
+
for (const arg of argv) {
|
|
66
|
+
if (arg === '--help' || arg === '-h') {
|
|
67
|
+
throw new HelpRequestedError();
|
|
68
|
+
}
|
|
69
|
+
if (arg === '--json') {
|
|
70
|
+
json = true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (arg === '--no-fail') {
|
|
74
|
+
failOnFindings = false;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (arg === '--resolve-missing-via-gh') {
|
|
78
|
+
resolveMissingViaGh = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (arg.startsWith('--file=')) {
|
|
82
|
+
filePath = arg.slice('--file='.length).trim();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (arg.startsWith('--repo=')) {
|
|
86
|
+
repo = arg.slice('--repo='.length).trim();
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (arg.startsWith('--id-issue-map=')) {
|
|
90
|
+
idIssueMapPath = arg.slice('--id-issue-map='.length).trim();
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (arg.startsWith('--id-issue-map-from=')) {
|
|
94
|
+
idIssueMapSourcePath = arg.slice('--id-issue-map-from='.length).trim();
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Unknown argument "${arg}"\n\n${HELP_TEXT}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!filePath) {
|
|
101
|
+
throw new Error(`Missing --file\n\n${HELP_TEXT}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
filePath: resolve(filePath),
|
|
106
|
+
repo: repo && repo.length > 0 ? repo : undefined,
|
|
107
|
+
idIssueMapPath: idIssueMapPath && idIssueMapPath.length > 0 ? resolve(idIssueMapPath) : undefined,
|
|
108
|
+
idIssueMapSourcePath:
|
|
109
|
+
idIssueMapSourcePath && idIssueMapSourcePath.length > 0
|
|
110
|
+
? resolve(idIssueMapSourcePath)
|
|
111
|
+
: undefined,
|
|
112
|
+
resolveMissingViaGh,
|
|
113
|
+
json,
|
|
114
|
+
failOnFindings,
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const formatHumanOutput = (result: Awaited<ReturnType<typeof runBacklogWatch>>): string => {
|
|
119
|
+
const lines: string[] = [];
|
|
120
|
+
const actionRequiredReasons = buildWatchActionRequiredReasons({
|
|
121
|
+
needsIssueCount: result.classification.needsIssue.length,
|
|
122
|
+
driftClosedIssueCount: result.classification.driftClosedIssue.length,
|
|
123
|
+
headingDriftCount: result.headingDrift.length,
|
|
124
|
+
});
|
|
125
|
+
lines.push(`[pumuki][backlog-watch] file=${result.filePath}`);
|
|
126
|
+
lines.push(
|
|
127
|
+
`[pumuki][backlog-watch] entries=${result.entriesScanned} non_closed=${result.nonClosedEntries} issue_states_resolved=${result.issueStatesResolved}`
|
|
128
|
+
);
|
|
129
|
+
lines.push(
|
|
130
|
+
`[pumuki][backlog-watch] needs_issue=${result.classification.needsIssue.length} drift_closed=${result.classification.driftClosedIssue.length} active_issue=${result.classification.activeIssue.length}`
|
|
131
|
+
);
|
|
132
|
+
lines.push(`[pumuki][backlog-watch] heading_drift=${result.headingDrift.length}`);
|
|
133
|
+
lines.push(
|
|
134
|
+
`[pumuki][backlog-watch] resolved_by_map=${result.resolution.resolvedByMap.length} resolved_by_gh_lookup=${result.resolution.resolvedByGhLookup.length} unresolved_ids=${result.resolution.unresolvedIds.length}`
|
|
135
|
+
);
|
|
136
|
+
if (result.resolution.resolvedByMap.length > 0) {
|
|
137
|
+
lines.push(`[pumuki][backlog-watch] resolved_by_map_ids=${result.resolution.resolvedByMap.join(',')}`);
|
|
138
|
+
}
|
|
139
|
+
if (result.resolution.resolvedByGhLookup.length > 0) {
|
|
140
|
+
lines.push(
|
|
141
|
+
`[pumuki][backlog-watch] resolved_by_gh_lookup_ids=${result.resolution.resolvedByGhLookup.join(',')}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (result.resolution.unresolvedIds.length > 0) {
|
|
145
|
+
lines.push(`[pumuki][backlog-watch] unresolved_ids_list=${result.resolution.unresolvedIds.join(',')}`);
|
|
146
|
+
}
|
|
147
|
+
if (result.classification.needsIssue.length > 0) {
|
|
148
|
+
lines.push('[pumuki][backlog-watch] needs_issue_entries:');
|
|
149
|
+
for (const entry of result.classification.needsIssue) {
|
|
150
|
+
lines.push(`- line ${entry.lineNumber} ${entry.id} status=${entry.status} upstream=missing`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (result.classification.driftClosedIssue.length > 0) {
|
|
154
|
+
lines.push('[pumuki][backlog-watch] drift_closed_entries:');
|
|
155
|
+
for (const entry of result.classification.driftClosedIssue) {
|
|
156
|
+
lines.push(`- line ${entry.lineNumber} ${entry.id} status=${entry.status} issue=#${entry.issueNumber}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (result.classification.activeIssue.length > 0) {
|
|
160
|
+
lines.push('[pumuki][backlog-watch] active_issue_entries:');
|
|
161
|
+
for (const entry of result.classification.activeIssue) {
|
|
162
|
+
lines.push(`- line ${entry.lineNumber} ${entry.id} status=${entry.status} issue=#${entry.issueNumber}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (result.headingDrift.length > 0) {
|
|
166
|
+
lines.push('[pumuki][backlog-watch] heading_drift_entries:');
|
|
167
|
+
for (const drift of result.headingDrift) {
|
|
168
|
+
lines.push(
|
|
169
|
+
`- line ${drift.lineNumber} ${drift.id}: heading=${drift.headingStatus} effective=${drift.effectiveStatus}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
lines.push(
|
|
174
|
+
`[pumuki][backlog-watch] action_required_reasons=${formatActionReasonsForHuman(actionRequiredReasons)}`
|
|
175
|
+
);
|
|
176
|
+
if (result.hasActionRequired) {
|
|
177
|
+
lines.push('[pumuki][backlog-watch] hint=use --no-fail to inspect findings without exit code 1');
|
|
178
|
+
}
|
|
179
|
+
lines.push(`[pumuki][backlog-watch] action_required=${result.hasActionRequired ? 'yes' : 'no'}`);
|
|
180
|
+
return `${lines.join('\n')}\n`;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const main = async (): Promise<void> => {
|
|
184
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
185
|
+
const fileMap = parsed.idIssueMapPath ? parseIdIssueMapRecordFile(parsed.idIssueMapPath) : undefined;
|
|
186
|
+
const markdownMap = parsed.idIssueMapSourcePath
|
|
187
|
+
? collectBacklogIdIssueMap(readFileSync(parsed.idIssueMapSourcePath, 'utf8'))
|
|
188
|
+
: undefined;
|
|
189
|
+
const idIssueMap = mergeIdIssueMapRecords(markdownMap, fileMap);
|
|
190
|
+
|
|
191
|
+
const result = await runBacklogWatch({
|
|
192
|
+
filePath: parsed.filePath,
|
|
193
|
+
repo: parsed.repo,
|
|
194
|
+
idIssueMap,
|
|
195
|
+
resolveIssueNumberById: parsed.resolveMissingViaGh ? resolveIssueNumberByIdWithGh : undefined,
|
|
196
|
+
});
|
|
197
|
+
const actionRequiredReasons = buildWatchActionRequiredReasons({
|
|
198
|
+
needsIssueCount: result.classification.needsIssue.length,
|
|
199
|
+
driftClosedIssueCount: result.classification.driftClosedIssue.length,
|
|
200
|
+
headingDriftCount: result.headingDrift.length,
|
|
201
|
+
});
|
|
202
|
+
const dryRunCommand = `npx --yes tsx@4.21.0 scripts/reconcile-consumer-backlog-issues.ts --file=${shellQuote(
|
|
203
|
+
parsed.filePath
|
|
204
|
+
)} --json`;
|
|
205
|
+
const applyCommand = `npx --yes tsx@4.21.0 scripts/reconcile-consumer-backlog-issues.ts --file=${shellQuote(
|
|
206
|
+
parsed.filePath
|
|
207
|
+
)} --apply`;
|
|
208
|
+
const nextCommands = result.hasActionRequired
|
|
209
|
+
? [
|
|
210
|
+
{
|
|
211
|
+
id: 1,
|
|
212
|
+
origin_tool: JSON_TOOL_NAME,
|
|
213
|
+
label: 'dry_run',
|
|
214
|
+
recommendation_type: 'reconcile_loop',
|
|
215
|
+
priority: 'high',
|
|
216
|
+
mode: 'dry-run',
|
|
217
|
+
safety: 'read_only',
|
|
218
|
+
idempotent: true,
|
|
219
|
+
max_retries: 2,
|
|
220
|
+
retry_strategy: 'immediate',
|
|
221
|
+
estimated_duration_ms: 3000,
|
|
222
|
+
requires_confirmation: false,
|
|
223
|
+
depends_on: null,
|
|
224
|
+
description: 'Inspect planned reconcile changes without mutating files.',
|
|
225
|
+
expected_outcome: 'Dry-run plan generated without file mutations.',
|
|
226
|
+
success_criteria: 'plan_generated_no_mutation',
|
|
227
|
+
success_probe: 'Check JSON includes dry_run/apply next_commands without mutating files.',
|
|
228
|
+
probe_kind: 'json_contract',
|
|
229
|
+
probe_timeout_ms: 1500,
|
|
230
|
+
failure_hint: 'Re-run dry_run with --json and inspect action_required_reasons before applying.',
|
|
231
|
+
command: dryRunCommand,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 2,
|
|
235
|
+
origin_tool: JSON_TOOL_NAME,
|
|
236
|
+
label: 'apply',
|
|
237
|
+
recommendation_type: 'reconcile_loop',
|
|
238
|
+
priority: 'medium',
|
|
239
|
+
mode: 'apply',
|
|
240
|
+
safety: 'mutating',
|
|
241
|
+
idempotent: true,
|
|
242
|
+
max_retries: 0,
|
|
243
|
+
retry_strategy: 'manual',
|
|
244
|
+
estimated_duration_ms: 5000,
|
|
245
|
+
requires_confirmation: true,
|
|
246
|
+
depends_on: 'dry_run',
|
|
247
|
+
description: 'Apply reconcile changes to the backlog markdown file.',
|
|
248
|
+
expected_outcome: 'Backlog markdown reconciled and persisted.',
|
|
249
|
+
success_criteria: 'backlog_reconciled_persisted',
|
|
250
|
+
success_probe: 'Re-run watch --json and confirm action_required_reasons are reduced or none.',
|
|
251
|
+
probe_kind: 'state_recheck',
|
|
252
|
+
probe_timeout_ms: 4000,
|
|
253
|
+
failure_hint: 'Run dry_run first, then retry apply on a clean worktree with file write permissions.',
|
|
254
|
+
command: applyCommand,
|
|
255
|
+
},
|
|
256
|
+
]
|
|
257
|
+
: undefined;
|
|
258
|
+
const nextCommand = nextCommands ? `${dryRunCommand} && ${applyCommand}` : undefined;
|
|
259
|
+
const nextCommandReason = nextCommand ? actionRequiredReasons[0] : undefined;
|
|
260
|
+
|
|
261
|
+
if (parsed.json) {
|
|
262
|
+
const generatedAt = new Date().toISOString();
|
|
263
|
+
const runId = randomUUID();
|
|
264
|
+
const nextCommandsWithExecutionGroup = nextCommands?.map((step) => ({
|
|
265
|
+
...step,
|
|
266
|
+
execution_group_id: runId,
|
|
267
|
+
origin_schema_version: BACKLOG_JSON_SCHEMA_VERSION,
|
|
268
|
+
origin_contract_id: BACKLOG_JSON_COMPAT_CONTRACT_ID,
|
|
269
|
+
}));
|
|
270
|
+
writeFileSync(
|
|
271
|
+
process.stdout.fd,
|
|
272
|
+
`${JSON.stringify(
|
|
273
|
+
{
|
|
274
|
+
tool: JSON_TOOL_NAME,
|
|
275
|
+
schema_version: BACKLOG_JSON_SCHEMA_VERSION,
|
|
276
|
+
generated_at: generatedAt,
|
|
277
|
+
run_id: runId,
|
|
278
|
+
invocation: {
|
|
279
|
+
mode: 'json',
|
|
280
|
+
repo: parsed.repo ?? null,
|
|
281
|
+
resolve_missing_via_gh: parsed.resolveMissingViaGh,
|
|
282
|
+
id_issue_map: parsed.idIssueMapPath ? 'provided' : 'none',
|
|
283
|
+
id_issue_map_from: parsed.idIssueMapSourcePath ? 'provided' : 'none',
|
|
284
|
+
},
|
|
285
|
+
compat: {
|
|
286
|
+
contract_id: BACKLOG_JSON_COMPAT_CONTRACT_ID,
|
|
287
|
+
min_reader_version: BACKLOG_JSON_COMPAT_MIN_READER_VERSION,
|
|
288
|
+
is_backward_compatible: true,
|
|
289
|
+
breaking_changes: [],
|
|
290
|
+
},
|
|
291
|
+
classification_counts: {
|
|
292
|
+
needs_issue: result.classification.needsIssue.length,
|
|
293
|
+
drift_closed_issue: result.classification.driftClosedIssue.length,
|
|
294
|
+
active_issue: result.classification.activeIssue.length,
|
|
295
|
+
heading_drift: result.headingDrift.length,
|
|
296
|
+
},
|
|
297
|
+
action_required_reasons: actionRequiredReasons,
|
|
298
|
+
...(nextCommand ? { next_command: nextCommand } : {}),
|
|
299
|
+
...(nextCommandReason ? { next_command_reason: nextCommandReason } : {}),
|
|
300
|
+
...(nextCommandsWithExecutionGroup ? { next_commands: nextCommandsWithExecutionGroup } : {}),
|
|
301
|
+
heading_drift_count: result.headingDrift.length,
|
|
302
|
+
...result,
|
|
303
|
+
},
|
|
304
|
+
null,
|
|
305
|
+
2
|
|
306
|
+
)}\n`
|
|
307
|
+
);
|
|
308
|
+
} else {
|
|
309
|
+
writeFileSync(process.stdout.fd, formatHumanOutput(result));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (parsed.failOnFindings && result.hasActionRequired) {
|
|
313
|
+
process.exitCode = 1;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
main().catch((error: unknown) => {
|
|
318
|
+
if (error instanceof HelpRequestedError) {
|
|
319
|
+
writeFileSync(process.stdout.fd, `${HELP_TEXT}\n`);
|
|
320
|
+
process.exitCode = 0;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
324
|
+
writeFileSync(process.stderr.fd, `${message}\n`);
|
|
325
|
+
process.exitCode = 1;
|
|
326
|
+
});
|