@principles/pd-cli 1.115.0 → 1.117.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/dist/commands/candidate.d.ts +23 -0
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +89 -3
- package/dist/commands/candidate.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +153 -132
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/runtime-features.d.ts.map +1 -1
- package/dist/commands/runtime-features.js +2 -7
- package/dist/commands/runtime-features.js.map +1 -1
- package/dist/commands/runtime-internalization-integrity-repair.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-integrity-repair.js +15 -31
- package/dist/commands/runtime-internalization-integrity-repair.js.map +1 -1
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +246 -326
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-recovery.d.ts.map +1 -1
- package/dist/commands/runtime-recovery.js +9 -8
- package/dist/commands/runtime-recovery.js.map +1 -1
- package/dist/services/__tests__/cli-output.test.d.ts +18 -0
- package/dist/services/__tests__/cli-output.test.d.ts.map +1 -0
- package/dist/services/__tests__/cli-output.test.js +103 -0
- package/dist/services/__tests__/cli-output.test.js.map +1 -0
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +18 -0
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +1 -0
- package/dist/services/__tests__/runtime-adapter-resolver.test.js +651 -0
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -0
- package/dist/services/cli-output.d.ts +61 -0
- package/dist/services/cli-output.d.ts.map +1 -0
- package/dist/services/cli-output.js +72 -0
- package/dist/services/cli-output.js.map +1 -0
- package/dist/services/runtime-adapter-resolver.d.ts +105 -0
- package/dist/services/runtime-adapter-resolver.d.ts.map +1 -0
- package/dist/services/runtime-adapter-resolver.js +188 -0
- package/dist/services/runtime-adapter-resolver.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/candidate.ts +92 -3
- package/src/commands/diagnose.ts +146 -138
- package/src/commands/runtime-features.ts +2 -6
- package/src/commands/runtime-internalization-integrity-repair.ts +16 -28
- package/src/commands/runtime-internalization-run-once.ts +242 -353
- package/src/commands/runtime-recovery.ts +9 -7
- package/src/services/__tests__/cli-output.test.ts +130 -0
- package/src/services/__tests__/runtime-adapter-resolver.test.ts +772 -0
- package/src/services/cli-output.ts +95 -0
- package/src/services/runtime-adapter-resolver.ts +339 -0
- package/tests/commands/candidate-internalization-backfill.test.ts +43 -3
- package/tests/commands/candidate-internalize-lineage.test.ts +521 -0
- package/tests/commands/candidate-internalize.test.ts +31 -5
- package/tests/commands/diagnose.test.ts +7 -3
- package/tests/commands/runtime-internalization-run-once.test.ts +11 -0
- package/tests/commands/runtime-recovery.test.ts +27 -4
- package/tests/services/rulehost-pipeline-e2e.test.ts +40 -7
|
@@ -3,6 +3,7 @@ import { createRecoverySweepService } from '@principles/core/runtime-v2';
|
|
|
3
3
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
4
4
|
import { createRemediationResult, remediationAction } from './remediation-output.js';
|
|
5
5
|
import type { RemediationResult } from './remediation-output.js';
|
|
6
|
+
import { emitResult, emitFlagConflict } from '../services/cli-output.js';
|
|
6
7
|
|
|
7
8
|
interface RecoverySweepOptions {
|
|
8
9
|
workspace?: string;
|
|
@@ -40,8 +41,10 @@ function formatTextOutput(output: RemediationResult): string {
|
|
|
40
41
|
|
|
41
42
|
export async function handleRuntimeRecoverySweep(opts: RecoverySweepOptions): Promise<void> {
|
|
42
43
|
if (opts.dryRun && opts.confirm) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// PRI-432: Bug fix — flag conflict now emits JSON when --json is set
|
|
45
|
+
// (previously always used console.error, violating CLI Operator Gate rule #1)
|
|
46
|
+
const exitCode = emitFlagConflict({ json: opts.json ?? false });
|
|
47
|
+
process.exit(exitCode);
|
|
45
48
|
return;
|
|
46
49
|
}
|
|
47
50
|
const workspaceDir = opts.workspace ? path.resolve(opts.workspace) : resolveWorkspaceDir();
|
|
@@ -92,11 +95,10 @@ export async function handleRuntimeRecoverySweep(opts: RecoverySweepOptions): Pr
|
|
|
92
95
|
safeToConfirm: isDryRun && expiredLeaseTaskIds.length > 0,
|
|
93
96
|
});
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
98
|
+
emitResult(output, {
|
|
99
|
+
json: opts.json ?? false,
|
|
100
|
+
formatText: formatTextOutput,
|
|
101
|
+
});
|
|
100
102
|
|
|
101
103
|
if (expiredLeaseTaskIds.length > 0 && isDryRun) {
|
|
102
104
|
process.exitCode = 1;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRI-432: Tests for shared CLI output module.
|
|
3
|
+
*
|
|
4
|
+
* Extracts common output patterns from:
|
|
5
|
+
* - runtime-features.ts (JSON/text emit + exit code)
|
|
6
|
+
* - runtime-recovery.ts (dry-run/confirm conflict + JSON/text emit)
|
|
7
|
+
* - runtime-internalization-integrity-repair.ts (conflict + error catch + JSON/text emit)
|
|
8
|
+
*
|
|
9
|
+
* TDD flow: these tests are RED until cli-output.ts is implemented.
|
|
10
|
+
*
|
|
11
|
+
* ERR refs:
|
|
12
|
+
* - ERR-001 (no any): all types explicit
|
|
13
|
+
* - ERR-005 (no as bypass): no type casts
|
|
14
|
+
* - ERR-009 (fail-loud): emitError returns exit code, does not swallow
|
|
15
|
+
* - ERR-002 (graceful degradation with reason): all error paths include reason + nextAction
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
19
|
+
|
|
20
|
+
const { emitResult, emitFlagConflict, emitError } = await import('../cli-output.js');
|
|
21
|
+
|
|
22
|
+
describe('cli-output module (PRI-432)', () => {
|
|
23
|
+
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
|
|
24
|
+
let consoleErrorSpy: ReturnType<typeof vi.spyOn>;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
29
|
+
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ── emitResult ──────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
describe('emitResult', () => {
|
|
35
|
+
it('emits JSON to stdout when json=true', () => {
|
|
36
|
+
const output = { status: 'ok', count: 5 };
|
|
37
|
+
const formatText = (o: typeof output) => `Status: ${o.status}`;
|
|
38
|
+
|
|
39
|
+
emitResult(output, { json: true, formatText });
|
|
40
|
+
|
|
41
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
|
42
|
+
const emitted = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
43
|
+
expect(emitted).toEqual(output);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('emits text to stdout when json=false', () => {
|
|
47
|
+
const output = { status: 'ok', count: 5 };
|
|
48
|
+
const formatText = (o: typeof output) => `Status: ${o.status}`;
|
|
49
|
+
|
|
50
|
+
emitResult(output, { json: false, formatText });
|
|
51
|
+
|
|
52
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('Status: ok');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('uses formatText callback for text output', () => {
|
|
56
|
+
const output = { status: 'failed', reason: 'bad config' };
|
|
57
|
+
const formatText = (o: typeof output) => `ERROR: ${o.reason}`;
|
|
58
|
+
|
|
59
|
+
emitResult(output, { json: false, formatText });
|
|
60
|
+
|
|
61
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('ERROR: bad config');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ── emitFlagConflict ────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
describe('emitFlagConflict', () => {
|
|
68
|
+
it('emits JSON error to stdout when json=true', () => {
|
|
69
|
+
const exitCode = emitFlagConflict({ json: true });
|
|
70
|
+
|
|
71
|
+
expect(exitCode).toBe(1);
|
|
72
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
|
73
|
+
const emitted = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
74
|
+
expect(emitted.ok).toBe(false);
|
|
75
|
+
expect(emitted.reason).toContain('mutually exclusive');
|
|
76
|
+
expect(emitted.nextAction).toContain('--dry-run');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('emits text error to stderr when json=false', () => {
|
|
80
|
+
const exitCode = emitFlagConflict({ json: false });
|
|
81
|
+
|
|
82
|
+
expect(exitCode).toBe(1);
|
|
83
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
84
|
+
const msg = consoleErrorSpy.mock.calls[0][0] as string;
|
|
85
|
+
expect(msg).toContain('mutually exclusive');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('always returns exit code 1', () => {
|
|
89
|
+
expect(emitFlagConflict({ json: true })).toBe(1);
|
|
90
|
+
expect(emitFlagConflict({ json: false })).toBe(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ── emitError ───────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
describe('emitError', () => {
|
|
97
|
+
it('emits JSON error to stdout when json=true', () => {
|
|
98
|
+
const err = new Error('DB connection failed');
|
|
99
|
+
const exitCode = emitError(err, { json: true, nextAction: 'Check DB connectivity' });
|
|
100
|
+
|
|
101
|
+
expect(exitCode).toBe(1);
|
|
102
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
|
103
|
+
const emitted = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
104
|
+
expect(emitted.ok).toBe(false);
|
|
105
|
+
expect(emitted.reason).toBe('DB connection failed');
|
|
106
|
+
expect(emitted.nextAction).toBe('Check DB connectivity');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('emits text error to stderr when json=false', () => {
|
|
110
|
+
const err = new Error('DB connection failed');
|
|
111
|
+
const exitCode = emitError(err, { json: false, nextAction: 'Check DB connectivity' });
|
|
112
|
+
|
|
113
|
+
expect(exitCode).toBe(1);
|
|
114
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error: DB connection failed');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('handles non-Error throwables via String()', () => {
|
|
118
|
+
const exitCode = emitError('string error', { json: true, nextAction: 'retry' });
|
|
119
|
+
|
|
120
|
+
expect(exitCode).toBe(1);
|
|
121
|
+
const emitted = JSON.parse(consoleLogSpy.mock.calls[0][0] as string);
|
|
122
|
+
expect(emitted.reason).toBe('string error');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('always returns exit code 1', () => {
|
|
126
|
+
expect(emitError(new Error('x'), { json: true, nextAction: 'y' })).toBe(1);
|
|
127
|
+
expect(emitError(new Error('x'), { json: false, nextAction: 'y' })).toBe(1);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|