@ankhorage/doctor 0.7.2 → 0.7.3

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - a6a33a3: Accept `@ankhorage/devtools` as a runtime dependency for `@ankhorage/ankh` while keeping the usual devDependency requirement for other public packages.
8
+
3
9
  ## 0.7.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # DOCTOR
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.7.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)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.7.2](././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
7
 
8
8
  Executable doctor provider and standalone CLI for lightweight Ankhorage repo and package compliance diagnostics.
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAGtF,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAQxB,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,CAgC5B;AAED,QAAA,MAAM,QAAQ,4BAAgC,CAAC;AAE/C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEtF,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AASxB,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,CAgC5B;AAED,QAAA,MAAM,QAAQ,4BAAgC,CAAC;AAE/C,eAAe,QAAQ,CAAC"}
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
2
1
  import { createProviderCommandDescriptors, DOCTOR_COMMANDS, runDoctorCommand, } from '../commands.js';
2
+ import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
3
3
  import { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from '../packageMetadata.js';
4
4
  export function createDoctorRuntimeProvider(options = {}) {
5
5
  const runCommandImpl = options.runCommandImpl ?? runDoctorCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EACL,gCAAgC,EAChC,eAAe,EAEf,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,UAAU,2BAA2B,CACzC,UAA8C,EAAE;IAEhD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAClE,MAAM,QAAQ,GAAG;QACf,aAAa,EAAE,gCAAgC;QAC/C,GAAG,OAAO,CAAC,QAAQ;KACY,CAAC;IAElC,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;YACxC,MAAM,OAAO,GAAuB,CAAC,OAAO,EAAE,EAAE,CAC9C,cAAc,CACZ;gBACE,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;gBACP,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,EACD;gBACE,QAAQ;aACT,CACF,CAAC;YAEJ,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC;KACkC,CAAC;AACzC,CAAC;AAED,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;AAE/C,eAAe,QAAQ,CAAC","sourcesContent":["import type { AnkhCommandHandler, AnkhRuntimeCommandProvider } from '@ankhorage/ankh';\n\nimport { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';\nimport {\n createProviderCommandDescriptors,\n DOCTOR_COMMANDS,\n type DoctorCommandServices,\n runDoctorCommand,\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 const services = {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n } satisfies DoctorCommandServices;\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 const handler: AnkhCommandHandler = (request) =>\n runCommandImpl(\n {\n argv: request.argv,\n command,\n context: request.context,\n },\n {\n services,\n },\n );\n\n return {\n path: command.path,\n handler,\n };\n }),\n } satisfies AnkhRuntimeCommandProvider;\n}\n\nconst provider = createDoctorRuntimeProvider();\n\nexport default provider;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAChC,eAAe,EAEf,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,UAAU,2BAA2B,CACzC,UAA8C,EAAE;IAEhD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAClE,MAAM,QAAQ,GAAG;QACf,aAAa,EAAE,gCAAgC;QAC/C,GAAG,OAAO,CAAC,QAAQ;KACY,CAAC;IAElC,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;YACxC,MAAM,OAAO,GAAuB,CAAC,OAAO,EAAE,EAAE,CAC9C,cAAc,CACZ;gBACE,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;gBACP,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,EACD;gBACE,QAAQ;aACT,CACF,CAAC;YAEJ,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC;KACkC,CAAC;AACzC,CAAC;AAED,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;AAE/C,eAAe,QAAQ,CAAC","sourcesContent":["import type { AnkhCommandHandler, AnkhRuntimeCommandProvider } from '@ankhorage/ankh';\n\nimport {\n createProviderCommandDescriptors,\n DOCTOR_COMMANDS,\n type DoctorCommandServices,\n runDoctorCommand,\n type RunDoctorCommandImpl,\n} from '../commands.js';\nimport { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.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 const services = {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n } satisfies DoctorCommandServices;\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 const handler: AnkhCommandHandler = (request) =>\n runCommandImpl(\n {\n argv: request.argv,\n command,\n context: request.context,\n },\n {\n services,\n },\n );\n\n return {\n path: command.path,\n handler,\n };\n }),\n } satisfies AnkhRuntimeCommandProvider;\n}\n\nconst provider = createDoctorRuntimeProvider();\n\nexport default provider;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EACL,KAAK,qBAAqB,EAK1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAExB,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,CAkCjC"}
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EACL,KAAK,qBAAqB,EAK1B,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAGxB,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,CAkCjC"}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
- import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
3
2
  import { createDefaultCommandContext } from '../commandContext.js';
4
3
  import { findDoctorCommandByStandaloneName, renderRootHelp, renderUnknownCommand, runDoctorCommand, } from '../commands.js';
4
+ import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
5
5
  export async function runCli(argv, options = {}) {
6
6
  const context = options.context ?? createDefaultCommandContext();
7
7
  const runCommand = options.runCommandImpl ?? runDoctorCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAEL,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AAQxB,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;YACR,aAAa,EAAE,gCAAgC;YAC/C,GAAG,OAAO,CAAC,QAAQ;SACpB;KACF,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 { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';\nimport type { DoctorCommandContext, DoctorCommandRunResult } from '../commandContext.js';\nimport { createDefaultCommandContext } from '../commandContext.js';\nimport {\n type DoctorCommandServices,\n findDoctorCommandByStandaloneName,\n renderRootHelp,\n renderUnknownCommand,\n runDoctorCommand,\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: {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n },\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"]}
1
+ {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../../src/cli/standalone.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAEL,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,gCAAgC,CAAC;AAQlF,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;YACR,aAAa,EAAE,gCAAgC;YAC/C,GAAG,OAAO,CAAC,QAAQ;SACpB;KACF,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 type DoctorCommandServices,\n findDoctorCommandByStandaloneName,\n renderRootHelp,\n renderUnknownCommand,\n runDoctorCommand,\n type RunDoctorCommandImpl,\n} from '../commands.js';\nimport { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.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: {\n analyzeTarget: analyzeDoctorTargetWithCliLayout,\n ...options.services,\n },\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,4 @@
1
+ import { type DoctorAnalysisRequest, type DoctorAnalysisResult } from './analysis.js';
2
+ export declare function analyzeDoctorTarget(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
3
+ export declare function analyzeDoctorTargetWithCliLayout(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
4
+ //# sourceMappingURL=dependencyPolicyAnalysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencyPolicyAnalysis.d.ts","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAQvB,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAI/B"}
@@ -0,0 +1,76 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { analyzeDoctorTarget as analyzeBaseDoctorTarget, } from './analysis.js';
4
+ import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
5
+ const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
6
+ const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
7
+ const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
8
+ export async function analyzeDoctorTarget(request) {
9
+ return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));
10
+ }
11
+ export async function analyzeDoctorTargetWithCliLayout(request) {
12
+ return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTargetWithCliLayout(request));
13
+ }
14
+ async function applyDevtoolsDependencyPlacementPolicy(result) {
15
+ if (!result.hasPackageJson) {
16
+ return result;
17
+ }
18
+ const packageJsonPath = path.join(result.targetPath, 'package.json');
19
+ const packageJson = await readPackageJson(packageJsonPath);
20
+ if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
21
+ return result;
22
+ }
23
+ const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
24
+ const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
25
+ const diagnosticsWithoutPlacementRule = result.diagnostics.filter((diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID);
26
+ const diagnostics = hasRuntimeDevtools
27
+ ? diagnosticsWithoutPlacementRule
28
+ : [
29
+ ...diagnosticsWithoutPlacementRule,
30
+ createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
31
+ ];
32
+ return {
33
+ ...result,
34
+ diagnostics,
35
+ fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
36
+ };
37
+ }
38
+ async function readPackageJson(packageJsonPath) {
39
+ try {
40
+ const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
41
+ return isRecord(parsed) ? parsed : null;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result) {
48
+ return {
49
+ code: 'missing-dependency',
50
+ message: '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
51
+ path: packageJsonPath,
52
+ profile: result.profile,
53
+ ruleId: DEVTOOLS_REQUIRED_RULE_ID,
54
+ severity: 'error',
55
+ };
56
+ }
57
+ function expectsDevtools(packageJson) {
58
+ const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
59
+ if (scripts === null) {
60
+ return false;
61
+ }
62
+ return Object.values(scripts).some((scriptValue) => isNonEmptyString(scriptValue) &&
63
+ (scriptValue.includes('ankhorage-eslint') ||
64
+ scriptValue.includes('ankhorage-prettier') ||
65
+ scriptValue.includes('ankhorage-knip')));
66
+ }
67
+ function hasDependency(dependencyMap, dependencyName) {
68
+ return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
69
+ }
70
+ function isNonEmptyString(value) {
71
+ return typeof value === 'string' && value.trim() !== '';
72
+ }
73
+ function isRecord(value) {
74
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
75
+ }
76
+ //# sourceMappingURL=dependencyPolicyAnalysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencyPolicyAnalysis.js","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport {\n analyzeDoctorTarget as analyzeBaseDoctorTarget,\n type DoctorAnalysisRequest,\n type DoctorAnalysisResult,\n} from './analysis.js';\nimport { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\n\nconst ANKH_PACKAGE_NAME = '@ankhorage/ankh';\nconst DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';\nconst DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';\n\nexport async function analyzeDoctorTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));\n}\n\nexport async function analyzeDoctorTargetWithCliLayout(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyDevtoolsDependencyPlacementPolicy(\n await analyzeBaseDoctorTargetWithCliLayout(request),\n );\n}\n\nasync function applyDevtoolsDependencyPlacementPolicy(\n result: DoctorAnalysisResult,\n): Promise<DoctorAnalysisResult> {\n if (!result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {\n return result;\n }\n\n const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;\n const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);\n const diagnosticsWithoutPlacementRule = result.diagnostics.filter(\n (diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID,\n );\n const diagnostics = hasRuntimeDevtools\n ? diagnosticsWithoutPlacementRule\n : [\n ...diagnosticsWithoutPlacementRule,\n createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),\n ];\n\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction createMissingRuntimeDevtoolsDiagnostic(\n packageJsonPath: string,\n result: DoctorAnalysisResult,\n): DoctorDiagnostic {\n return {\n code: 'missing-dependency',\n message:\n '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',\n path: packageJsonPath,\n profile: result.profile,\n ruleId: DEVTOOLS_REQUIRED_RULE_ID,\n severity: 'error',\n };\n}\n\nfunction expectsDevtools(packageJson: Record<string, unknown>): boolean {\n const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;\n if (scripts === null) {\n return false;\n }\n\n return Object.values(scripts).some(\n (scriptValue) =>\n isNonEmptyString(scriptValue) &&\n (scriptValue.includes('ankhorage-eslint') ||\n scriptValue.includes('ankhorage-prettier') ||\n scriptValue.includes('ankhorage-knip')),\n );\n}\n\nfunction hasDependency(\n dependencyMap: Record<string, unknown> | null,\n dependencyName: string,\n): boolean {\n return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  export type { DoctorAnalysisRequest, DoctorAnalysisResult, DoctorFixPlan, DoctorPlannedChange, DoctorPlannedChangeKind, DoctorTargetCheck, DoctorTargetMode, } from './analysis.js';
2
- export { analyzeDoctorTarget } from './analysis.js';
3
2
  export { createDoctorRuntimeProvider, type CreateDoctorRuntimeProviderOptions, } from './cli/index.js';
4
3
  export { runCli } from './cli/standalone.js';
5
- export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
6
4
  export type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
7
5
  export { createDefaultCommandContext } from './commandContext.js';
6
+ export { analyzeDoctorTarget, analyzeDoctorTargetWithCliLayout, } from './dependencyPolicyAnalysis.js';
8
7
  export type { DoctorDiagnostic, DoctorDiagnosticCode, DoctorDiagnosticSeverity, DoctorPolicyProfile, DoctorRuleId, } from './diagnostics.js';
9
8
  export { analyzeAppManifest, analyzeAppManifestFile, analyzeAppManifestTarget, } from './manifestAnalysis.js';
10
9
  export { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_METADATA, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from './packageMetadata.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,2BAA2B,EAC3B,KAAK,kCAAkC,GACxC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,2BAA2B,EAC3B,KAAK,kCAAkC,GACxC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- export { analyzeDoctorTarget } from './analysis.js';
2
1
  export { createDoctorRuntimeProvider, } from './cli/index.js';
3
2
  export { runCli } from './cli/standalone.js';
4
- export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
5
3
  export { createDefaultCommandContext } from './commandContext.js';
4
+ export { analyzeDoctorTarget, analyzeDoctorTargetWithCliLayout, } from './dependencyPolicyAnalysis.js';
6
5
  export { analyzeAppManifest, analyzeAppManifestFile, analyzeAppManifestTarget, } from './manifestAnalysis.js';
7
6
  export { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY, DOCTOR_PACKAGE_METADATA, DOCTOR_PACKAGE_NAME, DOCTOR_PACKAGE_VERSION, } from './packageMetadata.js';
8
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAQlE,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC","sourcesContent":["export type {\n DoctorAnalysisRequest,\n DoctorAnalysisResult,\n DoctorFixPlan,\n DoctorPlannedChange,\n DoctorPlannedChangeKind,\n DoctorTargetCheck,\n DoctorTargetMode,\n} from './analysis.js';\nexport { analyzeDoctorTarget } from './analysis.js';\nexport {\n createDoctorRuntimeProvider,\n type CreateDoctorRuntimeProviderOptions,\n} from './cli/index.js';\nexport { runCli } from './cli/standalone.js';\nexport { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';\nexport type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';\nexport { createDefaultCommandContext } from './commandContext.js';\nexport type {\n DoctorDiagnostic,\n DoctorDiagnosticCode,\n DoctorDiagnosticSeverity,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\nexport {\n analyzeAppManifest,\n analyzeAppManifestFile,\n analyzeAppManifestTarget,\n} from './manifestAnalysis.js';\nexport {\n DOCTOR_CAPABILITIES,\n DOCTOR_COMMAND_CATEGORY,\n DOCTOR_PACKAGE_METADATA,\n DOCTOR_PACKAGE_NAME,\n DOCTOR_PACKAGE_VERSION,\n} from './packageMetadata.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,+BAA+B,CAAC;AAQvC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC","sourcesContent":["export type {\n DoctorAnalysisRequest,\n DoctorAnalysisResult,\n DoctorFixPlan,\n DoctorPlannedChange,\n DoctorPlannedChangeKind,\n DoctorTargetCheck,\n DoctorTargetMode,\n} from './analysis.js';\nexport {\n createDoctorRuntimeProvider,\n type CreateDoctorRuntimeProviderOptions,\n} from './cli/index.js';\nexport { runCli } from './cli/standalone.js';\nexport type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';\nexport { createDefaultCommandContext } from './commandContext.js';\nexport {\n analyzeDoctorTarget,\n analyzeDoctorTargetWithCliLayout,\n} from './dependencyPolicyAnalysis.js';\nexport type {\n DoctorDiagnostic,\n DoctorDiagnosticCode,\n DoctorDiagnosticSeverity,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\nexport {\n analyzeAppManifest,\n analyzeAppManifestFile,\n analyzeAppManifestTarget,\n} from './manifestAnalysis.js';\nexport {\n DOCTOR_CAPABILITIES,\n DOCTOR_COMMAND_CATEGORY,\n DOCTOR_PACKAGE_METADATA,\n DOCTOR_PACKAGE_NAME,\n DOCTOR_PACKAGE_VERSION,\n} from './packageMetadata.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/doctor",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Executable Ankh provider and standalone CLI for lightweight repo and package compliance diagnostics.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
package/src/cli/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { AnkhCommandHandler, AnkhRuntimeCommandProvider } from '@ankhorage/ankh';
2
2
 
3
- import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
4
3
  import {
5
4
  createProviderCommandDescriptors,
6
5
  DOCTOR_COMMANDS,
@@ -8,6 +7,7 @@ import {
8
7
  runDoctorCommand,
9
8
  type RunDoctorCommandImpl,
10
9
  } from '../commands.js';
10
+ import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
11
11
  import {
12
12
  DOCTOR_CAPABILITIES,
13
13
  DOCTOR_COMMAND_CATEGORY,
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { analyzeDoctorTargetWithCliLayout } from '../cliLayoutAnalysis.js';
4
3
  import type { DoctorCommandContext, DoctorCommandRunResult } from '../commandContext.js';
5
4
  import { createDefaultCommandContext } from '../commandContext.js';
6
5
  import {
@@ -11,6 +10,7 @@ import {
11
10
  runDoctorCommand,
12
11
  type RunDoctorCommandImpl,
13
12
  } from '../commands.js';
13
+ import { analyzeDoctorTargetWithCliLayout } from '../dependencyPolicyAnalysis.js';
14
14
 
15
15
  export interface DoctorCliOptions {
16
16
  readonly context?: DoctorCommandContext;
@@ -0,0 +1,114 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import {
5
+ analyzeDoctorTarget as analyzeBaseDoctorTarget,
6
+ type DoctorAnalysisRequest,
7
+ type DoctorAnalysisResult,
8
+ } from './analysis.js';
9
+ import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
10
+ import type { DoctorDiagnostic } from './diagnostics.js';
11
+
12
+ const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
13
+ const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
14
+ const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
15
+
16
+ export async function analyzeDoctorTarget(
17
+ request: DoctorAnalysisRequest,
18
+ ): Promise<DoctorAnalysisResult> {
19
+ return applyDevtoolsDependencyPlacementPolicy(await analyzeBaseDoctorTarget(request));
20
+ }
21
+
22
+ export async function analyzeDoctorTargetWithCliLayout(
23
+ request: DoctorAnalysisRequest,
24
+ ): Promise<DoctorAnalysisResult> {
25
+ return applyDevtoolsDependencyPlacementPolicy(
26
+ await analyzeBaseDoctorTargetWithCliLayout(request),
27
+ );
28
+ }
29
+
30
+ async function applyDevtoolsDependencyPlacementPolicy(
31
+ result: DoctorAnalysisResult,
32
+ ): Promise<DoctorAnalysisResult> {
33
+ if (!result.hasPackageJson) {
34
+ return result;
35
+ }
36
+
37
+ const packageJsonPath = path.join(result.targetPath, 'package.json');
38
+ const packageJson = await readPackageJson(packageJsonPath);
39
+ if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
40
+ return result;
41
+ }
42
+
43
+ const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
44
+ const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
45
+ const diagnosticsWithoutPlacementRule = result.diagnostics.filter(
46
+ (diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID,
47
+ );
48
+ const diagnostics = hasRuntimeDevtools
49
+ ? diagnosticsWithoutPlacementRule
50
+ : [
51
+ ...diagnosticsWithoutPlacementRule,
52
+ createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
53
+ ];
54
+
55
+ return {
56
+ ...result,
57
+ diagnostics,
58
+ fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
59
+ };
60
+ }
61
+
62
+ async function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {
63
+ try {
64
+ const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;
65
+ return isRecord(parsed) ? parsed : null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ function createMissingRuntimeDevtoolsDiagnostic(
72
+ packageJsonPath: string,
73
+ result: DoctorAnalysisResult,
74
+ ): DoctorDiagnostic {
75
+ return {
76
+ code: 'missing-dependency',
77
+ message:
78
+ '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
79
+ path: packageJsonPath,
80
+ profile: result.profile,
81
+ ruleId: DEVTOOLS_REQUIRED_RULE_ID,
82
+ severity: 'error',
83
+ };
84
+ }
85
+
86
+ function expectsDevtools(packageJson: Record<string, unknown>): boolean {
87
+ const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
88
+ if (scripts === null) {
89
+ return false;
90
+ }
91
+
92
+ return Object.values(scripts).some(
93
+ (scriptValue) =>
94
+ isNonEmptyString(scriptValue) &&
95
+ (scriptValue.includes('ankhorage-eslint') ||
96
+ scriptValue.includes('ankhorage-prettier') ||
97
+ scriptValue.includes('ankhorage-knip')),
98
+ );
99
+ }
100
+
101
+ function hasDependency(
102
+ dependencyMap: Record<string, unknown> | null,
103
+ dependencyName: string,
104
+ ): boolean {
105
+ return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
106
+ }
107
+
108
+ function isNonEmptyString(value: unknown): value is string {
109
+ return typeof value === 'string' && value.trim() !== '';
110
+ }
111
+
112
+ function isRecord(value: unknown): value is Record<string, unknown> {
113
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
114
+ }
package/src/index.ts CHANGED
@@ -7,15 +7,17 @@ export type {
7
7
  DoctorTargetCheck,
8
8
  DoctorTargetMode,
9
9
  } from './analysis.js';
10
- export { analyzeDoctorTarget } from './analysis.js';
11
10
  export {
12
11
  createDoctorRuntimeProvider,
13
12
  type CreateDoctorRuntimeProviderOptions,
14
13
  } from './cli/index.js';
15
14
  export { runCli } from './cli/standalone.js';
16
- export { analyzeDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
17
15
  export type { DoctorCommandContext, DoctorCommandRunResult } from './commandContext.js';
18
16
  export { createDefaultCommandContext } from './commandContext.js';
17
+ export {
18
+ analyzeDoctorTarget,
19
+ analyzeDoctorTargetWithCliLayout,
20
+ } from './dependencyPolicyAnalysis.js';
19
21
  export type {
20
22
  DoctorDiagnostic,
21
23
  DoctorDiagnosticCode,