@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 +10 -7
- package/package.json +12 -6
- package/src/config.ts +19 -0
- package/src/rules.ts +14 -2
- package/src/services/SourceWalker.ts +14 -17
- package/.adhere/config.ts +0 -7
- package/.adhere/rules/refusals-name-the-file.md +0 -17
- package/.github/workflows/ci.yaml +0 -27
- package/.github/workflows/publish.yaml +0 -68
- package/.github/workflows/release.yaml +0 -71
- package/bun.lock +0 -773
- package/scripts/npm-packages.ts +0 -68
- package/test/audit.test.ts +0 -540
- package/test/cli.test.ts +0 -45
- package/tsconfig.json +0 -26
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.
|
|
82
|
-
`node_modules/`, `dist/`, `coverage/`, `vendor/`,
|
|
83
|
-
`.adhere/`, `.agents/`, `.claude/`, `.direnv/`,
|
|
84
|
-
|
|
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
|
|
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
|
|
310
|
-
|
|
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.
|
|
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.
|
|
65
|
-
"@drkmttr/adhere-darwin-x64": "0.3.
|
|
66
|
-
"@drkmttr/adhere-linux-arm64": "0.3.
|
|
67
|
-
"@drkmttr/adhere-linux-x64": "0.3.
|
|
68
|
-
"@drkmttr/adhere-win32-x64": "0.3.
|
|
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 {
|
|
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
|
|
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
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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(
|
|
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,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 }}
|