@principles/pd-cli 1.73.2 → 1.74.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/config-doctor.d.ts +20 -0
- package/dist/commands/config-doctor.d.ts.map +1 -0
- package/dist/commands/config-doctor.js +144 -0
- package/dist/commands/config-doctor.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -28
- package/dist/index.js.map +1 -1
- package/dist/services/config-doctor.d.ts +142 -0
- package/dist/services/config-doctor.d.ts.map +1 -0
- package/dist/services/config-doctor.js +603 -0
- package/dist/services/config-doctor.js.map +1 -0
- package/package.json +4 -2
- package/src/commands/config-doctor.ts +158 -0
- package/src/index.ts +47 -29
- package/src/services/config-doctor.ts +763 -0
- package/tests/commands/cli-command-tree.test.ts +1 -1
- package/tests/commands/config-doctor.test.ts +624 -0
- package/tests/pd-cli-smoke.test.ts +12 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pd config doctor — Discover and explain PD / OpenClaw configuration state.
|
|
3
|
+
*
|
|
4
|
+
* PRI-299 MVP UX:
|
|
5
|
+
* - Reports workspace + OpenClaw config paths and existence
|
|
6
|
+
* - Lists effective feature flags and enabled MVP channels
|
|
7
|
+
* - Classifies provider/model/auth connectivity (healthy, auth_missing, rate_limit, etc.)
|
|
8
|
+
* - Emits `reason` + `nextActions` for failures
|
|
9
|
+
* - NEVER leaks tokens, env var values, or raw config bytes
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* pd config doctor [--workspace <path>] [--json]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
17
|
+
import { buildDoctorOutput, type DoctorOutput } from '../services/config-doctor.js';
|
|
18
|
+
|
|
19
|
+
interface DoctorOptions {
|
|
20
|
+
workspace?: string;
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function formatTextOutput(output: DoctorOutput): string {
|
|
25
|
+
const lines: string[] = [];
|
|
26
|
+
const statusIcon = output.status === 'ok' ? '✓' : output.status === 'degraded' ? '⚠' : '✗';
|
|
27
|
+
|
|
28
|
+
lines.push('PD Config Doctor');
|
|
29
|
+
lines.push(`status: ${statusIcon} ${output.status.toUpperCase()}`);
|
|
30
|
+
lines.push(`workspace: ${output.workspaceDir}`);
|
|
31
|
+
lines.push('');
|
|
32
|
+
|
|
33
|
+
lines.push('PD config paths:');
|
|
34
|
+
for (const [k, v] of Object.entries(output.pdConfigPaths)) {
|
|
35
|
+
const exists = v.exists ? '[exists]' : '[missing]';
|
|
36
|
+
lines.push(` ${k.padEnd(16)} ${exists.padEnd(10)} ${v.path}`);
|
|
37
|
+
}
|
|
38
|
+
lines.push('');
|
|
39
|
+
|
|
40
|
+
lines.push('OpenClaw paths:');
|
|
41
|
+
for (const [k, v] of Object.entries(output.openclawConfigPaths)) {
|
|
42
|
+
const exists = v.exists ? '[exists]' : '[missing]';
|
|
43
|
+
lines.push(` ${k.padEnd(16)} ${exists.padEnd(10)} ${v.path}`);
|
|
44
|
+
}
|
|
45
|
+
lines.push('');
|
|
46
|
+
|
|
47
|
+
lines.push(`Feature flags: source=${output.featureFlags.source}`);
|
|
48
|
+
lines.push(` enabled MVP channels: ${output.featureFlags.enabledMvpChannels.length === 0 ? '(none)' : output.featureFlags.enabledMvpChannels.join(', ')}`);
|
|
49
|
+
if (output.featureFlags.disabledFlags.length > 0) {
|
|
50
|
+
lines.push(` disabled: ${output.featureFlags.disabledFlags.join(', ')}`);
|
|
51
|
+
}
|
|
52
|
+
if (output.featureFlags.warnings.length > 0) {
|
|
53
|
+
lines.push(` warnings: ${output.featureFlags.warnings.length}`);
|
|
54
|
+
}
|
|
55
|
+
lines.push('');
|
|
56
|
+
|
|
57
|
+
lines.push('Provider health:');
|
|
58
|
+
if (output.providerHealth.length === 0) {
|
|
59
|
+
lines.push(' (no providers discovered)');
|
|
60
|
+
} else {
|
|
61
|
+
for (const p of output.providerHealth) {
|
|
62
|
+
const cls = p.classification.toUpperCase();
|
|
63
|
+
const provider = p.provider ?? '(unset)';
|
|
64
|
+
const model = p.model ?? '(unset)';
|
|
65
|
+
const apiKeyEnv = p.apiKeyEnv ?? '(unset)';
|
|
66
|
+
const apiKeyState = p.apiKeyPresent ? 'present' : 'absent';
|
|
67
|
+
lines.push(` [${cls}] ${provider} / ${model}`);
|
|
68
|
+
lines.push(` apiKeyEnv: ${apiKeyEnv} (${apiKeyState})`);
|
|
69
|
+
lines.push(` source: ${p.source}`);
|
|
70
|
+
lines.push(` reason: ${p.reason}`);
|
|
71
|
+
lines.push(` nextAction: ${p.nextAction}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
lines.push('');
|
|
75
|
+
|
|
76
|
+
lines.push('Internal agents:');
|
|
77
|
+
if (output.internalAgents && output.internalAgents.correctionObserver) {
|
|
78
|
+
const co = output.internalAgents.correctionObserver;
|
|
79
|
+
const coStatus = co.status.toUpperCase();
|
|
80
|
+
const coProvider = co.provider ?? '(unset)';
|
|
81
|
+
const coModel = co.model ?? '(unset)';
|
|
82
|
+
const coApiKeyEnv = co.apiKeyEnv ?? '(unset)';
|
|
83
|
+
const coApiKeyState = co.apiKeyPresent ? 'present' : 'absent';
|
|
84
|
+
lines.push(` correctionObserver: [${coStatus}]`);
|
|
85
|
+
lines.push(` enabled: ${co.enabled}`);
|
|
86
|
+
lines.push(` flagSource: ${co.flagSource}`);
|
|
87
|
+
lines.push(` configSource:${co.configSource}`);
|
|
88
|
+
if (co.provider || co.model) {
|
|
89
|
+
lines.push(` provider: ${coProvider} / ${coModel}`);
|
|
90
|
+
}
|
|
91
|
+
lines.push(` apiKeyEnv: ${coApiKeyEnv} (${coApiKeyState})`);
|
|
92
|
+
lines.push(` reason: ${co.reason}`);
|
|
93
|
+
lines.push(` nextAction: ${co.nextAction}`);
|
|
94
|
+
} else {
|
|
95
|
+
lines.push(' (no internal agents diagnosed)');
|
|
96
|
+
}
|
|
97
|
+
lines.push('');
|
|
98
|
+
|
|
99
|
+
if (output.warnings.length > 0) {
|
|
100
|
+
lines.push('Warnings:');
|
|
101
|
+
for (const w of output.warnings) {
|
|
102
|
+
lines.push(` [!] ${w}`);
|
|
103
|
+
}
|
|
104
|
+
lines.push('');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (output.reason) {
|
|
108
|
+
lines.push(`Reason: ${output.reason}`);
|
|
109
|
+
lines.push('');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (output.nextActions.length > 0) {
|
|
113
|
+
lines.push('Next actions:');
|
|
114
|
+
for (const a of output.nextActions) {
|
|
115
|
+
lines.push(` → ${a}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function handleConfigDoctor(opts: DoctorOptions): Promise<void> {
|
|
123
|
+
let workspaceDir: string;
|
|
124
|
+
try {
|
|
125
|
+
workspaceDir = opts.workspace ? path.resolve(opts.workspace) : resolveWorkspaceDir();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
128
|
+
const output = {
|
|
129
|
+
status: 'failed' as const,
|
|
130
|
+
reason: 'workspace_resolution_failed',
|
|
131
|
+
message,
|
|
132
|
+
nextActions: ['Pass --workspace <path> explicitly, or set PD_WORKSPACE_DIR environment variable'],
|
|
133
|
+
};
|
|
134
|
+
if (opts.json) {
|
|
135
|
+
console.log(JSON.stringify(output, null, 2));
|
|
136
|
+
} else {
|
|
137
|
+
console.error(`error: ${message}`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const output = await buildDoctorOutput({ workspaceDir });
|
|
144
|
+
|
|
145
|
+
if (opts.json) {
|
|
146
|
+
// JSON mode: single parseable object on stdout.
|
|
147
|
+
console.log(JSON.stringify(output, null, 2));
|
|
148
|
+
} else {
|
|
149
|
+
console.log(formatTextOutput(output));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (output.status === 'failed') {
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
} else if (output.status === 'degraded') {
|
|
155
|
+
// degraded: do not exit 1, but emit a hint to stderr for the operator.
|
|
156
|
+
console.error(`\nNote: doctor status is degraded. Inspect warnings + nextActions.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* pd CLI
|
|
3
|
+
* pd CLI 鈥?Principles Disciple command-line interface.
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
6
|
* pd pain record --reason <text> [--score N] [--source manual]
|
|
@@ -55,7 +55,7 @@ const program = new Command();
|
|
|
55
55
|
|
|
56
56
|
program
|
|
57
57
|
.name('pd')
|
|
58
|
-
.description('PD CLI
|
|
58
|
+
.description('PD CLI 鈥?Pain recording, sample management, and evolution tasks')
|
|
59
59
|
.version(pkg.version);
|
|
60
60
|
|
|
61
61
|
const painCmd = program
|
|
@@ -147,7 +147,7 @@ centralCmd
|
|
|
147
147
|
await handleCentralSync();
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
//
|
|
150
|
+
// 鈹€鈹€ Runtime v2 task/run commands 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
151
151
|
|
|
152
152
|
const rtTaskCmd = program
|
|
153
153
|
.command('task')
|
|
@@ -190,7 +190,7 @@ rtRunCmd
|
|
|
190
190
|
await handleRunShow({ id: runId });
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
//
|
|
193
|
+
// 鈹€鈹€ Runtime v2 trajectory/history/context commands 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
194
194
|
|
|
195
195
|
const trajectoryCmd = program
|
|
196
196
|
.command('trajectory')
|
|
@@ -239,7 +239,7 @@ contextCmd
|
|
|
239
239
|
await handleContextBuild(taskId, opts);
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
//
|
|
242
|
+
// 鈹€鈹€ Legacy import command 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
243
243
|
|
|
244
244
|
const legacyCmd = program
|
|
245
245
|
.command('legacy')
|
|
@@ -258,7 +258,7 @@ importCmd
|
|
|
258
258
|
await handleLegacyImportOpenClaw(opts);
|
|
259
259
|
});
|
|
260
260
|
|
|
261
|
-
//
|
|
261
|
+
// 鈹€鈹€ Diagnostician run/status commands 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
262
262
|
|
|
263
263
|
const diagnoseCmd = program
|
|
264
264
|
.command('diagnose')
|
|
@@ -283,19 +283,19 @@ diagnoseCmd
|
|
|
283
283
|
.option('--openclaw-local', 'Use local OpenClaw (mutually exclusive with --openclaw-gateway)')
|
|
284
284
|
.option('--openclaw-gateway', 'Use gateway OpenClaw (mutually exclusive with --openclaw-local)')
|
|
285
285
|
.option('-a, --agent <agentId>', 'Agent ID to invoke')
|
|
286
|
-
.option('--provider <name>', 'LLM provider (e.g., openrouter)
|
|
287
|
-
.option('--model <id>', 'Model ID (e.g., anthropic/claude-sonnet-4)
|
|
288
|
-
.option('--apiKeyEnv <name>', 'Env var name for API key
|
|
289
|
-
.option('--baseUrl <url>', 'Custom base URL
|
|
290
|
-
.option('--maxRetries <n>', 'Max retry attempts for LLM failures
|
|
291
|
-
.option('--timeoutMs <ms>', 'Timeout in milliseconds
|
|
286
|
+
.option('--provider <name>', 'LLM provider (e.g., openrouter) 鈥?for pi-ai, falls back to policy')
|
|
287
|
+
.option('--model <id>', 'Model ID (e.g., anthropic/claude-sonnet-4) 鈥?for pi-ai, falls back to policy')
|
|
288
|
+
.option('--apiKeyEnv <name>', 'Env var name for API key 鈥?for pi-ai, falls back to policy')
|
|
289
|
+
.option('--baseUrl <url>', 'Custom base URL 鈥?for pi-ai, falls back to policy')
|
|
290
|
+
.option('--maxRetries <n>', 'Max retry attempts for LLM failures 鈥?for pi-ai, falls back to policy', parseInt)
|
|
291
|
+
.option('--timeoutMs <ms>', 'Timeout in milliseconds 鈥?for pi-ai, falls back to policy', parseInt)
|
|
292
292
|
.option('--no-intake', 'Skip candidate intake after successful diagnosis')
|
|
293
293
|
.option('--json', 'Output raw JSON')
|
|
294
294
|
.action(async (opts) => {
|
|
295
295
|
await handleDiagnoseRun(opts);
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
-
//
|
|
298
|
+
// 鈹€鈹€ Runtime probe command (HG-01 HARD GATE) 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
299
299
|
|
|
300
300
|
const runtimeCmd = program
|
|
301
301
|
.command('runtime')
|
|
@@ -316,7 +316,7 @@ const synthCmd = runtimeCmd
|
|
|
316
316
|
|
|
317
317
|
synthCmd
|
|
318
318
|
.command('baseline')
|
|
319
|
-
.description('Run synthetic PD workload baseline (PRI-206)
|
|
319
|
+
.description('Run synthetic PD workload baseline (PRI-206) 鈥?deterministic, no LLM required')
|
|
320
320
|
.option('-w, --workspace <path>', 'Workspace directory (default: temp workspace)')
|
|
321
321
|
.option('--json', 'Output raw JSON')
|
|
322
322
|
.action(async (opts) => {
|
|
@@ -325,7 +325,7 @@ synthCmd
|
|
|
325
325
|
|
|
326
326
|
synthCmd
|
|
327
327
|
.command('flood')
|
|
328
|
-
.description('Run pain flood simulation (PRI-208)
|
|
328
|
+
.description('Run pain flood simulation (PRI-208) 鈥?deterministic dedup/stress test, no LLM required')
|
|
329
329
|
.option('-w, --workspace <path>', 'Workspace directory (default: temp workspace)')
|
|
330
330
|
.option('--json', 'Output raw JSON')
|
|
331
331
|
.option('--identical-count <n>', 'Number of identical pain signals (default: 10)', parseInt)
|
|
@@ -343,7 +343,7 @@ synthCmd
|
|
|
343
343
|
|
|
344
344
|
synthCmd
|
|
345
345
|
.command('proven-channel')
|
|
346
|
-
.description('Run MVP activation continuity baseline (PRI-240)
|
|
346
|
+
.description('Run MVP activation continuity baseline (PRI-240) 鈥?deterministic, no LLM required')
|
|
347
347
|
.option('-w, --workspace <path>', 'Workspace directory (default: temp workspace)')
|
|
348
348
|
.option('--json', 'Output raw JSON')
|
|
349
349
|
.option('--channels <channels>', 'Comma-separated channel list (prompt,code_tool_hook,defer_archive)')
|
|
@@ -373,7 +373,7 @@ const demoCmd = program
|
|
|
373
373
|
|
|
374
374
|
demoCmd
|
|
375
375
|
.command('story-a')
|
|
376
|
-
.description('Run Story A\' proven-channel demo (PRI-246)
|
|
376
|
+
.description('Run Story A\' proven-channel demo (PRI-246) 鈥?full evidence鈫抪roposal鈫抋pproval鈫抋ctivation鈫抩bservation chain')
|
|
377
377
|
.option('-w, --workspace <path>', 'Workspace directory (default: temp workspace)')
|
|
378
378
|
.option('--json', 'Output raw JSON')
|
|
379
379
|
.option('--channels <channels>', 'Comma-separated channel list (prompt,code_tool_hook,defer_archive)')
|
|
@@ -392,13 +392,13 @@ runtimeCmd
|
|
|
392
392
|
.option('--openclaw-local', 'Use local OpenClaw (mutually exclusive with --openclaw-gateway)')
|
|
393
393
|
.option('--openclaw-gateway', 'Use gateway OpenClaw (mutually exclusive with --openclaw-local)')
|
|
394
394
|
.option('-a, --agent <agentId>', 'Agent ID to probe')
|
|
395
|
-
.option('--provider <name>', 'LLM provider (e.g., openrouter)
|
|
396
|
-
.option('--model <id>', 'Model ID (e.g., anthropic/claude-sonnet-4)
|
|
397
|
-
.option('--apiKeyEnv <name>', 'Env var name for API key (e.g., OPENROUTER_API_KEY)
|
|
398
|
-
.option('--baseUrl <url>', 'Custom base URL for OpenAI-compatible providers
|
|
395
|
+
.option('--provider <name>', 'LLM provider (e.g., openrouter) 鈥?for pi-ai, falls back to --workspace workflows.yaml')
|
|
396
|
+
.option('--model <id>', 'Model ID (e.g., anthropic/claude-sonnet-4) 鈥?for pi-ai, falls back to --workspace workflows.yaml')
|
|
397
|
+
.option('--apiKeyEnv <name>', 'Env var name for API key (e.g., OPENROUTER_API_KEY) 鈥?for pi-ai, falls back to --workspace workflows.yaml')
|
|
398
|
+
.option('--baseUrl <url>', 'Custom base URL for OpenAI-compatible providers 鈥?for pi-ai, falls back to --workspace workflows.yaml')
|
|
399
399
|
.option('--maxRetries <n>', 'Max retry attempts for LLM failures', parseInt)
|
|
400
400
|
.option('--timeoutMs <ms>', 'Timeout in milliseconds for probe', parseInt)
|
|
401
|
-
.option('-w, --workspace <path>', 'Workspace directory
|
|
401
|
+
.option('-w, --workspace <path>', 'Workspace directory 鈥?loads pi-ai policy from .state/workflows.yaml')
|
|
402
402
|
.option('--json', 'Output raw JSON')
|
|
403
403
|
.action(async (opts) => {
|
|
404
404
|
await handleRuntimeProbe(opts);
|
|
@@ -462,17 +462,17 @@ runtimeHealthCmd
|
|
|
462
462
|
|
|
463
463
|
runtimeHealthCmd
|
|
464
464
|
.command('gfi')
|
|
465
|
-
.description('GFI workspace snapshot
|
|
465
|
+
.description('GFI workspace snapshot 鈥?active vs stale session breakdown')
|
|
466
466
|
.option('-w, --workspace <path>', 'Workspace directory')
|
|
467
467
|
.option('--json', 'Output raw JSON')
|
|
468
468
|
.action(async (opts) => {
|
|
469
469
|
await handleRuntimeGfiSnapshot({ workspace: opts.workspace, json: opts.json });
|
|
470
470
|
});
|
|
471
471
|
|
|
472
|
-
// PRI-82: pd runtime gfi snapshot
|
|
472
|
+
// PRI-82: pd runtime gfi snapshot 鈥?canonical operator command (alias of runtime health gfi)
|
|
473
473
|
runtimeCmd
|
|
474
474
|
.command('gfi')
|
|
475
|
-
.description('GFI workspace snapshot
|
|
475
|
+
.description('GFI workspace snapshot 鈥?active vs stale session breakdown')
|
|
476
476
|
.command('snapshot')
|
|
477
477
|
.description('GFI workspace snapshot for the operator (alias: pd runtime health gfi)')
|
|
478
478
|
.option('-w, --workspace <path>', 'Workspace directory')
|
|
@@ -608,7 +608,7 @@ const pruningCmd = runtimeCmd
|
|
|
608
608
|
|
|
609
609
|
pruningCmd
|
|
610
610
|
.command('report')
|
|
611
|
-
.description('Show pruning health report
|
|
611
|
+
.description('Show pruning health report 鈥?watch/review principle signals')
|
|
612
612
|
.option('-w, --workspace <path>', 'Workspace directory')
|
|
613
613
|
.option('--json', 'Output raw JSON')
|
|
614
614
|
.action((opts) => {
|
|
@@ -679,7 +679,7 @@ pruningCmd
|
|
|
679
679
|
});
|
|
680
680
|
});
|
|
681
681
|
|
|
682
|
-
//
|
|
682
|
+
// 鈹€鈹€ Candidate inspection commands 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
683
683
|
|
|
684
684
|
const candidateCmd = program
|
|
685
685
|
.command('candidate')
|
|
@@ -783,7 +783,7 @@ candidateInternalizationCmd
|
|
|
783
783
|
await handleCandidateInternalizationBackfill({ workspace: opts.workspace, dryRun: opts.dryRun, confirm: opts.confirm, includePending: opts.includePending, json: opts.json });
|
|
784
784
|
});
|
|
785
785
|
|
|
786
|
-
//
|
|
786
|
+
// 鈹€鈹€ Artifact inspection commands 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
787
787
|
|
|
788
788
|
const artifactCmd = program
|
|
789
789
|
.command('artifact')
|
|
@@ -830,4 +830,22 @@ program
|
|
|
830
830
|
});
|
|
831
831
|
});
|
|
832
832
|
|
|
833
|
+
// PRI-299: PD Config Doctor 鈥?surface config paths, feature flags, and provider connectivity
|
|
834
|
+
const configCmd = program
|
|
835
|
+
.command('config')
|
|
836
|
+
.description('PD config diagnostics');
|
|
837
|
+
|
|
838
|
+
configCmd
|
|
839
|
+
.command('doctor')
|
|
840
|
+
.description('Inspect PD / OpenClaw config paths, feature flags, and provider connectivity (PRI-299)')
|
|
841
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
842
|
+
.option('--json', 'Output a single parseable JSON object on stdout', false)
|
|
843
|
+
.action(async (opts) => {
|
|
844
|
+
const { handleConfigDoctor } = await import('./commands/config-doctor.js');
|
|
845
|
+
await handleConfigDoctor({
|
|
846
|
+
workspace: opts.workspace,
|
|
847
|
+
json: opts.json,
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
|
|
833
851
|
program.parse();
|