agency-rules 0.3.2 → 0.4.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/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.1](https://github.com/deuriib/agency-rules/compare/v0.4.0...v0.4.1) (2026-10-31)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **pkg:** include rules directory in npm bundle and resolve package paths ([3877a97](https://github.com/deuriib/agency-rules/commit/3877a97e22ec5b79c183c47f3ed7b026171684e5))
14
+
15
+ ## [0.4.0](https://github.com/deuriib/agency-rules/compare/v0.3.2...v0.4.0) (2026-10-31)
16
+
17
+
18
+ ### Features
19
+
20
+ * **cli:** expose agency-rules binary and router ([dfb6b95](https://github.com/deuriib/agency-rules/commit/dfb6b95b8b6d9ac6f4622479527f182d9c1e637c))
21
+
8
22
  ## [0.3.2](https://github.com/deuriib/agency-rules/compare/v0.3.1...v0.3.2) (2026-10-31)
9
23
 
10
24
 
package/README.md CHANGED
@@ -29,6 +29,23 @@ with a single command, driven by a declarative contract (`tools.json`).
29
29
 
30
30
  ---
31
31
 
32
+ ## Global CLI Usage
33
+
34
+ You can also use `agency-rules` directly via `npx` without cloning the repository:
35
+
36
+ ```bash
37
+ # Validate rules
38
+ npx agency-rules validate
39
+
40
+ # Compile rules
41
+ npx agency-rules convert
42
+
43
+ # Install rules interactively
44
+ npx agency-rules install
45
+ ```
46
+
47
+ ---
48
+
32
49
  ## Quick Start
33
50
 
34
51
  ```bash
@@ -140,6 +157,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). Pull requests welcome.
140
157
 
141
158
  See [SECURITY.md](SECURITY.md) for responsible disclosure.
142
159
 
160
+ ![Repobeats analytics](https://repobeats.com/deuriib/agency-rules?theme=light&size=medium&period=30)
161
+
143
162
  ## License
144
163
 
145
164
  MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+
7
+ const command = process.argv[2];
8
+
9
+ // ── Read version dynamically ──────────────────────────────────────────
10
+ const getVersion = async () => {
11
+ try {
12
+ const pkgPath = resolve(import.meta.dirname, '..', 'package.json');
13
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
14
+ return pkg.version || '0.0.0';
15
+ } catch {
16
+ return '0.0.0';
17
+ }
18
+ };
19
+
20
+ const version = await getVersion();
21
+
22
+ // ── Help Menus ────────────────────────────────────────────────────────
23
+ const showHelp = () => {
24
+ console.log(`
25
+ \x1b[36magency-rules\x1b[0m v${version}
26
+ Write rules once. Run them in every harness.
27
+
28
+ \x1b[33mUsage:\x1b[0m
29
+ $ agency-rules <command> [options]
30
+
31
+ \x1b[33mCommands:\x1b[0m
32
+ \x1b[32mconvert\x1b[0m Compile rules/ into each harness's native format
33
+ \x1b[32minstall\x1b[0m Install compiled artifacts into detected harnesses
34
+ \x1b[32mvalidate\x1b[0m Validate each rule's frontmatter against the schema
35
+
36
+ \x1b[33mGlobal Options:\x1b[0m
37
+ -h, --help Show this help message
38
+ -v, --version Show version number
39
+
40
+ \x1b[33mExamples:\x1b[0m
41
+ $ agency-rules validate
42
+ $ agency-rules convert
43
+ $ agency-rules convert --dry-run
44
+ $ agency-rules install --scope user --harness opencode
45
+ `);
46
+ };
47
+
48
+ const showCommandHelp = (cmd) => {
49
+ const helps = {
50
+ convert: `
51
+ \x1b[36magency-rules convert\x1b[0m
52
+ Compile rules/ into each harness's native format.
53
+
54
+ \x1b[33mOptions:\x1b[0m
55
+ --dry-run Preview which files would be written (no writes)
56
+ --harness <id> Limit compilation to a specific harness (e.g., antigravity)
57
+
58
+ \x1b[33mExamples:\x1b[0m
59
+ $ agency-rules convert
60
+ $ agency-rules convert --dry-run
61
+ $ agency-rules convert --harness pi-agent
62
+ `,
63
+ install: `
64
+ \x1b[36magency-rules install\x1b[0m
65
+ Install compiled artifacts into detected harnesses.
66
+
67
+ \x1b[33mOptions:\x1b[0m
68
+ --dry-run Preview which files would be copied (no writes)
69
+ --scope <project|user> Install local to project or global to user (default: project)
70
+ --harness <id> Install into a specific harness
71
+ --all Force installation for all harnesses, ignoring auto-detection
72
+
73
+ \x1b[33mExamples:\x1b[0m
74
+ $ agency-rules install
75
+ $ agency-rules install --dry-run
76
+ $ agency-rules install --scope user
77
+ $ agency-rules install --harness opencode --scope project
78
+ `,
79
+ validate: `
80
+ \x1b[36magency-rules validate\x1b[0m
81
+ Validate each rule's frontmatter against the schema.
82
+
83
+ \x1b[33mOptions:\x1b[0m
84
+ (No specific options)
85
+
86
+ \x1b[33mExamples:\x1b[0m
87
+ $ agency-rules validate
88
+ `,
89
+ };
90
+ console.log(helps[cmd] || '');
91
+ };
92
+
93
+ // ── Global Flags ──────────────────────────────────────────────────────
94
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
95
+ console.log(version);
96
+ process.exit(0);
97
+ }
98
+
99
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
100
+ showHelp();
101
+ process.exit(0);
102
+ }
103
+
104
+ if (!['convert', 'install', 'validate'].includes(command)) {
105
+ console.error(`\x1b[31mError:\x1b[0m Unknown command '${command}'`);
106
+ showHelp();
107
+ process.exit(1);
108
+ }
109
+
110
+ // ── Command-specific Help ─────────────────────────────────────────────
111
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
112
+ showCommandHelp(command);
113
+ process.exit(0);
114
+ }
115
+
116
+ // ── Execute ───────────────────────────────────────────────────────────
117
+ // Remove the command from argv so the downstream script's parseArgs works cleanly
118
+ process.argv.splice(2, 1);
119
+
120
+ const scriptPath = resolve(import.meta.dirname, '..', 'scripts', `${command}.mjs`);
121
+
122
+ try {
123
+ await import(pathToFileURL(scriptPath).href);
124
+ } catch (err) {
125
+ console.error(`\x1b[31m❌ Failed to execute '${command}':\x1b[0m`, err.message);
126
+ process.exit(1);
127
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-rules",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "Write rules once. Run them in every harness.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,13 +22,17 @@
22
22
  "opencode",
23
23
  "oh-my-pi"
24
24
  ],
25
+ "bin": {
26
+ "agency-rules": "./bin/agency-rules.mjs"
27
+ },
25
28
  "engines": {
26
29
  "node": ">=20.12"
27
30
  },
28
31
  "files": [
32
+ "bin/",
29
33
  "scripts/",
30
34
  "tools.json",
31
- "rules/_schema.json",
35
+ "rules/",
32
36
  "docs/",
33
37
  "README.md",
34
38
  "LICENSE",
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: coding-standards
3
+ description: Baseline rules applied to every edit regardless of file type.
4
+ alwaysApply: true
5
+ tags: [base]
6
+ ---
7
+
8
+ # Coding Standards
9
+
10
+ - Keep functions under 40 lines.
11
+ - Prefer named exports over default exports.
12
+ - Never commit `console.log` or debug prints.
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: commit-conventions
3
+ description: Enforce Conventional Commits format for all git commit messages.
4
+ alwaysApply: true
5
+ tags: [git, commits, conventional-commits]
6
+ ---
7
+
8
+ # Git Commit Conventions
9
+
10
+ All commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
11
+ This is strictly required because automated release tooling (like `release-please`) uses these commits to calculate semantic versioning bumps and generate changelogs.
12
+
13
+ ## Format
14
+
15
+ ```
16
+ <type>(<optional scope>): <subject>
17
+ ```
18
+
19
+ - **type**: Must be one of the following:
20
+ - `feat`: A new feature (triggers a MINOR version bump).
21
+ - `fix`: A bug fix (triggers a PATCH version bump).
22
+ - `docs`: Documentation only changes.
23
+ - `style`: Changes that do not affect the meaning of the code (white-space, formatting, etc).
24
+ - `refactor`: A code change that neither fixes a bug nor adds a feature.
25
+ - `perf`: A code change that improves performance.
26
+ - `test`: Adding missing tests or correcting existing tests.
27
+ - `build`: Changes that affect the build system or external dependencies.
28
+ - `ci`: Changes to CI configuration files and scripts.
29
+ - `chore`: Other changes that don't modify src or test files.
30
+ - **scope** (optional): A noun describing a section of the codebase (e.g., `rules`, `convert`, `harness`, `docs`).
31
+ - **subject**: A short description in the imperative mood (e.g., "add", not "added").
32
+
33
+ ## Rules
34
+
35
+ - Use the **imperative, present tense** (e.g., "fix bug" not "fixed bug").
36
+ - Do **not** capitalize the first letter of the subject.
37
+ - Do **not** end the subject line with a period.
38
+ - Keep the subject line under **72 characters**.
39
+ - Separate the subject from the body with a blank line.
40
+ - Use the body to explain *what* and *why* (not *how*).
41
+ - Use footers for referencing issues (e.g., `Closes #123`) or declaring breaking changes (`BREAKING CHANGE: <description>`).
42
+
43
+ ## Examples
44
+
45
+ ```text
46
+ feat(rules): add python async conventions
47
+ fix(antigravity): join globs with comma instead of space
48
+ docs(spec): clarify trigger precedence for model_decision
49
+ chore(deps): bump biome to 2.5.1
50
+ feat(harness): support Cursor rules format
51
+
52
+ BREAKING CHANGE: The `trigger` field is now required for all Antigravity rules.
53
+ ```
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: python
3
+ description: Rules for Python modules.
4
+ globs: ["**/*.py"]
5
+ tags: [language, python]
6
+ ---
7
+
8
+ # Python Rules
9
+
10
+ - Use type hints on public functions.
11
+ - Prefer `pathlib` over `os.path`.
12
+ - No wildcard imports.
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: typescript
3
+ description: Mandatory rules for editing TypeScript and TSX files.
4
+ globs:
5
+ - "**/*.ts"
6
+ - "**/*.tsx"
7
+ alwaysApply: false
8
+ trigger: glob # preferencia para Antigravity; otros harnesses lo ignoran si no aplica
9
+ tags: [language, typescript]
10
+ ---
11
+
12
+ # TypeScript Rules
13
+
14
+ - No `any` unless absolutely necessary.
15
+ - Use top-level imports only.
16
+ - Prefer `interface` over `type` for object shapes.
17
+
18
+ ## Examples
19
+
20
+ ```ts
21
+ // ✅ good
22
+ interface User { id: string; name: string; }
23
+
24
+ // ❌ avoid
25
+ type User = { id: string; name: string; };
@@ -1,5 +1,5 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname, join } from 'node:path';
1
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, resolve } from 'node:path';
3
3
  import { parseArgs } from 'node:util';
4
4
  import { readRules } from './lib/frontmatter.mjs';
5
5
  import { renderers } from './lib/renderers/index.mjs';
@@ -14,17 +14,25 @@ const { values: args } = parseArgs({
14
14
  const dryRun = args['dry-run'];
15
15
  const prefix = dryRun ? ' [dry-run]' : ' ';
16
16
 
17
- const RULES_DIR = process.env.RULES_DIR || 'rules';
17
+ // Resolve paths: Use local if exists, otherwise fallback to the package's bundled files
18
+ const PACKAGE_ROOT = resolve(import.meta.dirname, '..');
19
+ const localRules = resolve(process.cwd(), 'rules');
20
+ const localTools = resolve(process.cwd(), 'tools.json');
21
+
22
+ const RULES_DIR =
23
+ process.env.RULES_DIR ||
24
+ ((await import('node:fs').then((fs) => fs.existsSync(localRules)))
25
+ ? localRules
26
+ : resolve(PACKAGE_ROOT, 'rules'));
27
+ const TOOLS_PATH = (await import('node:fs').then((fs) => fs.existsSync(localTools)))
28
+ ? localTools
29
+ : resolve(PACKAGE_ROOT, 'tools.json');
30
+
18
31
  const INTEGRATIONS_DIR = process.env.INTEGRATIONS_DIR || 'integrations';
19
32
 
20
- const tools = JSON.parse(await readFile('tools.json', 'utf8'));
33
+ const tools = JSON.parse(await readFile(TOOLS_PATH, 'utf8'));
21
34
  const rules = await readRules(RULES_DIR);
22
35
 
23
- if (rules.length === 0) {
24
- console.error(`❌ No rules found in ${RULES_DIR}`);
25
- process.exit(1);
26
- }
27
-
28
36
  console.log(
29
37
  `📚 Found ${rules.length} rule(s) in ${RULES_DIR}${dryRun ? ' (dry-run — nothing will be written)' : ''}\n`,
30
38
  );
@@ -1,5 +1,6 @@
1
+ import { existsSync } from 'node:fs';
1
2
  import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname, join } from 'node:path';
3
+ import { dirname, join, resolve } from 'node:path';
3
4
  import { stdin as input, stdout as output } from 'node:process';
4
5
  import { createInterface } from 'node:readline/promises';
5
6
  import { parseArgs } from 'node:util';
@@ -19,7 +20,12 @@ const tag = dryRun ? '[dry-run]' : ' ';
19
20
  const log = (msg) => console.log(` ${tag} ${msg}`);
20
21
 
21
22
  const INTEGRATIONS_DIR = process.env.INTEGRATIONS_DIR || 'integrations';
22
- const tools = JSON.parse(await readFile('tools.json', 'utf8'));
23
+
24
+ const PACKAGE_ROOT = resolve(import.meta.dirname, '..');
25
+ const localTools = resolve(process.cwd(), 'tools.json');
26
+ const TOOLS_PATH = existsSync(localTools) ? localTools : resolve(PACKAGE_ROOT, 'tools.json');
27
+
28
+ const tools = JSON.parse(await readFile(TOOLS_PATH, 'utf8'));
23
29
 
24
30
  let finalScope = args.scope;
25
31
  let finalHarness = args.harness;