@gobing-ai/ts-rule-engine 0.2.9 → 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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Resolve the absolute path to the rule presets bundled with
3
+ * `@gobing-ai/ts-rule-engine`.
4
+ *
5
+ * The directory ships portable presets (`recommended`, `spur-dev`) and category
6
+ * folders (`typescript`, `structure`, `quality`) so a consumer gets a working
7
+ * default ruleset without authoring any files. Pass the returned path as the
8
+ * lowest-priority entry to {@link loadPreset}'s `roots`, letting project-local
9
+ * and user-global roots shadow individual files while inheriting the rest.
10
+ *
11
+ * Resolution walks up from this module's compiled location (under `dist/` at
12
+ * runtime, under `src/` in tests) until it finds the bundled `rules/` directory,
13
+ * which makes it robust to the build's `src`→`dist` layout shift. Returns `null`
14
+ * if the directory is absent (e.g. a partial install that excluded the assets).
15
+ */
16
+ export declare function bundledRulesRoot(): string | null;
17
+ /**
18
+ * List the relative paths of every bundled rule asset (presets and category rule
19
+ * files), each as a `/`-joined path relative to {@link bundledRulesRoot}.
20
+ *
21
+ * Intended for consumers that copy the bundled rules into a writable location
22
+ * (e.g. a per-user global rules directory) on first run. Returns an empty array
23
+ * when no bundled directory is present.
24
+ */
25
+ export declare function listBundledRuleFiles(): string[];
26
+ //# sourceMappingURL=bundled-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled-rules.d.ts","sourceRoot":"","sources":["../../src/host/bundled-rules.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAoBD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAI/C"}
@@ -0,0 +1,76 @@
1
+ import { dirnamePath, joinPath, NodeSyncFileSystem } from '@gobing-ai/ts-runtime';
2
+ /**
3
+ * Directory name holding the rule presets and category folders bundled with this
4
+ * package. Lives at the package root (sibling to `dist/`), shipped via the
5
+ * package `files` allowlist.
6
+ */
7
+ const BUNDLED_RULES_DIR = 'rules';
8
+ /** Memoized result so the upward filesystem walk runs at most once per process. */
9
+ let cachedRoot;
10
+ const defaultFs = new NodeSyncFileSystem();
11
+ /**
12
+ * Resolve the absolute path to the rule presets bundled with
13
+ * `@gobing-ai/ts-rule-engine`.
14
+ *
15
+ * The directory ships portable presets (`recommended`, `spur-dev`) and category
16
+ * folders (`typescript`, `structure`, `quality`) so a consumer gets a working
17
+ * default ruleset without authoring any files. Pass the returned path as the
18
+ * lowest-priority entry to {@link loadPreset}'s `roots`, letting project-local
19
+ * and user-global roots shadow individual files while inheriting the rest.
20
+ *
21
+ * Resolution walks up from this module's compiled location (under `dist/` at
22
+ * runtime, under `src/` in tests) until it finds the bundled `rules/` directory,
23
+ * which makes it robust to the build's `src`→`dist` layout shift. Returns `null`
24
+ * if the directory is absent (e.g. a partial install that excluded the assets).
25
+ */
26
+ export function bundledRulesRoot() {
27
+ return bundledRulesRootWithFs(defaultFs);
28
+ }
29
+ function bundledRulesRootWithFs(fs) {
30
+ if (cachedRoot !== undefined)
31
+ return cachedRoot;
32
+ let dir = import.meta.dirname;
33
+ // Walk to filesystem root at most; the package root is only a few levels up.
34
+ while (true) {
35
+ const candidate = joinPath(dir, BUNDLED_RULES_DIR);
36
+ if (fs.stat(candidate)?.isDirectory() === true) {
37
+ cachedRoot = candidate;
38
+ return cachedRoot;
39
+ }
40
+ const parent = dirnamePath(dir);
41
+ if (parent === dir)
42
+ break;
43
+ dir = parent;
44
+ }
45
+ cachedRoot = null;
46
+ return cachedRoot;
47
+ }
48
+ /**
49
+ * List the relative paths of every bundled rule asset (presets and category rule
50
+ * files), each as a `/`-joined path relative to {@link bundledRulesRoot}.
51
+ *
52
+ * Intended for consumers that copy the bundled rules into a writable location
53
+ * (e.g. a per-user global rules directory) on first run. Returns an empty array
54
+ * when no bundled directory is present.
55
+ */
56
+ export function listBundledRuleFiles() {
57
+ const root = bundledRulesRoot();
58
+ if (root === null)
59
+ return [];
60
+ return walk(defaultFs, root, '').sort();
61
+ }
62
+ /** Recursively collect YAML/JSON files under `dir`, returning paths relative to the walk origin. */
63
+ function walk(fs, dir, relPrefix) {
64
+ const acc = [];
65
+ for (const entry of fs.readDir(dir)) {
66
+ const abs = joinPath(dir, entry);
67
+ const rel = relPrefix.length > 0 ? `${relPrefix}/${entry}` : entry;
68
+ if (fs.stat(abs)?.isDirectory() === true) {
69
+ acc.push(...walk(fs, abs, rel));
70
+ }
71
+ else if (/\.(ya?ml|json)$/i.test(entry)) {
72
+ acc.push(rel);
73
+ }
74
+ }
75
+ return acc;
76
+ }
@@ -1,24 +1,10 @@
1
- /** Registry origin for a host capability. */
2
- export type CapabilityOrigin = 'builtin' | 'extension';
3
- /** Registry entry metadata. */
4
- export interface CapabilityEntry<TCapability> {
5
- /** Capability implementation. */
6
- capability: TCapability;
7
- /** Registration origin. */
8
- origin: CapabilityOrigin;
9
- }
10
- /** Typed registry used by the rule engine host. */
11
- export declare class CapabilityRegistry<TCapability> {
12
- private readonly kind;
13
- private readonly capabilities;
14
- constructor(kind: string);
15
- /** Register or replace a capability. */
16
- register(name: string, capability: TCapability, origin?: CapabilityOrigin): void;
17
- /** Return true when a capability exists. */
18
- has(name: string): boolean;
19
- /** Get a registered capability or throw a clear error. */
20
- get(name: string): TCapability;
21
- /** List registered capability names. */
22
- list(): string[];
23
- }
1
+ /**
2
+ * Compatibility re-export (ADR-010 / task 0008).
3
+ *
4
+ * The capability registry now lives in the shared plugin core
5
+ * (`@gobing-ai/ts-runtime/plugin`). This module re-exports it from the original
6
+ * path so the public barrel and existing importers keep working unchanged. New
7
+ * code should import from `@gobing-ai/ts-runtime/plugin` directly.
8
+ */
9
+ export { type CapabilityEntry, type CapabilityOrigin, CapabilityRegistry } from '@gobing-ai/ts-runtime/plugin';
24
10
  //# sourceMappingURL=capability-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"capability-registry.d.ts","sourceRoot":"","sources":["../../src/host/capability-registry.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,CAAC;AAEvD,+BAA+B;AAC/B,MAAM,WAAW,eAAe,CAAC,WAAW;IACxC,iCAAiC;IACjC,UAAU,EAAE,WAAW,CAAC;IACxB,2BAA2B;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED,mDAAmD;AACnD,qBAAa,kBAAkB,CAAC,WAAW;IAG3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmD;gBAEnD,IAAI,EAAE,MAAM;IAEzC,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAE,gBAA8B,GAAG,IAAI;IAI7F,4CAA4C;IAC5C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,0DAA0D;IAC1D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAQ9B,wCAAwC;IACxC,IAAI,IAAI,MAAM,EAAE;CAGnB"}
1
+ {"version":3,"file":"capability-registry.d.ts","sourceRoot":"","sources":["../../src/host/capability-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,28 +1,9 @@
1
- /** Typed registry used by the rule engine host. */
2
- export class CapabilityRegistry {
3
- kind;
4
- capabilities = new Map();
5
- constructor(kind) {
6
- this.kind = kind;
7
- }
8
- /** Register or replace a capability. */
9
- register(name, capability, origin = 'extension') {
10
- this.capabilities.set(name, { capability, origin });
11
- }
12
- /** Return true when a capability exists. */
13
- has(name) {
14
- return this.capabilities.has(name);
15
- }
16
- /** Get a registered capability or throw a clear error. */
17
- get(name) {
18
- const entry = this.capabilities.get(name);
19
- if (entry === undefined) {
20
- throw new Error(`Unknown ${this.kind}: ${name}`);
21
- }
22
- return entry.capability;
23
- }
24
- /** List registered capability names. */
25
- list() {
26
- return [...this.capabilities.keys()];
27
- }
28
- }
1
+ /**
2
+ * Compatibility re-export (ADR-010 / task 0008).
3
+ *
4
+ * The capability registry now lives in the shared plugin core
5
+ * (`@gobing-ai/ts-runtime/plugin`). This module re-exports it from the original
6
+ * path so the public barrel and existing importers keep working unchanged. New
7
+ * code should import from `@gobing-ai/ts-runtime/plugin` directly.
8
+ */
9
+ export { CapabilityRegistry } from '@gobing-ai/ts-runtime/plugin';
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './fixers/fixers';
5
5
  export * from './fixers/test-stub-fixer';
6
6
  export * from './formatters/json';
7
7
  export * from './formatters/text';
8
+ export * from './host/bundled-rules';
8
9
  export * from './host/capability-registry';
9
10
  export * from './host/rule-engine-host';
10
11
  export * from './resolvers/test-path-resolver';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './fixers/fixers.js';
5
5
  export * from './fixers/test-stub-fixer.js';
6
6
  export * from './formatters/json.js';
7
7
  export * from './formatters/text.js';
8
+ export * from './host/bundled-rules.js';
8
9
  export * from './host/capability-registry.js';
9
10
  export * from './host/rule-engine-host.js';
10
11
  export * from './resolvers/test-path-resolver.js';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { assertRelativeExtensionPath } from '@gobing-ai/ts-runtime/plugin';
1
2
  import { z } from 'zod';
2
3
  /** Create a finding with inherited rule severity. */
3
4
  export function createFinding(rule, message, filePath, extras = {}) {
@@ -39,16 +40,23 @@ export const ConstraintRuleSchema = z.object({
39
40
  *
40
41
  * Rejects absolute paths and `..` traversal: extension declarations are data, and a
41
42
  * path that escapes the declaring file's directory is a trust-boundary violation even
42
- * when extension loading is explicitly allowed.
43
+ * when extension loading is explicitly allowed. The actual guard is the shared
44
+ * `assertRelativeExtensionPath` (ADR-010) so schema-time validation here and load-time
45
+ * validation in the shared loader use one source of truth.
43
46
  */
44
47
  const relativeExtensionPath = z
45
48
  .string()
46
49
  .min(1)
47
- .refine((value) => !/^([/\\]|[A-Za-z]:[/\\])/.test(value), {
48
- message: 'extension path must be relative (no absolute paths)',
49
- })
50
- .refine((value) => !value.split(/[/\\]/).includes('..'), {
51
- message: 'extension path must not contain ".." traversal',
50
+ .superRefine((value, ctx) => {
51
+ try {
52
+ assertRelativeExtensionPath(value);
53
+ }
54
+ catch (error) {
55
+ ctx.addIssue({
56
+ code: 'custom',
57
+ message: error instanceof Error ? error.message : 'invalid extension path',
58
+ });
59
+ }
52
60
  });
53
61
  /**
54
62
  * Shared zod schema for an `extensions` block, used by both preset and rule-file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-rule-engine",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "@gobing-ai/ts-rule-engine — Constraint rule schemas, loading, evaluation, and result formatting.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -32,6 +32,7 @@
32
32
  },
33
33
  "files": [
34
34
  "dist",
35
+ "rules",
35
36
  "schemas",
36
37
  "src",
37
38
  "README.md"
@@ -48,10 +49,10 @@
48
49
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-rule-engine-v<version> && git push --tags' && exit 1"
49
50
  },
50
51
  "dependencies": {
51
- "@gobing-ai/ts-ai-runner": "^0.2.9",
52
- "@gobing-ai/ts-db": "^0.2.9",
53
- "@gobing-ai/ts-runtime": "^0.2.9",
54
- "@gobing-ai/ts-utils": "^0.2.9",
52
+ "@gobing-ai/ts-ai-runner": "^0.3.1",
53
+ "@gobing-ai/ts-db": "^0.3.1",
54
+ "@gobing-ai/ts-runtime": "^0.3.1",
55
+ "@gobing-ai/ts-utils": "^0.3.1",
55
56
  "yaml": "^2.7.0",
56
57
  "zod": "^4.1.0"
57
58
  },
@@ -0,0 +1,21 @@
1
+ # Coverage gate — per-file line coverage must meet threshold.
2
+ rules:
3
+ - id: coverage-gate
4
+ description: "Per-file line coverage meets threshold (read from lcov)"
5
+ severity: error
6
+ evaluator:
7
+ type: coverage-gate
8
+ config:
9
+ lcovPath: coverage/lcov.info
10
+ threshold: 90
11
+ include:
12
+ - "apps/**"
13
+ - "packages/**"
14
+ exclude:
15
+ - "**/*.test.ts"
16
+ - "**/*.test.tsx"
17
+ - "**/*.spec.ts"
18
+ - "**/*.spec.tsx"
19
+ - "**/node_modules/**"
20
+ - "**/drizzle/**"
21
+ - "scripts/**"
@@ -0,0 +1,24 @@
1
+ # TSDoc export rules — every public export must carry a JSDoc comment.
2
+ rules:
3
+ - id: every-export-has-tsdoc
4
+ description: "Every export (function, class, interface, type, const, enum) must have a /** ... */ JSDoc comment describing what it is and why."
5
+ severity: error
6
+ evaluator:
7
+ type: tsdoc-export
8
+ config:
9
+ kinds:
10
+ - function
11
+ - class
12
+ - interface
13
+ - type
14
+ - const
15
+ - enum
16
+ include:
17
+ - "packages/**/src/**/*.ts"
18
+ - "apps/**/src/**/*.ts"
19
+ exclude:
20
+ - "**/index.ts"
21
+ - "**/node_modules/**"
22
+ - "**/tests/**"
23
+ - "**/*.test.ts"
24
+ - "**/*.test.tsx"
@@ -0,0 +1,10 @@
1
+ # Recommended preset — portable rule categories for TypeScript projects.
2
+ # Use with: spur rule run --preset recommended
3
+ #
4
+ # Bundled with @gobing-ai/ts-rule-engine. A project may override individual rule
5
+ # files by placing same-named files under its local .spur/rules/ root.
6
+ name: recommended
7
+ extends:
8
+ - typescript
9
+ - structure
10
+ - quality
@@ -0,0 +1,6 @@
1
+ # Development preset — stricter rules for development workflow.
2
+ # Use with: spur rule run --preset spur-dev --rule coverage-gate --fail-on warning
3
+ name: spur-dev
4
+ extends:
5
+ - typescript
6
+ - quality
@@ -0,0 +1,38 @@
1
+ # Test location rules — tests must live in tests/ directories mirroring src/.
2
+ # No __tests__ directories, no .test.ts files under src/.
3
+ rules:
4
+ - id: no-tests-dir
5
+ description: "Tests must live in tests/ directories at the same level as src/, not in __tests__."
6
+ severity: error
7
+ evaluator:
8
+ type: test-location
9
+ config:
10
+ expected: "**/*.test.*"
11
+ forbid:
12
+ - "**/__tests__/**"
13
+ - "**/src/**/*.test.ts"
14
+ - "**/src/**/*.test.tsx"
15
+ requireCorrespondingTest: false
16
+ include:
17
+ - "packages/**/tests/**/*.test.ts"
18
+ - "apps/**/tests/**/*.test.ts"
19
+ - "packages/**/tests/**/*.test.tsx"
20
+ - "apps/**/tests/**/*.test.tsx"
21
+
22
+ - id: require-corresponding-test
23
+ description: "Every source file must have a corresponding test file in tests/."
24
+ severity: warning
25
+ evaluator:
26
+ type: test-location
27
+ config:
28
+ expected: "**/*.test.*"
29
+ requireCorrespondingTest: true
30
+ include:
31
+ - "packages/**/src/**/*.ts"
32
+ - "apps/**/src/**/*.ts"
33
+ exclude:
34
+ - "**/index.ts"
35
+ - "**/*.d.ts"
36
+ - "**/types.ts"
37
+ - "**/node_modules/**"
38
+ - "**/tests/**"
@@ -0,0 +1,23 @@
1
+ # No Biome suppression comments — disallow biome-ignore comments.
2
+ # Lint issues must be fixed at the source, not silenced.
3
+ include:
4
+ - "packages/**/src/**/*.ts"
5
+ - "packages/**/src/**/*.tsx"
6
+ - "apps/**/src/**/*.ts"
7
+ - "apps/**/src/**/*.tsx"
8
+ - "packages/**/tests/**/*.ts"
9
+ - "packages/**/tests/**/*.tsx"
10
+ - "apps/**/tests/**/*.ts"
11
+ - "apps/**/tests/**/*.tsx"
12
+ exclude:
13
+ - "**/node_modules/**"
14
+ rules:
15
+ - id: no-biome-suppressions
16
+ description: >
17
+ Do not use biome-ignore or biome-ignore-all comments to suppress lint
18
+ rules. Fix the underlying issue instead.
19
+ severity: error
20
+ evaluator:
21
+ type: regex
22
+ config:
23
+ pattern: "biome-ignore"
@@ -0,0 +1,78 @@
1
+ import { dirnamePath, joinPath, NodeSyncFileSystem, type SyncFileSystem } from '@gobing-ai/ts-runtime';
2
+
3
+ /**
4
+ * Directory name holding the rule presets and category folders bundled with this
5
+ * package. Lives at the package root (sibling to `dist/`), shipped via the
6
+ * package `files` allowlist.
7
+ */
8
+ const BUNDLED_RULES_DIR = 'rules';
9
+
10
+ /** Memoized result so the upward filesystem walk runs at most once per process. */
11
+ let cachedRoot: string | null | undefined;
12
+ const defaultFs = new NodeSyncFileSystem();
13
+
14
+ /**
15
+ * Resolve the absolute path to the rule presets bundled with
16
+ * `@gobing-ai/ts-rule-engine`.
17
+ *
18
+ * The directory ships portable presets (`recommended`, `spur-dev`) and category
19
+ * folders (`typescript`, `structure`, `quality`) so a consumer gets a working
20
+ * default ruleset without authoring any files. Pass the returned path as the
21
+ * lowest-priority entry to {@link loadPreset}'s `roots`, letting project-local
22
+ * and user-global roots shadow individual files while inheriting the rest.
23
+ *
24
+ * Resolution walks up from this module's compiled location (under `dist/` at
25
+ * runtime, under `src/` in tests) until it finds the bundled `rules/` directory,
26
+ * which makes it robust to the build's `src`→`dist` layout shift. Returns `null`
27
+ * if the directory is absent (e.g. a partial install that excluded the assets).
28
+ */
29
+ export function bundledRulesRoot(): string | null {
30
+ return bundledRulesRootWithFs(defaultFs);
31
+ }
32
+
33
+ function bundledRulesRootWithFs(fs: SyncFileSystem): string | null {
34
+ if (cachedRoot !== undefined) return cachedRoot;
35
+ let dir = import.meta.dirname;
36
+ // Walk to filesystem root at most; the package root is only a few levels up.
37
+ while (true) {
38
+ const candidate = joinPath(dir, BUNDLED_RULES_DIR);
39
+ if (fs.stat(candidate)?.isDirectory() === true) {
40
+ cachedRoot = candidate;
41
+ return cachedRoot;
42
+ }
43
+ const parent = dirnamePath(dir);
44
+ if (parent === dir) break;
45
+ dir = parent;
46
+ }
47
+ cachedRoot = null;
48
+ return cachedRoot;
49
+ }
50
+
51
+ /**
52
+ * List the relative paths of every bundled rule asset (presets and category rule
53
+ * files), each as a `/`-joined path relative to {@link bundledRulesRoot}.
54
+ *
55
+ * Intended for consumers that copy the bundled rules into a writable location
56
+ * (e.g. a per-user global rules directory) on first run. Returns an empty array
57
+ * when no bundled directory is present.
58
+ */
59
+ export function listBundledRuleFiles(): string[] {
60
+ const root = bundledRulesRoot();
61
+ if (root === null) return [];
62
+ return walk(defaultFs, root, '').sort();
63
+ }
64
+
65
+ /** Recursively collect YAML/JSON files under `dir`, returning paths relative to the walk origin. */
66
+ function walk(fs: SyncFileSystem, dir: string, relPrefix: string): string[] {
67
+ const acc: string[] = [];
68
+ for (const entry of fs.readDir(dir)) {
69
+ const abs = joinPath(dir, entry);
70
+ const rel = relPrefix.length > 0 ? `${relPrefix}/${entry}` : entry;
71
+ if (fs.stat(abs)?.isDirectory() === true) {
72
+ acc.push(...walk(fs, abs, rel));
73
+ } else if (/\.(ya?ml|json)$/i.test(entry)) {
74
+ acc.push(rel);
75
+ }
76
+ }
77
+ return acc;
78
+ }
@@ -1,41 +1,9 @@
1
- /** Registry origin for a host capability. */
2
- export type CapabilityOrigin = 'builtin' | 'extension';
3
-
4
- /** Registry entry metadata. */
5
- export interface CapabilityEntry<TCapability> {
6
- /** Capability implementation. */
7
- capability: TCapability;
8
- /** Registration origin. */
9
- origin: CapabilityOrigin;
10
- }
11
-
12
- /** Typed registry used by the rule engine host. */
13
- export class CapabilityRegistry<TCapability> {
14
- private readonly capabilities = new Map<string, CapabilityEntry<TCapability>>();
15
-
16
- constructor(private readonly kind: string) {}
17
-
18
- /** Register or replace a capability. */
19
- register(name: string, capability: TCapability, origin: CapabilityOrigin = 'extension'): void {
20
- this.capabilities.set(name, { capability, origin });
21
- }
22
-
23
- /** Return true when a capability exists. */
24
- has(name: string): boolean {
25
- return this.capabilities.has(name);
26
- }
27
-
28
- /** Get a registered capability or throw a clear error. */
29
- get(name: string): TCapability {
30
- const entry = this.capabilities.get(name);
31
- if (entry === undefined) {
32
- throw new Error(`Unknown ${this.kind}: ${name}`);
33
- }
34
- return entry.capability;
35
- }
36
-
37
- /** List registered capability names. */
38
- list(): string[] {
39
- return [...this.capabilities.keys()];
40
- }
41
- }
1
+ /**
2
+ * Compatibility re-export (ADR-010 / task 0008).
3
+ *
4
+ * The capability registry now lives in the shared plugin core
5
+ * (`@gobing-ai/ts-runtime/plugin`). This module re-exports it from the original
6
+ * path so the public barrel and existing importers keep working unchanged. New
7
+ * code should import from `@gobing-ai/ts-runtime/plugin` directly.
8
+ */
9
+ export { type CapabilityEntry, type CapabilityOrigin, CapabilityRegistry } from '@gobing-ai/ts-runtime/plugin';
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from './fixers/fixers';
5
5
  export * from './fixers/test-stub-fixer';
6
6
  export * from './formatters/json';
7
7
  export * from './formatters/text';
8
+ export * from './host/bundled-rules';
8
9
  export * from './host/capability-registry';
9
10
  export * from './host/rule-engine-host';
10
11
  export * from './resolvers/test-path-resolver';
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { assertRelativeExtensionPath } from '@gobing-ai/ts-runtime/plugin';
1
2
  import { z } from 'zod';
2
3
 
3
4
  /** Finding severity emitted by the rule engine. */
@@ -218,16 +219,22 @@ export const ConstraintRuleSchema = z.object({
218
219
  *
219
220
  * Rejects absolute paths and `..` traversal: extension declarations are data, and a
220
221
  * path that escapes the declaring file's directory is a trust-boundary violation even
221
- * when extension loading is explicitly allowed.
222
+ * when extension loading is explicitly allowed. The actual guard is the shared
223
+ * `assertRelativeExtensionPath` (ADR-010) so schema-time validation here and load-time
224
+ * validation in the shared loader use one source of truth.
222
225
  */
223
226
  const relativeExtensionPath = z
224
227
  .string()
225
228
  .min(1)
226
- .refine((value) => !/^([/\\]|[A-Za-z]:[/\\])/.test(value), {
227
- message: 'extension path must be relative (no absolute paths)',
228
- })
229
- .refine((value) => !value.split(/[/\\]/).includes('..'), {
230
- message: 'extension path must not contain ".." traversal',
229
+ .superRefine((value, ctx) => {
230
+ try {
231
+ assertRelativeExtensionPath(value);
232
+ } catch (error) {
233
+ ctx.addIssue({
234
+ code: 'custom',
235
+ message: error instanceof Error ? error.message : 'invalid extension path',
236
+ });
237
+ }
231
238
  });
232
239
 
233
240
  /**