@ankhorage/devtools 1.21.3 → 2.0.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/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
 
4
4
  # @ankhorage/devtools
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v1.21.3](././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: v2.0.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
7
 
8
- Shared tooling, repository automation, runtime policies, and agent standards for Ankhorage TypeScript projects
8
+ Shared tooling, repository automation, and agent standards for Ankhorage TypeScript projects
9
9
 
10
10
  ## Usage
11
11
 
@@ -13,7 +13,7 @@ Shared development tools and repository standards for Ankhorage TypeScript proje
13
13
 
14
14
  ## What it owns
15
15
 
16
- `@ankhorage/devtools` is the single source of truth for these separate concerns:
16
+ `@ankhorage/policy` is the source of truth for canonical Ankhorage policy. `@ankhorage/devtools` is the execution and synchronization layer for these repository concerns:
17
17
 
18
18
  ```text
19
19
  src/
@@ -30,8 +30,8 @@ src/
30
30
  └── vscode/
31
31
  ```
32
32
 
33
- - `policy`: shared repository runtime policy, including the canonical Bun version
34
- - `changesets`: package-resolved Changesets execution and release command policy
33
+ - `policy`: policy application/rendering adapters that consume `@ankhorage/policy`
34
+ - `changesets`: package-resolved Changesets execution using centrally defined commands
35
35
  - `agents`: canonical repository `AGENTS.md` rendered from stable package identity
36
36
  - `skills`: immutable Ankhorage-owned repository skills under `.agents/skills/`
37
37
  - `eslint`: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
@@ -41,7 +41,7 @@ src/
41
41
  - `workflows`: canonical `.github/workflows/ci.yml` and `release.yml`
42
42
  - `vscode`: canonical `.vscode/settings.json` and `extensions.json`
43
43
 
44
- The package owns the supported Changesets CLI, ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories. It also owns the Bun runtime version used by Ankhorage repository metadata and managed workflows.
44
+ The package owns the supported Changesets CLI, ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories. The Bun/Node runtime baseline and canonical Changesets/PKGViz policy values are owned by `@ankhorage/policy` and rendered by Devtools.
45
45
 
46
46
  ## Bootstrap
47
47
 
@@ -316,7 +316,7 @@ export default createKnipConfig();
316
316
 
317
317
  ## Managed Bun runtime policy
318
318
 
319
- The canonical Bun policy is defined once in devtools and consumed by both package and workflow synchronization. The current policy is:
319
+ The canonical Bun policy is defined by `@ankhorage/policy` and consumed by Devtools package and workflow synchronization. The current released policy is:
320
320
 
321
321
  <!-- devtools-bun-policy:start -->
322
322
 
@@ -328,7 +328,7 @@ packageManager bun@1.4.2
328
328
 
329
329
  <!-- devtools-bun-policy:end -->
330
330
 
331
- Renovate owns the single `BUN_VERSION` literal in `src/policy/bunRuntimePolicy.ts`. Its trusted base-branch workflow invokes `bun scripts/sync-renovate-owner.ts sync repository` to regenerate `packageManager`, `@types/bun`, the Bun workflow setup versions, this documentation block, and `bun.lock`, then runs `bun scripts/sync-renovate-owner.ts status repository` to reject stale generated artifacts. Do not synchronize those values manually in a Renovate branch.
331
+ `ankhorage/policy` owns and updates the canonical Bun and `@types/bun` literals through Renovate. A released Policy update reaches Devtools as a normal dependency update; Devtools then renders that policy into `packageManager`, `@types/bun`, workflow setup versions, this documentation block, and `bun.lock`. The trusted owner workflow uses `bun scripts/sync-renovate-owner.ts sync repository` to regenerate those artifacts and `bun scripts/sync-renovate-owner.ts status repository` to reject stale rendered state. Do not duplicate runtime policy literals in Devtools.
332
332
 
333
333
  ## Managed package contract
334
334
 
@@ -363,7 +363,7 @@ A repository participates in Changesets synchronization when `.changeset/config.
363
363
  .github/workflows/release.yml
364
364
  ```
365
365
 
366
- CI and Release render their `bun-version` from the same managed Bun runtime policy used for `package.json`. They also render Changesets status, version, and publish commands from the same policy that owns the synchronized package scripts. The CI workflow installs that Bun version with the frozen lockfile, builds before repository-provider validation, runs `bunx @ankhorage/ankh doctor validate .`, and conditionally runs lint, formatting, Knip, tests, typecheck, and the strict `changeset:status --since=origin/main` guard for pull requests. After a green change reaches `main`, Release uses the scoped Ankhorage Renovate Sync App token to apply Changesets versioning directly to `main` in a `chore(release)` `[skip ci]` commit and publishes without creating a second Version Packages pull request. Release publishing calls the Devtools-owned runner through the synchronized package script; it never resolves an ambient or mutable Changesets executable. Changesets v3 creates the local release tags; the managed workflow pushes those exact tags and creates any missing GitHub Releases directly, without parsing Changesets’ human-readable publish output.
366
+ CI and Release render their `bun-version` from `@ankhorage/policy/repository`, the same policy used for `package.json`. They also render Changesets status, version, and publish commands from that central policy. The CI workflow installs that Bun version with the frozen lockfile, builds before repository-provider validation, runs `bunx @ankhorage/ankh doctor validate .`, and conditionally runs lint, formatting, Knip, tests, typecheck, and the strict `changeset:status --since=origin/main` guard for pull requests. After a green change reaches `main`, Release uses the scoped Ankhorage Renovate Sync App token to apply Changesets versioning directly to `main` in a `chore(release)` `[skip ci]` commit and publishes without creating a second Version Packages pull request. Release publishing calls the Devtools-owned runner through the synchronized package script; it never resolves an ambient or mutable Changesets executable. Changesets v3 creates the local release tags; the managed workflow pushes those exact tags and creates any missing GitHub Releases directly, without parsing Changesets’ human-readable publish output.
367
367
 
368
368
  For the **first npm publication** of a new `@ankhorage/*` package, the organization publishing credential must be able to create/publish packages in the `@ankhorage` scope. With a granular npm token, grant package permission **Read and write (publish and stage)** to the `@ankhorage` scope or **All Packages**. If initial publication fails after Changesets has already pushed the release commit, correct the npm credential and rerun Release; the current unpublished version is reused and must not be bumped again. The managed workflow diagnoses this first-publish state separately from registry/network failures.
369
369
 
@@ -1,4 +1,4 @@
1
- import { bunRuntimePolicy } from '../policy/bunRuntimePolicy.js';
1
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
2
2
  const REQUIRED_README_SNIPPETS = [
3
3
  'ankh devtools lint',
4
4
  'ankh devtools changeset',
@@ -35,9 +35,10 @@ const REQUIRED_README_SNIPPETS = [
35
35
  '<!-- devtools-bun-policy:end -->',
36
36
  'bun scripts/sync-renovate-owner.ts sync repository',
37
37
  'bun scripts/sync-renovate-owner.ts status',
38
- bunRuntimePolicy.version,
39
- bunRuntimePolicy.packageManager,
40
- bunRuntimePolicy.typesRange,
38
+ '@ankhorage/policy',
39
+ REPOSITORY_POLICY.runtime.bun.version,
40
+ REPOSITORY_POLICY.runtime.bun.packageManager,
41
+ REPOSITORY_POLICY.runtime.bun.typesRange,
41
42
  ];
42
43
  export function getReadmeDocumentationErrors(readmeContents) {
43
44
  return REQUIRED_README_SNIPPETS.flatMap((snippet) => readmeContents.includes(snippet)
@@ -1,4 +1,4 @@
1
- /*** Synchronize or validate Renovate-owned Devtools policy artifacts. */
1
+ /*** Synchronize or validate Renovate-owned Devtools artifacts from central repository policy. */
2
2
  export declare function synchronizeRenovateOwnerAsync(operation: OwnerSyncOperation, targetDirectory: string, options?: OwnerSyncOptions): Promise<void>;
3
3
  type OwnerSyncOperation = 'status' | 'sync';
4
4
  interface OwnerSyncOptions {
@@ -1,19 +1,18 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
3
  import { dirname, resolve } from 'node:path';
4
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
4
5
  import { resolveApmReleaseCommandAsync } from '../features/apm-release-validation/adapters/outbound/resolveApmReleaseCommandAsync.js';
5
6
  import { resolveStructureReleaseCommandAsync } from '../features/structure-descriptor-generation/adapters/outbound/resolveStructureReleaseCommandAsync.js';
6
7
  import { applyBunRuntimePolicy } from '../policy/applyBunRuntimePolicy.js';
7
- import { nodeRuntimePolicy } from '../policy/bunRuntimePolicy.js';
8
8
  import { renderBunPolicyDocumentation } from '../policy/renderBunPolicyDocumentation.js';
9
9
  import { readCurrentDoctorVersion } from '../tools/workflows/readCurrentDoctorVersion.js';
10
10
  import { renderRenovateWorkflowAsync } from '../tools/workflows/renderRenovateWorkflowAsync.js';
11
11
  import { renderWorkflowAsync, } from '../tools/workflows/renderWorkflowAsync.js';
12
- /*** Synchronize or validate Renovate-owned Devtools policy artifacts. */
12
+ /*** Synchronize or validate Renovate-owned Devtools artifacts from central repository policy. */
13
13
  export async function synchronizeRenovateOwnerAsync(operation, targetDirectory, options = {}) {
14
14
  const target = resolve(targetDirectory);
15
- const policy = await readTargetBunPolicyAsync(target);
16
- const definitions = await createManagedDefinitionsAsync(target, policy);
15
+ const definitions = await createManagedDefinitionsAsync(target);
17
16
  await assertDevtoolsTargetAsync(target);
18
17
  if (operation === 'sync') {
19
18
  await syncDefinitionsAsync(target, definitions);
@@ -34,18 +33,18 @@ async function assertDevtoolsTargetAsync(targetDirectory) {
34
33
  throw new Error('The Renovate owner sync target must be @ankhorage/devtools.');
35
34
  }
36
35
  }
37
- /*** Build the canonical owner-managed artifact definitions for the target repository. */
38
- async function createManagedDefinitionsAsync(targetDirectory, policy) {
36
+ /*** Build the owner-managed artifact definitions from released central policy. */
37
+ async function createManagedDefinitionsAsync(targetDirectory) {
39
38
  const manifest = JSON.parse(await readFile(resolve(targetDirectory, 'package.json'), 'utf8'));
40
39
  if (!isRecord(manifest)) {
41
40
  throw new Error('Devtools package.json must contain a JSON object.');
42
41
  }
43
42
  const readme = await readFile(resolve(targetDirectory, 'README.md'), 'utf8');
44
- const workflowPolicy = await createWorkflowPolicyAsync(targetDirectory, policy);
43
+ const workflowPolicy = await createWorkflowPolicyAsync(targetDirectory);
45
44
  return [
46
45
  {
47
46
  relativePath: 'package.json',
48
- contents: serializePackageManifest(applyBunRuntimePolicy(manifest, policy)),
47
+ contents: serializePackageManifest(applyBunRuntimePolicy(manifest)),
49
48
  },
50
49
  {
51
50
  relativePath: '.github/workflows/ci.yml',
@@ -61,18 +60,18 @@ async function createManagedDefinitionsAsync(targetDirectory, policy) {
61
60
  },
62
61
  {
63
62
  relativePath: 'README.md',
64
- contents: renderBunPolicyDocumentation(readme, policy),
63
+ contents: renderBunPolicyDocumentation(readme),
65
64
  },
66
65
  ];
67
66
  }
68
67
  /*** Build the self-hosted workflow policy used by Devtools owner synchronization. */
69
- async function createWorkflowPolicyAsync(targetDirectory, policy) {
68
+ async function createWorkflowPolicyAsync(targetDirectory) {
70
69
  return {
71
70
  apmReleaseCommand: await resolveApmReleaseCommandAsync(targetDirectory),
72
71
  structureReleaseCommand: await resolveStructureReleaseCommandAsync(targetDirectory),
73
- bunVersion: policy.version,
72
+ bunVersion: REPOSITORY_POLICY.runtime.bun.version,
74
73
  doctorVersion: readCurrentDoctorVersion(),
75
- nodeVersion: nodeRuntimePolicy.setupVersion,
74
+ nodeVersion: REPOSITORY_POLICY.runtime.node.setupVersion,
76
75
  };
77
76
  }
78
77
  /*** Return owner-managed artifact paths whose current bytes differ from policy output. */
@@ -95,25 +94,6 @@ function isNodeError(error) {
95
94
  function isRecord(value) {
96
95
  return typeof value === 'object' && value !== null && !Array.isArray(value);
97
96
  }
98
- /*** Read the target repository's canonical Bun policy literals. */
99
- async function readTargetBunPolicyAsync(targetDirectory) {
100
- const contents = await readFile(resolve(targetDirectory, 'src/policy/bunRuntimePolicy.ts'), 'utf8');
101
- const matches = [...contents.matchAll(BUN_VERSION_PATTERN)];
102
- const version = matches.length === 1 ? matches[0]?.[1] : undefined;
103
- if (version === undefined) {
104
- throw new Error('Expected exactly one canonical BUN_VERSION literal in the target policy.');
105
- }
106
- const typesMatches = [...contents.matchAll(BUN_TYPES_VERSION_PATTERN)];
107
- const typesVersion = typesMatches.length === 1 ? typesMatches[0]?.[1] : undefined;
108
- if (typesVersion === undefined) {
109
- throw new Error('Expected exactly one canonical BUN_TYPES_VERSION literal in the target policy.');
110
- }
111
- return {
112
- packageManager: `bun@${version}`,
113
- typesRange: `^${typesVersion}`,
114
- version,
115
- };
116
- }
117
97
  /*** Synchronize or validate the target Bun lockfile through Bun itself. */
118
98
  async function runBunLockfileAsync(operation, targetDirectory) {
119
99
  const args = [
@@ -155,5 +135,3 @@ async function syncDefinitionsAsync(targetDirectory, definitions) {
155
135
  await writeFile(targetPath, contents, 'utf8');
156
136
  }
157
137
  }
158
- const BUN_VERSION_PATTERN = /const BUN_VERSION = '(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)';/gu;
159
- const BUN_TYPES_VERSION_PATTERN = /const BUN_TYPES_VERSION = '(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)';/gu;
@@ -1,4 +1,2 @@
1
- export declare function applyBunRuntimePolicy(manifest: Record<string, unknown>, policy: {
2
- readonly packageManager: string;
3
- readonly typesRange: string;
4
- }): Record<string, unknown>;
1
+ /*** Apply the canonical Bun runtime policy to one package manifest. */
2
+ export declare function applyBunRuntimePolicy(manifest: Record<string, unknown>): Record<string, unknown>;
@@ -1,9 +1,11 @@
1
- export function applyBunRuntimePolicy(manifest, policy) {
1
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
2
+ /*** Apply the canonical Bun runtime policy to one package manifest. */
3
+ export function applyBunRuntimePolicy(manifest) {
2
4
  const devDependencies = toRecord(manifest.devDependencies);
3
- devDependencies['@types/bun'] = policy.typesRange;
5
+ devDependencies['@types/bun'] = REPOSITORY_POLICY.runtime.bun.typesRange;
4
6
  return {
5
7
  ...manifest,
6
- packageManager: policy.packageManager,
8
+ packageManager: REPOSITORY_POLICY.runtime.bun.packageManager,
7
9
  devDependencies,
8
10
  };
9
11
  }
@@ -1,3 +1,2 @@
1
- import type { BunPolicy } from '../types/bunPolicy.js';
2
1
  /*** Render the managed Bun policy section while preserving the surrounding guide. */
3
- export declare function renderBunPolicyDocumentation(readme: string, policy: BunPolicy): string;
2
+ export declare function renderBunPolicyDocumentation(readme: string): string;
@@ -1,5 +1,6 @@
1
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
1
2
  /*** Render the managed Bun policy section while preserving the surrounding guide. */
2
- export function renderBunPolicyDocumentation(readme, policy) {
3
+ export function renderBunPolicyDocumentation(readme) {
3
4
  const startIndex = readme.indexOf(README_POLICY_START);
4
5
  const endIndex = readme.indexOf(README_POLICY_END);
5
6
  if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
@@ -9,11 +10,12 @@ export function renderBunPolicyDocumentation(readme, policy) {
9
10
  readme.includes(README_POLICY_END, endIndex + README_POLICY_END.length)) {
10
11
  throw new Error('README.md must contain exactly one Devtools Bun policy marker pair.');
11
12
  }
12
- const replacement = `${README_POLICY_START}\n\n${renderReadmePolicy(policy)}\n\n${README_POLICY_END}`;
13
+ const replacement = `${README_POLICY_START}\n\n${renderReadmePolicy()}\n\n${README_POLICY_END}`;
13
14
  return `${readme.slice(0, startIndex)}${replacement}${readme.slice(endIndex + README_POLICY_END.length)}`;
14
15
  }
15
16
  /*** Format canonical runtime and type-package versions for the guide. */
16
- function renderReadmePolicy(policy) {
17
+ function renderReadmePolicy() {
18
+ const policy = REPOSITORY_POLICY.runtime.bun;
17
19
  return `\`\`\`text
18
20
  Bun runtime ${policy.version}
19
21
  packageManager ${policy.packageManager}
@@ -1,4 +1,4 @@
1
- /*** Resolve the centrally pinned PKGViz CI audit for repositories with analyzable source. */
1
+ /*** Resolve the central PKGViz CI audit for repositories with analyzable source. */
2
2
  export declare function resolvePkgvizAuditPolicyAsync(targetDirectory: string): Promise<PkgvizAuditPolicy | undefined>;
3
3
  interface PkgvizAuditPolicy {
4
4
  readonly artifactName: string;
@@ -1,14 +1,12 @@
1
1
  import { stat } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- /*** Resolve the centrally pinned PKGViz CI audit for repositories with analyzable source. */
3
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
4
+ /*** Resolve the central PKGViz CI audit for repositories with analyzable source. */
4
5
  export async function resolvePkgvizAuditPolicyAsync(targetDirectory) {
5
6
  if (!(await hasSourceDirectoryAsync(targetDirectory)))
6
7
  return undefined;
7
- return {
8
- artifactName: 'pkgviz-audit',
9
- artifactPath: 'pkgviz-audit.json',
10
- command: 'bunx pkgviz@0.8.1 --out pkgviz-audit.json --rule cyclic-dependencies=block',
11
- };
8
+ const { artifactName, artifactPath, command } = REPOSITORY_POLICY.pkgvizAudit;
9
+ return { artifactName, artifactPath, command };
12
10
  }
13
11
  /*** Detect whether the managed repository has a source tree that PKGViz can inspect. */
14
12
  async function hasSourceDirectoryAsync(targetDirectory) {
@@ -7,7 +7,7 @@
7
7
  * `lint:fix`, `format`, `format:check`, and `knip:check` scripts are written.
8
8
  * Unrelated manifest fields, scripts, dependencies, and metadata are preserved.
9
9
  *
10
- * The Bun runtime policy is shared by every repository, including devtools itself. Devtools skips
10
+ * The Bun runtime policy comes from @ankhorage/policy for every repository, including devtools itself. Devtools skips
11
11
  * only its consumer dependency/script normalization so it never attempts to install itself.
12
12
  *
13
13
  * Status compares only the fields owned by this contract, so unrelated repository customization
@@ -19,9 +19,8 @@
19
19
  import { existsSync, readFileSync } from 'node:fs';
20
20
  import { readFile, writeFile } from 'node:fs/promises';
21
21
  import { resolve } from 'node:path';
22
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
22
23
  import { applyBunRuntimePolicy } from '../../policy/applyBunRuntimePolicy.js';
23
- import { bunRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
24
- import { changesetsPolicy } from '../../policy/changesetsPolicy.js';
25
24
  const PACKAGE_PATH = 'package.json';
26
25
  const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
27
26
  const BUN_TYPES_PACKAGE_NAME = '@types/bun';
@@ -85,26 +84,26 @@ export async function syncPackageManifest(targetDirectory, devtoolsVersion, opti
85
84
  /*** Apply the Devtools-owned package scripts, dependencies, and Bun runtime policy. */
86
85
  export function applyManagedPackageContract(manifest, devtoolsVersion, changesetsConfigExists = false) {
87
86
  if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
88
- return applyBunRuntimePolicy(manifest, bunRuntimePolicy);
87
+ return applyBunRuntimePolicy(manifest);
89
88
  }
90
89
  const scripts = { ...toRecord(manifest.scripts) };
91
90
  const changesetsEnabled = isChangesetsEnabled(scripts, changesetsConfigExists);
92
91
  delete scripts.knip;
93
92
  Object.assign(scripts, STANDARD_SCRIPTS);
94
93
  if (changesetsEnabled) {
95
- Object.assign(scripts, changesetsPolicy.packageScripts);
94
+ Object.assign(scripts, REPOSITORY_POLICY.changesets.packageScripts);
96
95
  }
97
96
  const devDependencies = removeOwnedDependencies(toRecord(manifest.devDependencies));
98
97
  const dependencies = toRecord(manifest.dependencies);
99
- delete dependencies[changesetsPolicy.packageName];
100
- delete devDependencies[changesetsPolicy.packageName];
98
+ delete dependencies[REPOSITORY_POLICY.changesets.packageName];
99
+ delete devDependencies[REPOSITORY_POLICY.changesets.packageName];
101
100
  applyDevtoolsDependencyPlacement(dependencies, devDependencies, devtoolsVersion);
102
101
  return applyBunRuntimePolicy({
103
102
  ...manifest,
104
103
  ...normalizedDependencies(manifest, dependencies),
105
104
  scripts,
106
105
  devDependencies,
107
- }, bunRuntimePolicy);
106
+ });
108
107
  }
109
108
  /*** Check whether the Devtools-owned package manifest fields match current policy. */
110
109
  export function isManagedPackageContractCurrent(manifest, devtoolsVersion, changesetsConfigExists = false) {
@@ -120,8 +119,8 @@ export function isManagedPackageContractCurrent(manifest, devtoolsVersion, chang
120
119
  const changesetsEnabled = isChangesetsEnabled(scripts, changesetsConfigExists);
121
120
  return (hasStandardScripts(scripts) &&
122
121
  DEVTOOLS_OWNED_DEV_DEPENDENCIES.every((name) => devDependencies[name] === undefined) &&
123
- dependencies[changesetsPolicy.packageName] === undefined &&
124
- devDependencies[changesetsPolicy.packageName] === undefined &&
122
+ dependencies[REPOSITORY_POLICY.changesets.packageName] === undefined &&
123
+ devDependencies[REPOSITORY_POLICY.changesets.packageName] === undefined &&
125
124
  hasCurrentChangesetsScripts(scripts, changesetsEnabled) &&
126
125
  hasCurrentDevtoolsDependencyPlacement(dependencies, devDependencies, devtoolsVersion));
127
126
  }
@@ -154,8 +153,8 @@ async function readPackageManifest(targetDirectory) {
154
153
  /*** Check the Bun package manager and Bun type dependency contract. */
155
154
  function hasCurrentBunRuntimePolicy(manifest) {
156
155
  const devDependencies = toRecord(manifest.devDependencies);
157
- return (manifest.packageManager === bunRuntimePolicy.packageManager &&
158
- devDependencies[BUN_TYPES_PACKAGE_NAME] === bunRuntimePolicy.typesRange);
156
+ return (manifest.packageManager === REPOSITORY_POLICY.runtime.bun.packageManager &&
157
+ devDependencies[BUN_TYPES_PACKAGE_NAME] === REPOSITORY_POLICY.runtime.bun.typesRange);
159
158
  }
160
159
  /*** Place Devtools in development dependencies for every consuming repository. */
161
160
  function applyDevtoolsDependencyPlacement(dependencies, devDependencies, devtoolsVersion) {
@@ -188,12 +187,12 @@ function hasStandardScripts(scripts) {
188
187
  /*** Check Changesets scripts when the repository participates in Changesets. */
189
188
  function hasCurrentChangesetsScripts(scripts, changesetsEnabled) {
190
189
  return (!changesetsEnabled ||
191
- Object.entries(changesetsPolicy.packageScripts).every(([name, command]) => scripts[name] === command));
190
+ Object.entries(REPOSITORY_POLICY.changesets.packageScripts).every(([name, command]) => scripts[name] === command));
192
191
  }
193
192
  /*** Determine whether the repository participates in Changesets synchronization. */
194
193
  function isChangesetsEnabled(scripts, changesetsConfigExists) {
195
194
  return (changesetsConfigExists ||
196
- Object.keys(changesetsPolicy.packageScripts).some((scriptName) => scripts[scriptName] !== undefined));
195
+ Object.keys(REPOSITORY_POLICY.changesets.packageScripts).some((scriptName) => scripts[scriptName] !== undefined));
197
196
  }
198
197
  /*** Serialize a package manifest using the repository formatting contract. */
199
198
  function serializePackageManifest(manifest) {
@@ -52,16 +52,22 @@ exceptions or replaced by generic preferences from this skill.
52
52
  - Use ordinary inline comments when they explain non-obvious intent, invariants, constraints, or the
53
53
  reason behind a decision. Do not narrate self-explanatory code or duplicate what names and types
54
54
  already express.
55
- - Paradox documentation metadata belongs only inside `/*** ... */` comments. Use only supported
56
- Paradox tags: `@readme`, `@config`, `@example`, and `@usage`. Do not use JSDoc-only tags such as
57
- `@param` or `@returns` as Paradox metadata.
55
+ - Paradox documentation metadata belongs only inside `/*** ... */` comments. The canonical tag
56
+ vocabulary is owned by `@ankhorage/policy`: `@readme`, `@usage`, `@config`, `@title`,
57
+ `@see`, and `@security`. Unsupported tag-shaped lines are invalid; `@example` does not exist.
58
+ Do not use JSDoc-only tags such as `@param` or `@returns` as Paradox metadata.
59
+ - Paradox comments contain documentation prose and metadata only. Fenced or indented code blocks are
60
+ invalid; source examples come from real code. Inline code spans remain valid prose.
58
61
  - Add a supported Paradox tag only when it changes or usefully enriches generated documentation;
59
62
  plain function descriptions do not need tags.
60
63
  - README usage documentation must come from a real repository-root `examples/<example>/...` source
61
- file. Put `@usage` in that example file's leading `/*** ... */` comment so Paradox promotes the
62
- runnable example into the README Usage section. Do not create dedicated `readme-usage`,
63
- `usage-readme`, `readmeUsage`, or equivalent source modules whose only purpose is feeding README
64
- usage text.
64
+ file. A package that opts into `@usage` must have exactly one example combining `@usage`,
65
+ `@readme`, and `@title`; additional `@usage` examples remain full-doc-only. Do not create
66
+ dedicated `readme-usage`, `usage-readme`, `readmeUsage`, or equivalent source modules whose
67
+ only purpose is feeding README usage text.
68
+ - Configuration documentation is optional until a package opts in. Once it does, the canonical schema
69
+ is `src/types/config.ts` with exactly one root type/interface carrying `@config`, `@readme`,
70
+ and `@title`.
65
71
  - Update documentation sources in the pull request, including repository-owned manual documentation
66
72
  outside the configured generated output. Never hand-edit generated README or Paradox artifacts.
67
73
  - Generated documentation is release-owned. Ordinary feature pull requests must not regenerate or
@@ -1,8 +1,8 @@
1
1
  import { access } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
3
4
  import { resolveApmReleaseCommandAsync } from '../../features/apm-release-validation/adapters/outbound/resolveApmReleaseCommandAsync.js';
4
5
  import { resolveStructureReleaseCommandAsync } from '../../features/structure-descriptor-generation/adapters/outbound/resolveStructureReleaseCommandAsync.js';
5
- import { bunRuntimePolicy, nodeRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
6
6
  import { resolvePkgvizAuditPolicyAsync } from '../../policy/resolvePkgvizAuditPolicyAsync.js';
7
7
  import { readCurrentDoctorVersion } from './readCurrentDoctorVersion.js';
8
8
  import { renderRenovateConfigAsync } from './renderRenovateConfigAsync.js';
@@ -38,9 +38,9 @@ function createWorkflowDefinition(relativePath, sourcePath) {
38
38
  render: async (targetDirectory) => await renderWorkflowAsync(sourceUrl, {
39
39
  apmReleaseCommand: await resolveApmReleaseCommandAsync(targetDirectory),
40
40
  structureReleaseCommand: await resolveStructureReleaseCommandAsync(targetDirectory),
41
- bunVersion: bunRuntimePolicy.version,
41
+ bunVersion: REPOSITORY_POLICY.runtime.bun.version,
42
42
  doctorVersion: readCurrentDoctorVersion(),
43
- nodeVersion: nodeRuntimePolicy.setupVersion,
43
+ nodeVersion: REPOSITORY_POLICY.runtime.node.setupVersion,
44
44
  pkgvizAudit: await resolvePkgvizAuditPolicyAsync(targetDirectory),
45
45
  }),
46
46
  };
@@ -53,9 +53,9 @@ function createRenovateWorkflowDefinition() {
53
53
  render: async (targetDirectory) => await renderRenovateWorkflowAsync(sourceUrl, targetDirectory, {
54
54
  apmReleaseCommand: await resolveApmReleaseCommandAsync(targetDirectory),
55
55
  structureReleaseCommand: await resolveStructureReleaseCommandAsync(targetDirectory),
56
- bunVersion: bunRuntimePolicy.version,
56
+ bunVersion: REPOSITORY_POLICY.runtime.bun.version,
57
57
  doctorVersion: readCurrentDoctorVersion(),
58
- nodeVersion: nodeRuntimePolicy.setupVersion,
58
+ nodeVersion: REPOSITORY_POLICY.runtime.node.setupVersion,
59
59
  }),
60
60
  };
61
61
  }
@@ -1,14 +1,14 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { changesetsPolicy } from '../../policy/changesetsPolicy.js';
2
+ import { REPOSITORY_POLICY } from '@ankhorage/policy/repository';
3
3
  /*** Renders one workflow template with the canonical runtime, Doctor, and Changesets policy. */
4
4
  export async function renderWorkflowAsync(sourceUrl, policy) {
5
5
  const template = await readFile(sourceUrl, 'utf8');
6
6
  const rendered = template
7
7
  .replaceAll(BUN_VERSION_TOKEN, policy.bunVersion)
8
8
  .replaceAll(PKGVIZ_AUDIT_STEPS_TOKEN, renderPkgvizAuditSteps(policy))
9
- .replaceAll(CHANGESETS_PUBLISH_COMMAND_TOKEN, changesetsPolicy.workflowCommands.publish)
10
- .replaceAll(CHANGESETS_STATUS_COMMAND_TOKEN, changesetsPolicy.workflowCommands.status)
11
- .replaceAll(CHANGESETS_VERSION_COMMAND_TOKEN, changesetsPolicy.workflowCommands.version)
9
+ .replaceAll(CHANGESETS_PUBLISH_COMMAND_TOKEN, REPOSITORY_POLICY.changesets.workflowCommands.publish)
10
+ .replaceAll(CHANGESETS_STATUS_COMMAND_TOKEN, REPOSITORY_POLICY.changesets.workflowCommands.status)
11
+ .replaceAll(CHANGESETS_VERSION_COMMAND_TOKEN, REPOSITORY_POLICY.changesets.workflowCommands.version)
12
12
  .replaceAll(DOCTOR_VERSION_TOKEN, policy.doctorVersion)
13
13
  .replaceAll('__ANKH_APM_RELEASE_COMMAND__', policy.apmReleaseCommand ?? './node_modules/.bin/ankhorage-apm-release')
14
14
  .replaceAll('__ANKH_STRUCTURE_RELEASE_COMMAND__', policy.structureReleaseCommand ?? './node_modules/.bin/ankhorage-structure')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/devtools",
3
- "version": "1.21.3",
4
- "description": "Shared tooling, repository automation, runtime policies, and agent standards for Ankhorage TypeScript projects",
3
+ "version": "2.0.0",
4
+ "description": "Shared tooling, repository automation, and agent standards for Ankhorage TypeScript projects",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/ankhorage/devtools#readme",
7
7
  "bugs": {
@@ -82,10 +82,6 @@
82
82
  "types": "./dist/tools/knip/index.d.ts",
83
83
  "import": "./dist/tools/knip/index.js"
84
84
  },
85
- "./policy": {
86
- "types": "./dist/policy/bunRuntimePolicy.d.ts",
87
- "import": "./dist/policy/bunRuntimePolicy.js"
88
- },
89
85
  "./prettier": {
90
86
  "import": "./dist/tools/prettier/index.cjs",
91
87
  "require": "./dist/tools/prettier/index.cjs",
@@ -146,12 +142,13 @@
146
142
  "prettier": "^3.9.9",
147
143
  "typescript-eslint": "^8.70.1",
148
144
  "@ankhorage/contracts": "^22.8.1",
149
- "typescript": "~6.0.3"
145
+ "typescript": "~6.0.3",
146
+ "@ankhorage/policy": "^0.3.1"
150
147
  },
151
148
  "devDependencies": {
152
149
  "@ankhorage/paradox": "^0.1.26",
153
150
  "@ankhorage/ankh": "^0.10.4",
154
- "@ankhorage/doctor": "0.11.0",
151
+ "@ankhorage/doctor": "0.11.3",
155
152
  "@techstark/opencv-js": "^5.0.0-release.1",
156
153
  "@types/bun": "^1.4.1",
157
154
  "@types/node": "^26.6.2",
@@ -1,17 +0,0 @@
1
- export declare const bunRuntimePolicy: {
2
- readonly packageManager: "bun@1.4.2";
3
- readonly typesRange: "^1.4.1";
4
- readonly version: "1.4.2";
5
- };
6
- /**
7
- * Canonical Node LTS baseline for Node-based Ankhorage tooling and CI execution.
8
- *
9
- * Bun remains the repository package manager/primary command runtime where configured;
10
- * this policy makes Node-based tooling deterministic instead of inheriting the ambient
11
- * Node version from a CI runner image.
12
- */
13
- export declare const nodeRuntimePolicy: {
14
- readonly engineRange: "24.x";
15
- readonly major: 24;
16
- readonly setupVersion: "24";
17
- };
@@ -1,25 +0,0 @@
1
- /**
2
- * Canonical runtime/tooling policies for Ankhorage repositories.
3
- *
4
- * Import these from `@ankhorage/devtools/policy` when another package needs to inspect
5
- * the managed Bun or Node baseline without defining an independent version authority.
6
- */
7
- const BUN_VERSION = '1.4.2';
8
- const BUN_TYPES_VERSION = '1.4.1';
9
- export const bunRuntimePolicy = {
10
- packageManager: `bun@${BUN_VERSION}`,
11
- typesRange: `^${BUN_TYPES_VERSION}`,
12
- version: BUN_VERSION,
13
- };
14
- /**
15
- * Canonical Node LTS baseline for Node-based Ankhorage tooling and CI execution.
16
- *
17
- * Bun remains the repository package manager/primary command runtime where configured;
18
- * this policy makes Node-based tooling deterministic instead of inheriting the ambient
19
- * Node version from a CI runner image.
20
- */
21
- export const nodeRuntimePolicy = {
22
- engineRange: '24.x',
23
- major: 24,
24
- setupVersion: '24',
25
- };
@@ -1,19 +0,0 @@
1
- export declare const changesetsPolicy: {
2
- readonly packageName: "@changesets/cli";
3
- readonly binaryName: "ankhorage-changeset";
4
- readonly packageScripts: {
5
- readonly changeset: "ankhorage-changeset";
6
- readonly 'changeset:status': "ankhorage-changeset status --since=origin/main";
7
- readonly 'version-packages': "ankhorage-changeset version";
8
- };
9
- readonly ownerPackageScripts: {
10
- readonly changeset: "bun src/cli/bin/changeset.ts";
11
- readonly 'changeset:status': "bun src/cli/bin/changeset.ts status --since=origin/main";
12
- readonly 'version-packages': "bun src/cli/bin/changeset.ts version";
13
- };
14
- readonly workflowCommands: {
15
- readonly status: "bun run changeset:status";
16
- readonly version: "bun run version-packages";
17
- readonly publish: "bun run changeset -- publish";
18
- };
19
- };
@@ -1,19 +0,0 @@
1
- export const changesetsPolicy = {
2
- packageName: '@changesets/cli',
3
- binaryName: 'ankhorage-changeset',
4
- packageScripts: {
5
- changeset: 'ankhorage-changeset',
6
- 'changeset:status': 'ankhorage-changeset status --since=origin/main',
7
- 'version-packages': 'ankhorage-changeset version',
8
- },
9
- ownerPackageScripts: {
10
- changeset: 'bun src/cli/bin/changeset.ts',
11
- 'changeset:status': 'bun src/cli/bin/changeset.ts status --since=origin/main',
12
- 'version-packages': 'bun src/cli/bin/changeset.ts version',
13
- },
14
- workflowCommands: {
15
- status: 'bun run changeset:status',
16
- version: 'bun run version-packages',
17
- publish: 'bun run changeset -- publish',
18
- },
19
- };
@@ -1,5 +0,0 @@
1
- export interface BunPolicy {
2
- readonly packageManager: string;
3
- readonly typesRange: string;
4
- readonly version: string;
5
- }
@@ -1 +0,0 @@
1
- export {};