@ankhorage/doctor 0.2.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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/analysis.d.ts +17 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +181 -0
- package/dist/analysis.js.map +1 -0
- package/dist/ankh.provider.d.ts +10 -0
- package/dist/ankh.provider.d.ts.map +1 -0
- package/dist/ankh.provider.js +27 -0
- package/dist/ankh.provider.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -0
- package/dist/commandContext.d.ts +12 -0
- package/dist/commandContext.d.ts.map +1 -0
- package/dist/commandContext.js +15 -0
- package/dist/commandContext.js.map +1 -0
- package/dist/commands.d.ts +71 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +207 -0
- package/dist/commands.js.map +1 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +9 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/packageMetadata.d.ts +10 -0
- package/dist/packageMetadata.d.ts.map +1 -0
- package/dist/packageMetadata.js +16 -0
- package/dist/packageMetadata.js.map +1 -0
- package/dist/readme-usage.d.ts +2 -0
- package/dist/readme-usage.d.ts.map +1 -0
- package/dist/readme-usage.js +32 -0
- package/dist/readme-usage.js.map +1 -0
- package/package.json +85 -0
- package/src/analysis.ts +354 -0
- package/src/ankh.provider.ts +53 -0
- package/src/cli.ts +67 -0
- package/src/commandContext.ts +27 -0
- package/src/commands.ts +312 -0
- package/src/diagnostics.ts +33 -0
- package/src/index.ts +26 -0
- package/src/packageMetadata.ts +23 -0
- package/src/readme-usage.ts +32 -0
package/src/commands.ts
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import type { AnkhCapabilityId, AnkhCommandDescriptor } from '@ankhorage/contracts/cli';
|
|
2
|
+
|
|
3
|
+
import type { DoctorAnalysisResult, DoctorTargetMode } from './analysis.js';
|
|
4
|
+
import { analyzeDoctorTarget } from './analysis.js';
|
|
5
|
+
import type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
|
|
6
|
+
import {
|
|
7
|
+
countErrorDiagnostics,
|
|
8
|
+
countWarningDiagnostics,
|
|
9
|
+
type DoctorDiagnostic,
|
|
10
|
+
} from './diagnostics.js';
|
|
11
|
+
import { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY } from './packageMetadata.js';
|
|
12
|
+
|
|
13
|
+
type DoctorCommandName = 'fix' | 'package' | 'repo' | 'validate';
|
|
14
|
+
|
|
15
|
+
interface DoctorCommandRunRequest {
|
|
16
|
+
readonly context: DoctorCommandContext;
|
|
17
|
+
readonly inputPath?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type DoctorCommandImplementation = (
|
|
21
|
+
request: DoctorCommandRunRequest,
|
|
22
|
+
services: DoctorCommandServices,
|
|
23
|
+
) => Promise<DoctorCommandRunResult>;
|
|
24
|
+
|
|
25
|
+
export interface DoctorCommandDefinition {
|
|
26
|
+
readonly capability: AnkhCapabilityId;
|
|
27
|
+
readonly mode: DoctorTargetMode;
|
|
28
|
+
readonly path: readonly [DoctorCommandName];
|
|
29
|
+
readonly standaloneName: DoctorCommandName;
|
|
30
|
+
readonly summary: string;
|
|
31
|
+
readonly run: DoctorCommandImplementation;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface DoctorCommandInvocation {
|
|
35
|
+
readonly argv: readonly string[];
|
|
36
|
+
readonly command: DoctorCommandDefinition;
|
|
37
|
+
readonly context: DoctorCommandContext;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DoctorCommandServices {
|
|
41
|
+
readonly analyzeTarget: typeof analyzeDoctorTarget;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RunDoctorCommandOptions {
|
|
45
|
+
readonly runCommandImpl?: RunDoctorCommandImpl;
|
|
46
|
+
readonly services?: Partial<DoctorCommandServices>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type RunDoctorCommandImpl = (
|
|
50
|
+
request: DoctorCommandInvocation,
|
|
51
|
+
options?: RunDoctorCommandOptions,
|
|
52
|
+
) => Promise<DoctorCommandRunResult>;
|
|
53
|
+
|
|
54
|
+
const COMMAND_CAPABILITIES = {
|
|
55
|
+
validate: DOCTOR_CAPABILITIES[0],
|
|
56
|
+
fix: DOCTOR_CAPABILITIES[1],
|
|
57
|
+
repo: DOCTOR_CAPABILITIES[2],
|
|
58
|
+
package: DOCTOR_CAPABILITIES[3],
|
|
59
|
+
} as const satisfies Record<DoctorCommandName, AnkhCapabilityId>;
|
|
60
|
+
|
|
61
|
+
export const DOCTOR_COMMANDS = [
|
|
62
|
+
{
|
|
63
|
+
standaloneName: 'validate',
|
|
64
|
+
path: ['validate'],
|
|
65
|
+
capability: COMMAND_CAPABILITIES.validate,
|
|
66
|
+
mode: 'validate',
|
|
67
|
+
summary: 'Auto-detect lightweight repo and package diagnostics for a local path.',
|
|
68
|
+
run: runValidateCommand,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
standaloneName: 'fix',
|
|
72
|
+
path: ['fix'],
|
|
73
|
+
capability: COMMAND_CAPABILITIES.fix,
|
|
74
|
+
mode: 'fix',
|
|
75
|
+
summary: 'Run the same lightweight checks and report that no automatic fixes exist yet.',
|
|
76
|
+
run: runFixCommand,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
standaloneName: 'repo',
|
|
80
|
+
path: ['repo'],
|
|
81
|
+
capability: COMMAND_CAPABILITIES.repo,
|
|
82
|
+
mode: 'repo',
|
|
83
|
+
summary: 'Force repo-light diagnostics for a repo/workspace-root candidate path.',
|
|
84
|
+
run: runRepoCommand,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
standaloneName: 'package',
|
|
88
|
+
path: ['package'],
|
|
89
|
+
capability: COMMAND_CAPABILITIES.package,
|
|
90
|
+
mode: 'package',
|
|
91
|
+
summary: 'Force package-light diagnostics for a directory containing package.json.',
|
|
92
|
+
run: runPackageCommand,
|
|
93
|
+
},
|
|
94
|
+
] as const satisfies readonly DoctorCommandDefinition[];
|
|
95
|
+
|
|
96
|
+
export async function runDoctorCommand(
|
|
97
|
+
request: DoctorCommandInvocation,
|
|
98
|
+
options: RunDoctorCommandOptions = {},
|
|
99
|
+
): Promise<DoctorCommandRunResult> {
|
|
100
|
+
const services = createDoctorCommandServices(options.services);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const parsedArgs = parseCommandArguments(request.command, request.argv);
|
|
104
|
+
if (parsedArgs.kind === 'help') {
|
|
105
|
+
request.context.writeStdout(renderCommandHelp(request.command));
|
|
106
|
+
return { exitCode: 0 };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return await request.command.run(
|
|
110
|
+
{
|
|
111
|
+
context: request.context,
|
|
112
|
+
inputPath: parsedArgs.inputPath,
|
|
113
|
+
},
|
|
114
|
+
services,
|
|
115
|
+
);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
request.context.writeStderr(renderCommandFailure(request.command.standaloneName, error));
|
|
118
|
+
return { exitCode: 1 };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function createProviderCommandDescriptors(): readonly AnkhCommandDescriptor[] {
|
|
123
|
+
return DOCTOR_COMMANDS.map((command) => ({
|
|
124
|
+
capability: command.capability,
|
|
125
|
+
path: command.path,
|
|
126
|
+
summary: command.summary,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function findDoctorCommandByStandaloneName(value: string): DoctorCommandDefinition | null {
|
|
131
|
+
return DOCTOR_COMMANDS.find((command) => command.standaloneName === value) ?? null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function renderRootHelp(version: string): string {
|
|
135
|
+
const commandLines = DOCTOR_COMMANDS.map(
|
|
136
|
+
(command) => ` ${command.standaloneName.padEnd(8, ' ')} ${command.summary}`,
|
|
137
|
+
).join('\n');
|
|
138
|
+
|
|
139
|
+
return [
|
|
140
|
+
`@ankhorage/doctor v${version}`,
|
|
141
|
+
'',
|
|
142
|
+
'Usage:',
|
|
143
|
+
' ankhorage-doctor <command> [path]',
|
|
144
|
+
` ankh ${DOCTOR_COMMAND_CATEGORY} <command> [path]`,
|
|
145
|
+
'',
|
|
146
|
+
'Commands:',
|
|
147
|
+
commandLines,
|
|
148
|
+
'',
|
|
149
|
+
'Path resolution:',
|
|
150
|
+
' Pass [path], or omit it to validate the current working directory.',
|
|
151
|
+
'',
|
|
152
|
+
].join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function renderUnknownCommand(value: string): string {
|
|
156
|
+
return [`Unknown doctor command: ${value}`, '', 'Run ankhorage-doctor --help', ''].join('\n');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function renderCommandHelp(command: DoctorCommandDefinition): string {
|
|
160
|
+
return [
|
|
161
|
+
command.summary,
|
|
162
|
+
'',
|
|
163
|
+
'Usage:',
|
|
164
|
+
` ankhorage-doctor ${command.standaloneName} [path]`,
|
|
165
|
+
` ankh ${DOCTOR_COMMAND_CATEGORY} ${command.path.join(' ')} [path]`,
|
|
166
|
+
'',
|
|
167
|
+
'Path resolution:',
|
|
168
|
+
' Pass [path], or omit it to validate the current working directory.',
|
|
169
|
+
'',
|
|
170
|
+
].join('\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function createDoctorCommandServices(
|
|
174
|
+
overrides: Partial<DoctorCommandServices> = {},
|
|
175
|
+
): DoctorCommandServices {
|
|
176
|
+
return {
|
|
177
|
+
analyzeTarget: overrides.analyzeTarget ?? analyzeDoctorTarget,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseCommandArguments(
|
|
182
|
+
command: DoctorCommandDefinition,
|
|
183
|
+
argv: readonly string[],
|
|
184
|
+
): { readonly kind: 'help' } | { readonly kind: 'run'; readonly inputPath?: string } {
|
|
185
|
+
const [firstArg] = argv;
|
|
186
|
+
|
|
187
|
+
if (argv.length === 1 && firstArg !== undefined && isHelpToken(firstArg)) {
|
|
188
|
+
return { kind: 'help' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (argv.length > 1) {
|
|
192
|
+
throw new Error(`Doctor ${command.standaloneName} accepts at most one path argument.`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
kind: 'run',
|
|
197
|
+
inputPath: firstArg,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function runValidateCommand(
|
|
202
|
+
request: DoctorCommandRunRequest,
|
|
203
|
+
services: DoctorCommandServices,
|
|
204
|
+
): Promise<DoctorCommandRunResult> {
|
|
205
|
+
const result = await services.analyzeTarget({
|
|
206
|
+
cwd: request.context.cwd,
|
|
207
|
+
inputPath: request.inputPath,
|
|
208
|
+
mode: 'validate',
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
request.context.writeStdout(renderAnalysisReport('validate', result));
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
exitCode: countErrorDiagnostics(result.diagnostics) > 0 ? 1 : 0,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function runFixCommand(
|
|
219
|
+
request: DoctorCommandRunRequest,
|
|
220
|
+
services: DoctorCommandServices,
|
|
221
|
+
): Promise<DoctorCommandRunResult> {
|
|
222
|
+
const result = await services.analyzeTarget({
|
|
223
|
+
cwd: request.context.cwd,
|
|
224
|
+
inputPath: request.inputPath,
|
|
225
|
+
mode: 'fix',
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
request.context.writeStdout(renderAnalysisReport('fix', result));
|
|
229
|
+
request.context.writeStdout('No automatic fixes available.\n');
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
exitCode: countErrorDiagnostics(result.diagnostics) > 0 ? 1 : 0,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function runRepoCommand(
|
|
237
|
+
request: DoctorCommandRunRequest,
|
|
238
|
+
services: DoctorCommandServices,
|
|
239
|
+
): Promise<DoctorCommandRunResult> {
|
|
240
|
+
const result = await services.analyzeTarget({
|
|
241
|
+
cwd: request.context.cwd,
|
|
242
|
+
inputPath: request.inputPath,
|
|
243
|
+
mode: 'repo',
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
request.context.writeStdout(renderAnalysisReport('repo', result));
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
exitCode: countErrorDiagnostics(result.diagnostics) > 0 ? 1 : 0,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function runPackageCommand(
|
|
254
|
+
request: DoctorCommandRunRequest,
|
|
255
|
+
services: DoctorCommandServices,
|
|
256
|
+
): Promise<DoctorCommandRunResult> {
|
|
257
|
+
const result = await services.analyzeTarget({
|
|
258
|
+
cwd: request.context.cwd,
|
|
259
|
+
inputPath: request.inputPath,
|
|
260
|
+
mode: 'package',
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
request.context.writeStdout(renderAnalysisReport('package', result));
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
exitCode: countErrorDiagnostics(result.diagnostics) > 0 ? 1 : 0,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function renderAnalysisReport(
|
|
271
|
+
commandName: DoctorCommandName,
|
|
272
|
+
result: DoctorAnalysisResult,
|
|
273
|
+
): string {
|
|
274
|
+
const lines = [
|
|
275
|
+
`doctor ${commandName}`,
|
|
276
|
+
`target: ${result.targetPath}`,
|
|
277
|
+
`checks: ${result.appliedChecks.length > 0 ? result.appliedChecks.join(', ') : 'none'}`,
|
|
278
|
+
`repoMarkers: ${result.repoMarkers.length > 0 ? result.repoMarkers.join(', ') : 'none'}`,
|
|
279
|
+
'',
|
|
280
|
+
'Diagnostics:',
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
if (result.diagnostics.length === 0) {
|
|
284
|
+
lines.push(' none');
|
|
285
|
+
} else {
|
|
286
|
+
for (const diagnostic of result.diagnostics) {
|
|
287
|
+
lines.push(renderDiagnosticLine(diagnostic));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const errorCount = countErrorDiagnostics(result.diagnostics);
|
|
292
|
+
const warningCount = countWarningDiagnostics(result.diagnostics);
|
|
293
|
+
|
|
294
|
+
lines.push('');
|
|
295
|
+
lines.push(`Summary: ${errorCount} error(s), ${warningCount} warning(s)`);
|
|
296
|
+
lines.push('');
|
|
297
|
+
|
|
298
|
+
return lines.join('\n');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function renderDiagnosticLine(diagnostic: DoctorDiagnostic): string {
|
|
302
|
+
return ` - ${diagnostic.severity.toUpperCase()} ${diagnostic.code}: ${diagnostic.message}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function renderCommandFailure(commandName: DoctorCommandName, error: unknown): string {
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
return [`Failed to run doctor ${commandName}: ${message}`, ''].join('\n');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function isHelpToken(value: string): boolean {
|
|
311
|
+
return value === '--help' || value === '-h' || value === 'help';
|
|
312
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type DoctorDiagnosticCode =
|
|
2
|
+
| 'invalid-ankh-metadata'
|
|
3
|
+
| 'invalid-package-json'
|
|
4
|
+
| 'missing-changelog'
|
|
5
|
+
| 'missing-changeset-directory'
|
|
6
|
+
| 'missing-license'
|
|
7
|
+
| 'missing-readme'
|
|
8
|
+
| 'missing-workflows-directory'
|
|
9
|
+
| 'package-json-missing'
|
|
10
|
+
| 'package-name-missing'
|
|
11
|
+
| 'package-type-missing'
|
|
12
|
+
| 'package-version-missing'
|
|
13
|
+
| 'repo-markers-missing'
|
|
14
|
+
| 'target-not-directory'
|
|
15
|
+
| 'target-not-found'
|
|
16
|
+
| 'unsupported-target';
|
|
17
|
+
|
|
18
|
+
export type DoctorDiagnosticSeverity = 'error' | 'warning';
|
|
19
|
+
|
|
20
|
+
export interface DoctorDiagnostic {
|
|
21
|
+
readonly code: DoctorDiagnosticCode;
|
|
22
|
+
readonly message: string;
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly severity: DoctorDiagnosticSeverity;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function countErrorDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {
|
|
28
|
+
return diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function countWarningDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {
|
|
32
|
+
return diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
DoctorAnalysisRequest,
|
|
3
|
+
DoctorAnalysisResult,
|
|
4
|
+
DoctorTargetCheck,
|
|
5
|
+
DoctorTargetMode,
|
|
6
|
+
} from './analysis.js';
|
|
7
|
+
export { analyzeDoctorTarget } from './analysis.js';
|
|
8
|
+
export {
|
|
9
|
+
createDoctorRuntimeProvider,
|
|
10
|
+
type CreateDoctorRuntimeProviderOptions,
|
|
11
|
+
} from './ankh.provider.js';
|
|
12
|
+
export { runCli } from './cli.js';
|
|
13
|
+
export type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
|
|
14
|
+
export { createDefaultCommandContext } from './commandContext.js';
|
|
15
|
+
export type {
|
|
16
|
+
DoctorDiagnostic,
|
|
17
|
+
DoctorDiagnosticCode,
|
|
18
|
+
DoctorDiagnosticSeverity,
|
|
19
|
+
} from './diagnostics.js';
|
|
20
|
+
export {
|
|
21
|
+
DOCTOR_CAPABILITIES,
|
|
22
|
+
DOCTOR_COMMAND_CATEGORY,
|
|
23
|
+
DOCTOR_PACKAGE_METADATA,
|
|
24
|
+
DOCTOR_PACKAGE_NAME,
|
|
25
|
+
DOCTOR_PACKAGE_VERSION,
|
|
26
|
+
} from './packageMetadata.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnkhCapabilityId,
|
|
3
|
+
AnkhCommandCategory,
|
|
4
|
+
AnkhPackageMetadata,
|
|
5
|
+
} from '@ankhorage/contracts/cli';
|
|
6
|
+
|
|
7
|
+
import packageJson from '../package.json';
|
|
8
|
+
|
|
9
|
+
export const DOCTOR_PACKAGE_NAME = packageJson.name;
|
|
10
|
+
export const DOCTOR_PACKAGE_VERSION = packageJson.version;
|
|
11
|
+
export const DOCTOR_COMMAND_CATEGORY = 'doctor' as const satisfies AnkhCommandCategory;
|
|
12
|
+
export const DOCTOR_CAPABILITIES = [
|
|
13
|
+
'doctor.validate',
|
|
14
|
+
'doctor.fix',
|
|
15
|
+
'doctor.repo',
|
|
16
|
+
'doctor.package',
|
|
17
|
+
] as const satisfies readonly AnkhCapabilityId[];
|
|
18
|
+
|
|
19
|
+
export const DOCTOR_PACKAGE_METADATA = {
|
|
20
|
+
category: DOCTOR_COMMAND_CATEGORY,
|
|
21
|
+
provider: './dist/ankh.provider.js',
|
|
22
|
+
capabilities: DOCTOR_CAPABILITIES,
|
|
23
|
+
} as const satisfies AnkhPackageMetadata;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { runCli } from './cli.js';
|
|
2
|
+
|
|
3
|
+
/***
|
|
4
|
+
* Provider and CLI surface
|
|
5
|
+
*
|
|
6
|
+
* `@ankhorage/doctor` is the repo/package compliance provider for Ankhorage.
|
|
7
|
+
*
|
|
8
|
+
* The same shared command implementation backs both:
|
|
9
|
+
*
|
|
10
|
+
* - `ankh doctor ...`
|
|
11
|
+
* - `bunx @ankhorage/doctor ...`
|
|
12
|
+
*
|
|
13
|
+
* Current command surface:
|
|
14
|
+
*
|
|
15
|
+
* - `validate`
|
|
16
|
+
* - `fix`
|
|
17
|
+
* - `repo`
|
|
18
|
+
* - `package`
|
|
19
|
+
*
|
|
20
|
+
* `doctor#1` intentionally stays lightweight:
|
|
21
|
+
*
|
|
22
|
+
* - path-aware local repo/package diagnostics are implemented now
|
|
23
|
+
* - GitHub checks, CI checks, full policy enforcement, and autofix rules are deferred
|
|
24
|
+
*
|
|
25
|
+
* Path handling:
|
|
26
|
+
*
|
|
27
|
+
* - pass `[path]`, or
|
|
28
|
+
* - omit it to inspect the current working directory
|
|
29
|
+
*
|
|
30
|
+
* @usage
|
|
31
|
+
*/
|
|
32
|
+
await runCli(['--help']);
|