@drkmttr/adhere 0.3.0 → 0.3.1

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
@@ -78,10 +78,11 @@ prints a completion script.
78
78
 
79
79
  `adhere lint` reads the `.ts` files under the working directory, except `.d.ts`,
80
80
  `.test.ts`, and config files. When the working directory contains `agents/`,
81
- `apps/`, or `packages/`, only those trees are read. Paths under
82
- `node_modules/`, `dist/`, `coverage/`, `vendor/`, `e2e/`, `references/`,
83
- `.adhere/`, `.agents/`, `.claude/`, `.direnv/`, `.alchemy/`, or `.vite/` are
84
- skipped. `.tsx` files are not read, and `.gitignore` is not consulted.
81
+ `apps/`, or `packages/`, only those trees are read. Below the working
82
+ directory, anything under `node_modules/`, `dist/`, `coverage/`, `vendor/`,
83
+ `e2e/`, `references/`, `.adhere/`, `.agents/`, `.claude/`, `.direnv/`,
84
+ `.alchemy/`, or `.vite/` is skipped; the directories above it do not count.
85
+ `.tsx` files are not read, and `.gitignore` is not consulted.
85
86
 
86
87
  ### Init
87
88
 
@@ -175,7 +176,8 @@ const Port = Schema.Int.pipe(
175
176
  validation refuses the run with its path in the message. `loadRules(directory)`
176
177
  from the package root does the same load for your own tooling.
177
178
 
178
- Nested `.adhere/` directories are also discovered. A rule in
179
+ Nested `.adhere/` directories are also discovered, except under
180
+ `node_modules/`, `dist/`, and the other skipped directories (above). A rule in
179
181
  `packages/api/.adhere/data/brand-ports.md` has the same id,
180
182
  `data/brand-ports`, but applies only to files under `packages/api/`. Root
181
183
  `.adhere/` rules apply project-wide. If a nested rule has the same id as a root
@@ -306,5 +308,6 @@ before it can be given trusted publishers on npmjs.com. npm checks the
306
308
  workflow that started the run, so every package has two: `release.yaml`, which
307
309
  calls the publish workflow for tag releases, and `publish.yaml`, for manual
308
310
  runs. The publish workflow can be run manually for an already-created release
309
- tag if a publish needs to be retried; it skips platform packages whose version
310
- is already on npm.
311
+ tag if a publish needs to be retried; it skips every package whose version is
312
+ already on npm. The main package's `files` list keeps it to `bin/`, `src/`,
313
+ `presets/`, and `skills/`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drkmttr/adhere",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Lints a repository against reference code in .adhere/, judged by Jev.",
6
6
  "repository": {
@@ -17,6 +17,12 @@
17
17
  "exports": {
18
18
  ".": "./src/index.ts"
19
19
  },
20
+ "files": [
21
+ "bin",
22
+ "src",
23
+ "presets",
24
+ "skills"
25
+ ],
20
26
  "publishConfig": {
21
27
  "access": "public"
22
28
  },
@@ -61,10 +67,10 @@
61
67
  }
62
68
  },
63
69
  "optionalDependencies": {
64
- "@drkmttr/adhere-darwin-arm64": "0.3.0",
65
- "@drkmttr/adhere-darwin-x64": "0.3.0",
66
- "@drkmttr/adhere-linux-arm64": "0.3.0",
67
- "@drkmttr/adhere-linux-x64": "0.3.0",
68
- "@drkmttr/adhere-win32-x64": "0.3.0"
70
+ "@drkmttr/adhere-darwin-arm64": "0.3.1",
71
+ "@drkmttr/adhere-darwin-x64": "0.3.1",
72
+ "@drkmttr/adhere-linux-arm64": "0.3.1",
73
+ "@drkmttr/adhere-linux-x64": "0.3.1",
74
+ "@drkmttr/adhere-win32-x64": "0.3.1"
69
75
  }
70
76
  }
package/src/config.ts CHANGED
@@ -38,6 +38,25 @@ export const CONFIG_FILES = [
38
38
  "adhere.config.ts",
39
39
  ".adhere.config.ts",
40
40
  ] as const;
41
+ /**
42
+ * Directories never read into, for source files or for nested `.adhere/`
43
+ * rules: dependency, generated, vendored, and tool trees. Matched as whole
44
+ * path segments below the working directory, so a repo that itself sits under
45
+ * one of these names is still read.
46
+ */
47
+ export const SKIPPED_DIRECTORIES: ReadonlySet<string> = new Set([
48
+ "node_modules",
49
+ "dist",
50
+ ".agents",
51
+ ".claude",
52
+ ".direnv",
53
+ ".alchemy",
54
+ "coverage",
55
+ ".vite",
56
+ "references",
57
+ "vendor",
58
+ "e2e",
59
+ ]);
41
60
  export interface AdhereConfig extends Schema.Schema.Type<typeof AdhereConfig> {}
42
61
 
43
62
  /** The shape a config file default-exports. */
package/src/rules.ts CHANGED
@@ -1,4 +1,10 @@
1
- import { ConfigUnavailable, type Rule, type RuleId, type RuleSource } from "#config.ts";
1
+ import {
2
+ ConfigUnavailable,
3
+ type Rule,
4
+ type RuleId,
5
+ type RuleSource,
6
+ SKIPPED_DIRECTORIES,
7
+ } from "#config.ts";
2
8
  import { parseRuleMarkdown } from "#markdown.ts";
3
9
  import { Effect, FileSystem, Path, Record } from "effect";
4
10
 
@@ -31,7 +37,13 @@ const isMarkdown = (path: string): boolean => path.endsWith(".md");
31
37
  const isNestedRuleFile = (relative: string): boolean => {
32
38
  const segments = segmentsOf(relative);
33
39
  const adhere = segments.lastIndexOf(ADHERE_SEGMENT);
34
- return adhere >= 0 && segments[adhere + 1] !== CACHE_SEGMENT && isMarkdown(relative);
40
+ return (
41
+ adhere >= 0 &&
42
+ segments[adhere + 1] !== CACHE_SEGMENT &&
43
+ isMarkdown(relative) &&
44
+ // Only the path to the `.adhere/`: `.adhere/e2e/` is a rule topic, not a skip.
45
+ !segments.slice(0, adhere).some((segment) => SKIPPED_DIRECTORIES.has(segment))
46
+ );
35
47
  };
36
48
 
37
49
  const rulePathOf = (segments: ReadonlyArray<string>): ReadonlyArray<string> => {
@@ -1,4 +1,5 @@
1
1
  import { Context, Effect, FileSystem, Layer, Path } from "effect";
2
+ import { ADHERE_DIRECTORY, SKIPPED_DIRECTORIES } from "#config.ts";
2
3
  import type { ScannedFile } from "#models/Audit.ts";
3
4
  import { WalkUnavailable } from "#models/Audit.ts";
4
5
 
@@ -20,21 +21,15 @@ export class SourceWalker extends Context.Service<
20
21
  * trees. Otherwise read the working directory itself.
21
22
  */
22
23
  const WORKSPACE_DIRS = ["agents", "apps", "packages"] as const;
23
- /** Path fragments that mark generated, vendored, or dependency trees. */
24
- const SKIPS = [
25
- "/node_modules/",
26
- "/.adhere/",
27
- "/dist/",
28
- "/.agents/",
29
- "/.claude/",
30
- "/.direnv/",
31
- "/.alchemy/",
32
- "/coverage/",
33
- "/.vite/",
34
- "/references/",
35
- "/vendor/",
36
- "/e2e/",
37
- ] as const;
24
+ /**
25
+ * A path, relative to the root being read, through a skipped directory or
26
+ * through `.adhere/`, whose files are rules and cache rather than source.
27
+ * Relative, so the directories above the working directory never count.
28
+ */
29
+ export const isInSkippedTree = (relative: string): boolean =>
30
+ relative
31
+ .split(/[\\/]/)
32
+ .some((segment) => segment === ADHERE_DIRECTORY || SKIPPED_DIRECTORIES.has(segment));
38
33
 
39
34
  const isInside = (file: string, root: string): boolean =>
40
35
  file === root || file.startsWith(`${root}/`);
@@ -45,7 +40,6 @@ const isScannable = (file: string, self: string): boolean =>
45
40
  !file.endsWith(".test.ts") &&
46
41
  !file.endsWith("/adhere.config.ts") &&
47
42
  !file.endsWith("/.adhere.config.ts") &&
48
- SKIPS.every((skip) => !file.includes(skip)) &&
49
43
  // The audit's own detector patterns are data, not violations of themselves.
50
44
  !isInside(file, self);
51
45
 
@@ -57,7 +51,10 @@ const rooted = (root: string, paths: ReadonlyArray<string>) => paths.map((rel) =
57
51
 
58
52
  /** What the root actually contains: only paths the audit reads. */
59
53
  const scannable = (root: string, paths: ReadonlyArray<string>, self: string) =>
60
- rooted(root, paths).filter((file) => isScannable(file, self));
54
+ rooted(
55
+ root,
56
+ paths.filter((relative) => !isInSkippedTree(relative)),
57
+ ).filter((file) => isScannable(file, self));
61
58
 
62
59
  /** One root's paths, filtered to what the audit reads. */
63
60
  const listRoot = Effect.fn("SourceWalker.listRoot")(
package/.adhere/config.ts DELETED
@@ -1,7 +0,0 @@
1
- import type { Config } from "../src/index.ts";
2
-
3
- // Type-only import: erased at runtime, so the compiled binary loads this file
4
- // without resolving the package (see README, "Configuration").
5
- export default {
6
- presets: ["effect"],
7
- } satisfies Config;
@@ -1,17 +0,0 @@
1
- ---
2
- description: An error raised while reading a config, rule, or cache file names that file in its message, so the user can open it.
3
- threshold: 0.8
4
- ---
5
-
6
- Adhere's own rule. A refusal is the only thing the user sees, so it points
7
- at the file it is about.
8
-
9
- ```ts
10
- const front =
11
- yield *
12
- Schema.decodeUnknownEffect(FrontMatter)(fields).pipe(
13
- Effect.mapError((problem) =>
14
- ConfigUnavailable.make({ message: `${file}: ${problem.message}` }),
15
- ),
16
- );
17
- ```
@@ -1,27 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- push:
6
- branches: [main]
7
-
8
- jobs:
9
- verify:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v4
13
- - uses: oven-sh/setup-bun@v2
14
- with:
15
- bun-version: "1.4.2"
16
- - run: bun install --frozen-lockfile
17
- - run: bun run typecheck
18
- - run: bun run test
19
- - run: bun run build:npm
20
- - name: Run the launcher against the linux-x64 package
21
- run: |
22
- # Outside the checkout, where the launcher has no source to fall back to.
23
- launcher="$RUNNER_TEMP/launcher"
24
- mkdir -p "$launcher/node_modules/@drkmttr"
25
- cp -r bin "$launcher/bin"
26
- ln -s "$PWD/dist/npm/linux-x64" "$launcher/node_modules/@drkmttr/adhere-linux-x64"
27
- node "$launcher/bin/adhere.js" --version
@@ -1,68 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- tag_name:
7
- description: "Release tag to publish, for example v0.3.0."
8
- required: true
9
- type: string
10
- workflow_dispatch:
11
- inputs:
12
- tag_name:
13
- description: "Release tag to publish, for example v0.3.0."
14
- required: true
15
- type: string
16
-
17
- permissions:
18
- id-token: write
19
- contents: read
20
-
21
- jobs:
22
- publish:
23
- runs-on: ubuntu-latest
24
- steps:
25
- - uses: actions/checkout@v4
26
- with:
27
- ref: main
28
- - uses: oven-sh/setup-bun@v2
29
- with:
30
- bun-version: "1.4.2"
31
- - uses: actions/setup-node@v4
32
- with:
33
- node-version: "24"
34
- registry-url: "https://registry.npmjs.org"
35
- - name: Verify release tag matches package version
36
- env:
37
- TAG_NAME: ${{ inputs.tag_name }}
38
- run: |
39
- version="${TAG_NAME#v}"
40
- if [[ "$version" == "$TAG_NAME" || ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then
41
- echo "Release tags must look like vX.Y.Z, optionally with a prerelease/build suffix." >&2
42
- exit 1
43
- fi
44
- package_version="$(node -p "require('./package.json').version")"
45
- if [ "$version" != "$package_version" ]; then
46
- echo "Release tag $TAG_NAME does not match package.json version $package_version." >&2
47
- exit 1
48
- fi
49
- - run: bun install --frozen-lockfile
50
- - run: bun run typecheck
51
- - run: bun run test
52
- - run: bun run build:npm
53
- - name: Publish the platform packages
54
- run: |
55
- for directory in dist/npm/*/; do
56
- name="$(node -p "require('./${directory}package.json').name")"
57
- version="$(node -p "require('./${directory}package.json').version")"
58
- # A rerun after a partial publish skips the versions already on npm.
59
- if npm view "$name@$version" version >/dev/null 2>&1; then
60
- echo "$name@$version is already published."
61
- else
62
- npm publish "$directory" --access public --provenance
63
- fi
64
- done
65
- - name: Publish @drkmttr/adhere with the platform packages as optional dependencies
66
- run: |
67
- bun scripts/npm-packages.ts pin
68
- npm publish --access public --provenance
@@ -1,71 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
-
8
- permissions:
9
- contents: write
10
-
11
- jobs:
12
- release:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Derive version from tag
16
- id: release
17
- shell: bash
18
- run: |
19
- version="${GITHUB_REF_NAME#v}"
20
- if [[ "$version" == "$GITHUB_REF_NAME" || ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then
21
- echo "Release tags must look like vX.Y.Z, optionally with a prerelease/build suffix." >&2
22
- exit 1
23
- fi
24
- echo "version=$version" >> "$GITHUB_OUTPUT"
25
-
26
- - uses: actions/checkout@v4
27
- with:
28
- ref: main
29
- fetch-depth: 0
30
-
31
- - name: Ensure tag was cut from current main
32
- shell: bash
33
- run: |
34
- tag_sha="$(git rev-list -n 1 "$GITHUB_REF_NAME")"
35
- main_sha="$(git rev-parse HEAD)"
36
- if [[ "$tag_sha" != "$main_sha" ]]; then
37
- echo "Tag $GITHUB_REF_NAME must point at current main before release-it adds the version bump commit." >&2
38
- echo "tag=$tag_sha main=$main_sha" >&2
39
- exit 1
40
- fi
41
-
42
- - uses: oven-sh/setup-bun@v2
43
- with:
44
- bun-version: "1.4.2"
45
-
46
- - uses: actions/setup-node@v4
47
- with:
48
- node-version: "24"
49
-
50
- - name: Configure git author
51
- run: |
52
- git config user.name "github-actions[bot]"
53
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
54
-
55
- - run: bun install --frozen-lockfile
56
- - run: bun run typecheck
57
- - run: bun run test
58
- - run: bun run build:npm
59
- - name: Create GitHub release
60
- run: bun run release -- --ci "${{ steps.release.outputs.version }}"
61
- env:
62
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63
-
64
- publish:
65
- needs: release
66
- uses: ./.github/workflows/publish.yaml
67
- permissions:
68
- contents: read
69
- id-token: write
70
- with:
71
- tag_name: ${{ github.ref_name }}