@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.
Files changed (49) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +61 -0
  4. package/dist/analysis.d.ts +17 -0
  5. package/dist/analysis.d.ts.map +1 -0
  6. package/dist/analysis.js +181 -0
  7. package/dist/analysis.js.map +1 -0
  8. package/dist/ankh.provider.d.ts +10 -0
  9. package/dist/ankh.provider.d.ts.map +1 -0
  10. package/dist/ankh.provider.js +27 -0
  11. package/dist/ankh.provider.js.map +1 -0
  12. package/dist/cli.d.ts +10 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +39 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commandContext.d.ts +12 -0
  17. package/dist/commandContext.d.ts.map +1 -0
  18. package/dist/commandContext.js +15 -0
  19. package/dist/commandContext.js.map +1 -0
  20. package/dist/commands.d.ts +71 -0
  21. package/dist/commands.d.ts.map +1 -0
  22. package/dist/commands.js +207 -0
  23. package/dist/commands.js.map +1 -0
  24. package/dist/diagnostics.d.ts +11 -0
  25. package/dist/diagnostics.d.ts.map +1 -0
  26. package/dist/diagnostics.js +9 -0
  27. package/dist/diagnostics.js.map +1 -0
  28. package/dist/index.d.ts +9 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +6 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/packageMetadata.d.ts +10 -0
  33. package/dist/packageMetadata.d.ts.map +1 -0
  34. package/dist/packageMetadata.js +16 -0
  35. package/dist/packageMetadata.js.map +1 -0
  36. package/dist/readme-usage.d.ts +2 -0
  37. package/dist/readme-usage.d.ts.map +1 -0
  38. package/dist/readme-usage.js +32 -0
  39. package/dist/readme-usage.js.map +1 -0
  40. package/package.json +85 -0
  41. package/src/analysis.ts +354 -0
  42. package/src/ankh.provider.ts +53 -0
  43. package/src/cli.ts +67 -0
  44. package/src/commandContext.ts +27 -0
  45. package/src/commands.ts +312 -0
  46. package/src/diagnostics.ts +33 -0
  47. package/src/index.ts +26 -0
  48. package/src/packageMetadata.ts +23 -0
  49. package/src/readme-usage.ts +32 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ab58890: Bootstrap `@ankhorage/doctor` as an executable Ankh provider and standalone CLI with lightweight repo/package diagnostics.
8
+
9
+ All notable changes to this project will be documented in this file.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ankhorage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ <!-- markdownlint-disable MD013 MD033 -->
2
+ <!-- This file is generated by Paradox. Do not edit manually. -->
3
+
4
+ # DOCTOR
5
+
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.1.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
+
8
+ Executable doctor provider and standalone CLI for lightweight Ankhorage repo and package compliance diagnostics.
9
+
10
+ ## Usage
11
+
12
+ ### Provider and CLI surface
13
+
14
+ `@ankhorage/doctor` is the repo/package compliance provider for Ankhorage.
15
+
16
+ The same shared command implementation backs both:
17
+
18
+ - `ankh doctor ...`
19
+ - `bunx @ankhorage/doctor ...`
20
+
21
+ Current command surface:
22
+
23
+ - `validate`
24
+ - `fix`
25
+ - `repo`
26
+ - `package`
27
+
28
+ `doctor#1` intentionally stays lightweight:
29
+
30
+ - path-aware local repo/package diagnostics are implemented now
31
+ - GitHub checks, CI checks, full policy enforcement, and autofix rules are deferred
32
+
33
+ Path handling:
34
+
35
+ - pass `[path]`, or
36
+ - omit it to inspect the current working directory
37
+
38
+ Source: `src/readme-usage.ts`
39
+
40
+ ```ts
41
+ import { runCli } from './cli.js';
42
+
43
+ await runCli(['--help']);
44
+ ```
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ bunx @ankhorage/doctor
50
+ ```
51
+
52
+ ## Generated documentation
53
+
54
+ - [Interactive documentation app](././paradox/index.html)
55
+ - [Public API reference](././paradox/exports.md)
56
+ - [Component registry](././paradox/components.md)
57
+ - [Architecture overview](././paradox/diagrams/architecture-overview.mmd)
58
+ - [Module relationships](././paradox/diagrams/module-relationships.mmd)
59
+ - [Export graph](././paradox/diagrams/export-graph.mmd)
60
+ - [ankhorage-doctor sequence](././paradox/diagrams/sequences/ankhorage-doctor.mmd)
61
+ - [createDoctorRuntimeProvider sequence](././paradox/diagrams/sequences/create-doctor-runtime-provider.mmd)
@@ -0,0 +1,17 @@
1
+ import type { DoctorDiagnostic } from "./diagnostics.js";
2
+ export type DoctorTargetCheck = "package" | "repo";
3
+ export type DoctorTargetMode = "fix" | "package" | "repo" | "validate";
4
+ export interface DoctorAnalysisRequest {
5
+ readonly cwd: string;
6
+ readonly inputPath?: string;
7
+ readonly mode: DoctorTargetMode;
8
+ }
9
+ export interface DoctorAnalysisResult {
10
+ readonly appliedChecks: readonly DoctorTargetCheck[];
11
+ readonly diagnostics: readonly DoctorDiagnostic[];
12
+ readonly hasPackageJson: boolean;
13
+ readonly repoMarkers: readonly string[];
14
+ readonly targetPath: string;
15
+ }
16
+ export declare function analyzeDoctorTarget(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
17
+ //# sourceMappingURL=analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,CAAC;AACnD,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAcD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CA6G/B"}
@@ -0,0 +1,181 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ const REPO_MARKERS = [
4
+ { label: ".git", relativePath: ".git" },
5
+ { label: ".github/workflows", relativePath: path.join(".github", "workflows") },
6
+ { label: ".changeset", relativePath: ".changeset" },
7
+ { label: "package.json", relativePath: "package.json" },
8
+ { label: "pnpm-workspace.yaml", relativePath: "pnpm-workspace.yaml" },
9
+ { label: "bun.lock", relativePath: "bun.lock" },
10
+ { label: "bun.lockb", relativePath: "bun.lockb" },
11
+ { label: "package-lock.json", relativePath: "package-lock.json" },
12
+ { label: "yarn.lock", relativePath: "yarn.lock" },
13
+ ];
14
+ export async function analyzeDoctorTarget(request) {
15
+ const targetPath = path.resolve(request.cwd, request.inputPath ?? ".");
16
+ const stats = await statOrNull(targetPath);
17
+ if (stats === null) {
18
+ return {
19
+ appliedChecks: [],
20
+ diagnostics: [
21
+ createDiagnostic("error", "target-not-found", targetPath, `Target path does not exist: ${targetPath}`),
22
+ ],
23
+ hasPackageJson: false,
24
+ repoMarkers: [],
25
+ targetPath,
26
+ };
27
+ }
28
+ if (!stats.isDirectory()) {
29
+ return {
30
+ appliedChecks: [],
31
+ diagnostics: [
32
+ createDiagnostic("error", "target-not-directory", targetPath, `Target path must be a directory: ${targetPath}`),
33
+ ],
34
+ hasPackageJson: false,
35
+ repoMarkers: [],
36
+ targetPath,
37
+ };
38
+ }
39
+ const repoMarkers = (await Promise.all(REPO_MARKERS.map(async (marker) => (await pathExists(path.join(targetPath, marker.relativePath)))
40
+ ? marker.label
41
+ : null))).filter((marker) => marker !== null);
42
+ const packageJsonPath = path.join(targetPath, "package.json");
43
+ const hasPackageJson = repoMarkers.includes("package.json");
44
+ const appliedChecks = [];
45
+ const diagnostics = [];
46
+ const shouldRunRepoChecks = request.mode === "repo" ||
47
+ ((request.mode === "validate" || request.mode === "fix") && repoMarkers.length > 0);
48
+ const shouldRunPackageChecks = request.mode === "package" ||
49
+ ((request.mode === "validate" || request.mode === "fix") && hasPackageJson);
50
+ if (request.mode === "repo" && repoMarkers.length === 0) {
51
+ diagnostics.push(createDiagnostic("error", "repo-markers-missing", targetPath, "Target is not a repo/workspace-root candidate because no repo markers were found."));
52
+ }
53
+ if (request.mode === "package" && !hasPackageJson) {
54
+ diagnostics.push(createDiagnostic("error", "package-json-missing", packageJsonPath, "Target does not contain a readable package.json file."));
55
+ }
56
+ if ((request.mode === "validate" || request.mode === "fix") &&
57
+ repoMarkers.length === 0 &&
58
+ !hasPackageJson) {
59
+ diagnostics.push(createDiagnostic("error", "unsupported-target", targetPath, "Target is neither a repo/workspace-root candidate nor a package root."));
60
+ }
61
+ if (shouldRunRepoChecks && repoMarkers.length > 0) {
62
+ appliedChecks.push("repo");
63
+ diagnostics.push(...(await analyzeRepoLight(targetPath)));
64
+ }
65
+ if (shouldRunPackageChecks && hasPackageJson) {
66
+ appliedChecks.push("package");
67
+ diagnostics.push(...(await analyzePackageLight(targetPath, packageJsonPath)));
68
+ }
69
+ return {
70
+ appliedChecks,
71
+ diagnostics,
72
+ hasPackageJson,
73
+ repoMarkers,
74
+ targetPath,
75
+ };
76
+ }
77
+ async function analyzeRepoLight(targetPath) {
78
+ const diagnostics = [];
79
+ await maybeWarnForMissingPath(diagnostics, targetPath, "README.md", "missing-readme");
80
+ await maybeWarnForMissingPath(diagnostics, targetPath, "CHANGELOG.md", "missing-changelog");
81
+ await maybeWarnForMissingPath(diagnostics, targetPath, "LICENSE", "missing-license");
82
+ await maybeWarnForMissingPath(diagnostics, targetPath, ".changeset", "missing-changeset-directory");
83
+ await maybeWarnForMissingPath(diagnostics, targetPath, path.join(".github", "workflows"), "missing-workflows-directory");
84
+ return diagnostics;
85
+ }
86
+ async function analyzePackageLight(targetPath, packageJsonPath) {
87
+ const diagnostics = [];
88
+ let parsed;
89
+ try {
90
+ parsed = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
91
+ }
92
+ catch (error) {
93
+ diagnostics.push(createDiagnostic("error", "invalid-package-json", packageJsonPath, createInvalidPackageJsonMessage(error)));
94
+ return diagnostics;
95
+ }
96
+ if (!isRecord(parsed)) {
97
+ diagnostics.push(createDiagnostic("error", "invalid-package-json", packageJsonPath, "package.json must contain a JSON object."));
98
+ return diagnostics;
99
+ }
100
+ if (!isNonEmptyString(parsed.name)) {
101
+ diagnostics.push(createDiagnostic("error", "package-name-missing", packageJsonPath, 'package.json must define a non-empty "name" field.'));
102
+ }
103
+ if (!isNonEmptyString(parsed.version)) {
104
+ diagnostics.push(createDiagnostic("error", "package-version-missing", packageJsonPath, 'package.json must define a non-empty "version" field.'));
105
+ }
106
+ if (!isNonEmptyString(parsed.type)) {
107
+ diagnostics.push(createDiagnostic("error", "package-type-missing", packageJsonPath, 'package.json must define a non-empty "type" field.'));
108
+ }
109
+ if ("ankh" in parsed) {
110
+ const validationError = validateAnkhMetadataShape(parsed.ankh);
111
+ if (validationError !== null) {
112
+ diagnostics.push(createDiagnostic("error", "invalid-ankh-metadata", targetPath, validationError));
113
+ }
114
+ }
115
+ return diagnostics;
116
+ }
117
+ async function maybeWarnForMissingPath(diagnostics, rootPath, relativePath, code) {
118
+ const absolutePath = path.join(rootPath, relativePath);
119
+ if (await pathExists(absolutePath)) {
120
+ return;
121
+ }
122
+ diagnostics.push(createDiagnostic("warning", code, absolutePath, `Missing expected repo artifact: ${relativePath}`));
123
+ }
124
+ function validateAnkhMetadataShape(value) {
125
+ if (!isRecord(value)) {
126
+ return 'package.json "ankh" metadata must be an object when present.';
127
+ }
128
+ if (!isNonEmptyString(value.category)) {
129
+ return 'package.json "ankh.category" must be a non-empty string.';
130
+ }
131
+ if (!(value.provider === null || isPackageRelativeProviderPath(value.provider))) {
132
+ return 'package.json "ankh.provider" must be null or a package-relative "./..." string.';
133
+ }
134
+ if (!Array.isArray(value.capabilities) ||
135
+ value.capabilities.some((capability) => !isNonEmptyString(capability))) {
136
+ return 'package.json "ankh.capabilities" must be an array of non-empty strings.';
137
+ }
138
+ return null;
139
+ }
140
+ function createDiagnostic(severity, code, diagnosticPath, message) {
141
+ return {
142
+ code,
143
+ message,
144
+ path: diagnosticPath,
145
+ severity,
146
+ };
147
+ }
148
+ function createInvalidPackageJsonMessage(error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ return `package.json contains invalid JSON: ${message}`;
151
+ }
152
+ async function pathExists(targetPath) {
153
+ return (await statOrNull(targetPath)) !== null;
154
+ }
155
+ async function statOrNull(targetPath) {
156
+ try {
157
+ return await fs.stat(targetPath);
158
+ }
159
+ catch (error) {
160
+ if (isMissingPathError(error)) {
161
+ return null;
162
+ }
163
+ throw error;
164
+ }
165
+ }
166
+ function isMissingPathError(error) {
167
+ return (error instanceof Error &&
168
+ "code" in error &&
169
+ typeof error.code === "string" &&
170
+ error.code === "ENOENT");
171
+ }
172
+ function isNonEmptyString(value) {
173
+ return typeof value === "string" && value.trim() !== "";
174
+ }
175
+ function isPackageRelativeProviderPath(value) {
176
+ return isNonEmptyString(value) && value.startsWith("./");
177
+ }
178
+ function isRecord(value) {
179
+ return typeof value === "object" && value !== null && !Array.isArray(value);
180
+ }
181
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { DoctorDiagnostic } from \"./diagnostics.js\";\n\nexport type DoctorTargetCheck = \"package\" | \"repo\";\nexport type DoctorTargetMode = \"fix\" | \"package\" | \"repo\" | \"validate\";\n\nexport interface DoctorAnalysisRequest {\n readonly cwd: string;\n readonly inputPath?: string;\n readonly mode: DoctorTargetMode;\n}\n\nexport interface DoctorAnalysisResult {\n readonly appliedChecks: readonly DoctorTargetCheck[];\n readonly diagnostics: readonly DoctorDiagnostic[];\n readonly hasPackageJson: boolean;\n readonly repoMarkers: readonly string[];\n readonly targetPath: string;\n}\n\nconst REPO_MARKERS = [\n { label: \".git\", relativePath: \".git\" },\n { label: \".github/workflows\", relativePath: path.join(\".github\", \"workflows\") },\n { label: \".changeset\", relativePath: \".changeset\" },\n { label: \"package.json\", relativePath: \"package.json\" },\n { label: \"pnpm-workspace.yaml\", relativePath: \"pnpm-workspace.yaml\" },\n { label: \"bun.lock\", relativePath: \"bun.lock\" },\n { label: \"bun.lockb\", relativePath: \"bun.lockb\" },\n { label: \"package-lock.json\", relativePath: \"package-lock.json\" },\n { label: \"yarn.lock\", relativePath: \"yarn.lock\" },\n] as const;\n\nexport async function analyzeDoctorTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n const targetPath = path.resolve(request.cwd, request.inputPath ?? \".\");\n const stats = await statOrNull(targetPath);\n\n if (stats === null) {\n return {\n appliedChecks: [],\n diagnostics: [\n createDiagnostic(\"error\", \"target-not-found\", targetPath, `Target path does not exist: ${targetPath}`),\n ],\n hasPackageJson: false,\n repoMarkers: [],\n targetPath,\n };\n }\n\n if (!stats.isDirectory()) {\n return {\n appliedChecks: [],\n diagnostics: [\n createDiagnostic(\n \"error\",\n \"target-not-directory\",\n targetPath,\n `Target path must be a directory: ${targetPath}`,\n ),\n ],\n hasPackageJson: false,\n repoMarkers: [],\n targetPath,\n };\n }\n\n const repoMarkers = (\n await Promise.all(\n REPO_MARKERS.map(async (marker) =>\n (await pathExists(path.join(targetPath, marker.relativePath)))\n ? marker.label\n : null,\n ),\n )\n ).filter((marker): marker is (typeof REPO_MARKERS)[number][\"label\"] => marker !== null);\n\n const packageJsonPath = path.join(targetPath, \"package.json\");\n const hasPackageJson = repoMarkers.includes(\"package.json\");\n const appliedChecks: DoctorTargetCheck[] = [];\n const diagnostics: DoctorDiagnostic[] = [];\n\n const shouldRunRepoChecks =\n request.mode === \"repo\" ||\n ((request.mode === \"validate\" || request.mode === \"fix\") && repoMarkers.length > 0);\n const shouldRunPackageChecks =\n request.mode === \"package\" ||\n ((request.mode === \"validate\" || request.mode === \"fix\") && hasPackageJson);\n\n if (request.mode === \"repo\" && repoMarkers.length === 0) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"repo-markers-missing\",\n targetPath,\n \"Target is not a repo/workspace-root candidate because no repo markers were found.\",\n ),\n );\n }\n\n if (request.mode === \"package\" && !hasPackageJson) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"package-json-missing\",\n packageJsonPath,\n \"Target does not contain a readable package.json file.\",\n ),\n );\n }\n\n if (\n (request.mode === \"validate\" || request.mode === \"fix\") &&\n repoMarkers.length === 0 &&\n !hasPackageJson\n ) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"unsupported-target\",\n targetPath,\n \"Target is neither a repo/workspace-root candidate nor a package root.\",\n ),\n );\n }\n\n if (shouldRunRepoChecks && repoMarkers.length > 0) {\n appliedChecks.push(\"repo\");\n diagnostics.push(...(await analyzeRepoLight(targetPath)));\n }\n\n if (shouldRunPackageChecks && hasPackageJson) {\n appliedChecks.push(\"package\");\n diagnostics.push(...(await analyzePackageLight(targetPath, packageJsonPath)));\n }\n\n return {\n appliedChecks,\n diagnostics,\n hasPackageJson,\n repoMarkers,\n targetPath,\n };\n}\n\nasync function analyzeRepoLight(targetPath: string): Promise<DoctorDiagnostic[]> {\n const diagnostics: DoctorDiagnostic[] = [];\n\n await maybeWarnForMissingPath(diagnostics, targetPath, \"README.md\", \"missing-readme\");\n await maybeWarnForMissingPath(\n diagnostics,\n targetPath,\n \"CHANGELOG.md\",\n \"missing-changelog\",\n );\n await maybeWarnForMissingPath(diagnostics, targetPath, \"LICENSE\", \"missing-license\");\n await maybeWarnForMissingPath(\n diagnostics,\n targetPath,\n \".changeset\",\n \"missing-changeset-directory\",\n );\n await maybeWarnForMissingPath(\n diagnostics,\n targetPath,\n path.join(\".github\", \"workflows\"),\n \"missing-workflows-directory\",\n );\n\n return diagnostics;\n}\n\nasync function analyzePackageLight(\n targetPath: string,\n packageJsonPath: string,\n): Promise<DoctorDiagnostic[]> {\n const diagnostics: DoctorDiagnostic[] = [];\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(await fs.readFile(packageJsonPath, \"utf8\")) as unknown;\n } catch (error) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"invalid-package-json\",\n packageJsonPath,\n createInvalidPackageJsonMessage(error),\n ),\n );\n return diagnostics;\n }\n\n if (!isRecord(parsed)) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"invalid-package-json\",\n packageJsonPath,\n \"package.json must contain a JSON object.\",\n ),\n );\n return diagnostics;\n }\n\n if (!isNonEmptyString(parsed.name)) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"package-name-missing\",\n packageJsonPath,\n 'package.json must define a non-empty \"name\" field.',\n ),\n );\n }\n\n if (!isNonEmptyString(parsed.version)) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"package-version-missing\",\n packageJsonPath,\n 'package.json must define a non-empty \"version\" field.',\n ),\n );\n }\n\n if (!isNonEmptyString(parsed.type)) {\n diagnostics.push(\n createDiagnostic(\n \"error\",\n \"package-type-missing\",\n packageJsonPath,\n 'package.json must define a non-empty \"type\" field.',\n ),\n );\n }\n\n if (\"ankh\" in parsed) {\n const validationError = validateAnkhMetadataShape(parsed.ankh);\n if (validationError !== null) {\n diagnostics.push(\n createDiagnostic(\"error\", \"invalid-ankh-metadata\", targetPath, validationError),\n );\n }\n }\n\n return diagnostics;\n}\n\nasync function maybeWarnForMissingPath(\n diagnostics: DoctorDiagnostic[],\n rootPath: string,\n relativePath: string,\n code:\n | \"missing-changelog\"\n | \"missing-changeset-directory\"\n | \"missing-license\"\n | \"missing-readme\"\n | \"missing-workflows-directory\",\n): Promise<void> {\n const absolutePath = path.join(rootPath, relativePath);\n if (await pathExists(absolutePath)) {\n return;\n }\n\n diagnostics.push(\n createDiagnostic(\n \"warning\",\n code,\n absolutePath,\n `Missing expected repo artifact: ${relativePath}`,\n ),\n );\n}\n\nfunction validateAnkhMetadataShape(value: unknown): string | null {\n if (!isRecord(value)) {\n return 'package.json \"ankh\" metadata must be an object when present.';\n }\n\n if (!isNonEmptyString(value.category)) {\n return 'package.json \"ankh.category\" must be a non-empty string.';\n }\n\n if (!(value.provider === null || isPackageRelativeProviderPath(value.provider))) {\n return 'package.json \"ankh.provider\" must be null or a package-relative \"./...\" string.';\n }\n\n if (\n !Array.isArray(value.capabilities) ||\n value.capabilities.some((capability) => !isNonEmptyString(capability))\n ) {\n return 'package.json \"ankh.capabilities\" must be an array of non-empty strings.';\n }\n\n return null;\n}\n\nfunction createDiagnostic(\n severity: DoctorDiagnostic[\"severity\"],\n code: DoctorDiagnostic[\"code\"],\n diagnosticPath: string,\n message: string,\n): DoctorDiagnostic {\n return {\n code,\n message,\n path: diagnosticPath,\n severity,\n };\n}\n\nfunction createInvalidPackageJsonMessage(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return `package.json contains invalid JSON: ${message}`;\n}\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n return (await statOrNull(targetPath)) !== null;\n}\n\nasync function statOrNull(targetPath: string) {\n try {\n return await fs.stat(targetPath);\n } catch (error) {\n if (isMissingPathError(error)) {\n return null;\n }\n\n throw error;\n }\n}\n\nfunction isMissingPathError(error: unknown): error is NodeJS.ErrnoException {\n return (\n error instanceof Error &&\n \"code\" in error &&\n typeof error.code === \"string\" &&\n error.code === \"ENOENT\"\n );\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nfunction isPackageRelativeProviderPath(value: unknown): value is string {\n return isNonEmptyString(value) && value.startsWith(\"./\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { AnkhRuntimeCommandProvider } from "@ankhorage/ankh";
2
+ import { type DoctorCommandServices, type RunDoctorCommandImpl } from "./commands.js";
3
+ export interface CreateDoctorRuntimeProviderOptions {
4
+ readonly runCommandImpl?: RunDoctorCommandImpl;
5
+ readonly services?: Partial<DoctorCommandServices>;
6
+ }
7
+ export declare function createDoctorRuntimeProvider(options?: CreateDoctorRuntimeProviderOptions): AnkhRuntimeCommandProvider;
8
+ declare const provider: AnkhRuntimeCommandProvider;
9
+ export default provider;
10
+ //# sourceMappingURL=ankh.provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ankh.provider.d.ts","sourceRoot":"","sources":["../src/ankh.provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAQvB,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACpD;AAED,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kCAAuC,GAC/C,0BAA0B,CAyB5B;AAED,QAAA,MAAM,QAAQ,4BAAgC,CAAC;AAE/C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { createProviderCommandDescriptors, DOCTOR_COMMANDS, runDoctorCommand, } from "./commands.js";
2
+ import { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from "./packageMetadata.js";
3
+ export function createDoctorRuntimeProvider(options = {}) {
4
+ const runCommandImpl = options.runCommandImpl ?? runDoctorCommand;
5
+ return {
6
+ id: DOCTOR_PACKAGE_NAME,
7
+ category: DOCTOR_COMMAND_CATEGORY,
8
+ version: DOCTOR_PACKAGE_VERSION,
9
+ capabilities: [...DOCTOR_CAPABILITIES],
10
+ commands: createProviderCommandDescriptors(),
11
+ handlers: DOCTOR_COMMANDS.map((command) => ({
12
+ path: command.path,
13
+ handler(request) {
14
+ return runCommandImpl({
15
+ argv: request.argv,
16
+ command,
17
+ context: request.context,
18
+ }, {
19
+ services: options.services,
20
+ });
21
+ },
22
+ })),
23
+ };
24
+ }
25
+ const provider = createDoctorRuntimeProvider();
26
+ export default provider;
27
+ //# sourceMappingURL=ankh.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ankh.provider.js","sourceRoot":"","sources":["../src/ankh.provider.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAChC,eAAe,EACf,gBAAgB,GAGjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAO9B,MAAM,UAAU,2BAA2B,CACzC,UAA8C,EAAE;IAEhD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAElE,OAAO;QACL,EAAE,EAAE,mBAAmB;QACvB,QAAQ,EAAE,uBAAuB;QACjC,OAAO,EAAE,sBAAsB;QAC/B,YAAY,EAAE,CAAC,GAAG,mBAAmB,CAAC;QACtC,QAAQ,EAAE,gCAAgC,EAAE;QAC5C,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC1C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,OAAO,CAAC,OAAO;gBACb,OAAO,cAAc,CACnB;oBACE,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,OAAO;oBACP,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,EACD;oBACE,QAAQ,EAAE,OAAO,CAAC,QAAQ;iBAC3B,CACF,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;KACiC,CAAC;AACzC,CAAC;AAED,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;AAE/C,eAAe,QAAQ,CAAC","sourcesContent":["import type { AnkhRuntimeCommandProvider } from \"@ankhorage/ankh\";\n\nimport {\n createProviderCommandDescriptors,\n DOCTOR_COMMANDS,\n runDoctorCommand,\n type DoctorCommandServices,\n type RunDoctorCommandImpl,\n} from \"./commands.js\";\nimport {\n DOCTOR_CAPABILITIES,\n DOCTOR_COMMAND_CATEGORY,\n DOCTOR_PACKAGE_NAME,\n DOCTOR_PACKAGE_VERSION,\n} from \"./packageMetadata.js\";\n\nexport interface CreateDoctorRuntimeProviderOptions {\n readonly runCommandImpl?: RunDoctorCommandImpl;\n readonly services?: Partial<DoctorCommandServices>;\n}\n\nexport function createDoctorRuntimeProvider(\n options: CreateDoctorRuntimeProviderOptions = {},\n): AnkhRuntimeCommandProvider {\n const runCommandImpl = options.runCommandImpl ?? runDoctorCommand;\n\n return {\n id: DOCTOR_PACKAGE_NAME,\n category: DOCTOR_COMMAND_CATEGORY,\n version: DOCTOR_PACKAGE_VERSION,\n capabilities: [...DOCTOR_CAPABILITIES],\n commands: createProviderCommandDescriptors(),\n handlers: DOCTOR_COMMANDS.map((command) => ({\n path: command.path,\n handler(request) {\n return runCommandImpl(\n {\n argv: request.argv,\n command,\n context: request.context,\n },\n {\n services: options.services,\n },\n );\n },\n })),\n } satisfies AnkhRuntimeCommandProvider;\n}\n\nconst provider = createDoctorRuntimeProvider();\n\nexport default provider;\n"]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bun
2
+ import type { DoctorCommandContext, DoctorCommandRunResult } from "./commandContext.js";
3
+ import { type DoctorCommandServices, type RunDoctorCommandImpl } from "./commands.js";
4
+ export interface DoctorCliOptions {
5
+ readonly context?: DoctorCommandContext;
6
+ readonly runCommandImpl?: RunDoctorCommandImpl;
7
+ readonly services?: Partial<DoctorCommandServices>;
8
+ }
9
+ export declare function runCli(argv: readonly string[], options?: DoctorCliOptions): Promise<DoctorCommandRunResult>;
10
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAExF,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACpD;AAED,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CA+BjC"}
package/dist/cli.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bun
2
+ import { createDefaultCommandContext } from "./commandContext.js";
3
+ import { findDoctorCommandByStandaloneName, renderRootHelp, renderUnknownCommand, runDoctorCommand, } from "./commands.js";
4
+ export async function runCli(argv, options = {}) {
5
+ const context = options.context ?? createDefaultCommandContext();
6
+ const runCommand = options.runCommandImpl ?? runDoctorCommand;
7
+ const [firstToken, ...restTokens] = argv;
8
+ if (firstToken === undefined || isHelpToken(firstToken)) {
9
+ context.writeStdout(renderRootHelp(context.version));
10
+ return { exitCode: 0 };
11
+ }
12
+ if (isVersionToken(firstToken)) {
13
+ context.writeStdout(`${context.version}\n`);
14
+ return { exitCode: 0 };
15
+ }
16
+ const command = findDoctorCommandByStandaloneName(firstToken);
17
+ if (command === null) {
18
+ context.writeStderr(renderUnknownCommand(firstToken));
19
+ return { exitCode: 1 };
20
+ }
21
+ return runCommand({
22
+ argv: restTokens,
23
+ command,
24
+ context,
25
+ }, {
26
+ services: options.services,
27
+ });
28
+ }
29
+ function isHelpToken(value) {
30
+ return value === "--help" || value === "-h" || value === "help";
31
+ }
32
+ function isVersionToken(value) {
33
+ return value === "--version" || value === "-v" || value === "version";
34
+ }
35
+ if (import.meta.main) {
36
+ const result = await runCli(process.argv.slice(2));
37
+ process.exit(result.exitCode);
38
+ }
39
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GAGjB,MAAM,eAAe,CAAC;AAQvB,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,IAAuB,EACvB,UAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,2BAA2B,EAAE,CAAC;IACjE,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAC9D,MAAM,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAEzC,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,iCAAiC,CAAC,UAAU,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC;QACtD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,UAAU,CACf;QACE,IAAI,EAAE,UAAU;QAChB,OAAO;QACP,OAAO;KACR,EACD;QACE,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC;AAClE,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AACxE,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC","sourcesContent":["#!/usr/bin/env bun\n\nimport type { DoctorCommandContext, DoctorCommandRunResult } from \"./commandContext.js\";\nimport { createDefaultCommandContext } from \"./commandContext.js\";\nimport {\n findDoctorCommandByStandaloneName,\n renderRootHelp,\n renderUnknownCommand,\n runDoctorCommand,\n type DoctorCommandServices,\n type RunDoctorCommandImpl,\n} from \"./commands.js\";\n\nexport interface DoctorCliOptions {\n readonly context?: DoctorCommandContext;\n readonly runCommandImpl?: RunDoctorCommandImpl;\n readonly services?: Partial<DoctorCommandServices>;\n}\n\nexport async function runCli(\n argv: readonly string[],\n options: DoctorCliOptions = {},\n): Promise<DoctorCommandRunResult> {\n const context = options.context ?? createDefaultCommandContext();\n const runCommand = options.runCommandImpl ?? runDoctorCommand;\n const [firstToken, ...restTokens] = argv;\n\n if (firstToken === undefined || isHelpToken(firstToken)) {\n context.writeStdout(renderRootHelp(context.version));\n return { exitCode: 0 };\n }\n\n if (isVersionToken(firstToken)) {\n context.writeStdout(`${context.version}\\n`);\n return { exitCode: 0 };\n }\n\n const command = findDoctorCommandByStandaloneName(firstToken);\n if (command === null) {\n context.writeStderr(renderUnknownCommand(firstToken));\n return { exitCode: 1 };\n }\n\n return runCommand(\n {\n argv: restTokens,\n command,\n context,\n },\n {\n services: options.services,\n },\n );\n}\n\nfunction isHelpToken(value: string): boolean {\n return value === \"--help\" || value === \"-h\" || value === \"help\";\n}\n\nfunction isVersionToken(value: string): boolean {\n return value === \"--version\" || value === \"-v\" || value === \"version\";\n}\n\nif (import.meta.main) {\n const result = await runCli(process.argv.slice(2));\n process.exit(result.exitCode);\n}\n"]}
@@ -0,0 +1,12 @@
1
+ export interface DoctorCommandContext {
2
+ readonly cwd: string;
3
+ readonly env: NodeJS.ProcessEnv;
4
+ readonly version: string;
5
+ writeStdout(text: string): void;
6
+ writeStderr(text: string): void;
7
+ }
8
+ export interface DoctorCommandRunResult {
9
+ readonly exitCode: number;
10
+ }
11
+ export declare function createDefaultCommandContext(): DoctorCommandContext;
12
+ //# sourceMappingURL=commandContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandContext.d.ts","sourceRoot":"","sources":["../src/commandContext.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,2BAA2B,IAAI,oBAAoB,CAYlE"}
@@ -0,0 +1,15 @@
1
+ import packageJson from "../package.json";
2
+ export function createDefaultCommandContext() {
3
+ return {
4
+ cwd: process.cwd(),
5
+ env: process.env,
6
+ version: packageJson.version,
7
+ writeStdout(text) {
8
+ process.stdout.write(text);
9
+ },
10
+ writeStderr(text) {
11
+ process.stderr.write(text);
12
+ },
13
+ };
14
+ }
15
+ //# sourceMappingURL=commandContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandContext.js","sourceRoot":"","sources":["../src/commandContext.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAc1C,MAAM,UAAU,2BAA2B;IACzC,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,WAAW,CAAC,IAAY;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,WAAW,CAAC,IAAY;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import packageJson from \"../package.json\";\n\nexport interface DoctorCommandContext {\n readonly cwd: string;\n readonly env: NodeJS.ProcessEnv;\n readonly version: string;\n writeStdout(text: string): void;\n writeStderr(text: string): void;\n}\n\nexport interface DoctorCommandRunResult {\n readonly exitCode: number;\n}\n\nexport function createDefaultCommandContext(): DoctorCommandContext {\n return {\n cwd: process.cwd(),\n env: process.env,\n version: packageJson.version,\n writeStdout(text: string) {\n process.stdout.write(text);\n },\n writeStderr(text: string) {\n process.stderr.write(text);\n },\n };\n}\n"]}
@@ -0,0 +1,71 @@
1
+ import type { AnkhCapabilityId, AnkhCommandDescriptor } from "@ankhorage/contracts/cli";
2
+ import type { DoctorTargetMode } from "./analysis.js";
3
+ import { analyzeDoctorTarget } from "./analysis.js";
4
+ import type { DoctorCommandContext, DoctorCommandRunResult } from "./commandContext.js";
5
+ type DoctorCommandName = "fix" | "package" | "repo" | "validate";
6
+ interface DoctorCommandRunRequest {
7
+ readonly context: DoctorCommandContext;
8
+ readonly inputPath?: string;
9
+ }
10
+ type DoctorCommandImplementation = (request: DoctorCommandRunRequest, services: DoctorCommandServices) => Promise<DoctorCommandRunResult>;
11
+ export interface DoctorCommandDefinition {
12
+ readonly capability: AnkhCapabilityId;
13
+ readonly mode: DoctorTargetMode;
14
+ readonly path: readonly [DoctorCommandName];
15
+ readonly standaloneName: DoctorCommandName;
16
+ readonly summary: string;
17
+ readonly run: DoctorCommandImplementation;
18
+ }
19
+ export interface DoctorCommandInvocation {
20
+ readonly argv: readonly string[];
21
+ readonly command: DoctorCommandDefinition;
22
+ readonly context: DoctorCommandContext;
23
+ }
24
+ export interface DoctorCommandServices {
25
+ readonly analyzeTarget: typeof analyzeDoctorTarget;
26
+ }
27
+ export interface RunDoctorCommandOptions {
28
+ readonly runCommandImpl?: RunDoctorCommandImpl;
29
+ readonly services?: Partial<DoctorCommandServices>;
30
+ }
31
+ export type RunDoctorCommandImpl = (request: DoctorCommandInvocation, options?: RunDoctorCommandOptions) => Promise<DoctorCommandRunResult>;
32
+ export declare const DOCTOR_COMMANDS: readonly [{
33
+ readonly standaloneName: "validate";
34
+ readonly path: readonly ["validate"];
35
+ readonly capability: "doctor.validate";
36
+ readonly mode: "validate";
37
+ readonly summary: "Auto-detect lightweight repo and package diagnostics for a local path.";
38
+ readonly run: typeof runValidateCommand;
39
+ }, {
40
+ readonly standaloneName: "fix";
41
+ readonly path: readonly ["fix"];
42
+ readonly capability: "doctor.fix";
43
+ readonly mode: "fix";
44
+ readonly summary: "Run the same lightweight checks and report that no automatic fixes exist yet.";
45
+ readonly run: typeof runFixCommand;
46
+ }, {
47
+ readonly standaloneName: "repo";
48
+ readonly path: readonly ["repo"];
49
+ readonly capability: "doctor.repo";
50
+ readonly mode: "repo";
51
+ readonly summary: "Force repo-light diagnostics for a repo/workspace-root candidate path.";
52
+ readonly run: typeof runRepoCommand;
53
+ }, {
54
+ readonly standaloneName: "package";
55
+ readonly path: readonly ["package"];
56
+ readonly capability: "doctor.package";
57
+ readonly mode: "package";
58
+ readonly summary: "Force package-light diagnostics for a directory containing package.json.";
59
+ readonly run: typeof runPackageCommand;
60
+ }];
61
+ export declare function runDoctorCommand(request: DoctorCommandInvocation, options?: RunDoctorCommandOptions): Promise<DoctorCommandRunResult>;
62
+ export declare function createProviderCommandDescriptors(): readonly AnkhCommandDescriptor[];
63
+ export declare function findDoctorCommandByStandaloneName(value: string): DoctorCommandDefinition | null;
64
+ export declare function renderRootHelp(version: string): string;
65
+ export declare function renderUnknownCommand(value: string): string;
66
+ declare function runValidateCommand(request: DoctorCommandRunRequest, services: DoctorCommandServices): Promise<DoctorCommandRunResult>;
67
+ declare function runFixCommand(request: DoctorCommandRunRequest, services: DoctorCommandServices): Promise<DoctorCommandRunResult>;
68
+ declare function runRepoCommand(request: DoctorCommandRunRequest, services: DoctorCommandServices): Promise<DoctorCommandRunResult>;
69
+ declare function runPackageCommand(request: DoctorCommandRunRequest, services: DoctorCommandServices): Promise<DoctorCommandRunResult>;
70
+ export {};
71
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAOpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAExF,KAAK,iBAAiB,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjE,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,KAAK,2BAA2B,GAAG,CACjC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,KAC5B,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAErC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,iBAAiB,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,2BAA2B,CAAC;CAC3C;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,mBAAmB,CAAC;CACpD;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,uBAAuB,EAChC,OAAO,CAAC,EAAE,uBAAuB,KAC9B,OAAO,CAAC,sBAAsB,CAAC,CAAC;AASrC,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiC2B,CAAC;AAExD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,EAChC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAuBjC;AAED,wBAAgB,gCAAgC,IAAI,SAAS,qBAAqB,EAAE,CAMnF;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,MAAM,GACZ,uBAAuB,GAAG,IAAI,CAEhC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmBtD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO1D;AA4CD,iBAAe,kBAAkB,CAC/B,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAYjC;AAED,iBAAe,aAAa,CAC1B,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAajC;AAED,iBAAe,cAAc,CAC3B,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAYjC;AAED,iBAAe,iBAAiB,CAC9B,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAYjC"}