dotai-cli 0.3.0__tar.gz → 0.4.0__tar.gz

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.
Files changed (72) hide show
  1. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.gitignore +1 -0
  2. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/PKG-INFO +92 -17
  3. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/README.md +91 -16
  4. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/pyproject.toml +1 -1
  5. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/__init__.py +1 -1
  6. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/rules_cmd.py +255 -0
  7. dotai_cli-0.4.0/src/dotai/maintenance.py +821 -0
  8. dotai_cli-0.4.0/src/dotai/seed/skills/compress.md +59 -0
  9. dotai_cli-0.4.0/tests/test_maintenance.py +362 -0
  10. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/uv.lock +1 -1
  11. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.github/workflows/ci.yml +0 -0
  12. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.github/workflows/publish.yml +0 -0
  13. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/LICENSE +0 -0
  14. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/rdc.yaml +0 -0
  15. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/__main__.py +0 -0
  16. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/__init__.py +0 -0
  17. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/__main__.py +0 -0
  18. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/prefs_cmd.py +0 -0
  19. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/roles_cmd.py +0 -0
  20. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/skills_cmd.py +0 -0
  21. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/sync_cmd.py +0 -0
  22. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/watch_cmd.py +0 -0
  23. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/converter.py +0 -0
  24. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/import_agent.py +0 -0
  25. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/indexer.py +0 -0
  26. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/models.py +0 -0
  27. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/preferences.py +0 -0
  28. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/roles.py +0 -0
  29. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/rules.py +0 -0
  30. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/architect.md +0 -0
  31. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/debugger.md +0 -0
  32. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/founder.md +0 -0
  33. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/mentor.md +0 -0
  34. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/product-manager.md +0 -0
  35. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/qa.md +0 -0
  36. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/reviewer.md +0 -0
  37. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/security.md +0 -0
  38. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/ship.md +0 -0
  39. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/writer.md +0 -0
  40. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/careful.md +0 -0
  41. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/commit-helper.md +0 -0
  42. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/context-dump.md +0 -0
  43. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/investigate.md +0 -0
  44. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/learn.md +0 -0
  45. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/parallel-work.md +0 -0
  46. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/plan.md +0 -0
  47. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/review.md +0 -0
  48. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/scaffold.md +0 -0
  49. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/ship.md +0 -0
  50. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/techdebt.md +0 -0
  51. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/verify.md +0 -0
  52. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/skills.py +0 -0
  53. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/store.py +0 -0
  54. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/sync.py +0 -0
  55. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/__init__.py +0 -0
  56. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/base.py +0 -0
  57. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/registry.py +0 -0
  58. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/utils.py +0 -0
  59. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/vet.py +0 -0
  60. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/__init__.py +0 -0
  61. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/conftest.py +0 -0
  62. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_converter.py +0 -0
  63. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_import_agent.py +0 -0
  64. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_models.py +0 -0
  65. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_preferences.py +0 -0
  66. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_roles.py +0 -0
  67. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_rules.py +0 -0
  68. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_skills.py +0 -0
  69. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_store.py +0 -0
  70. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_sync.py +0 -0
  71. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_utils.py +0 -0
  72. {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_vet.py +0 -0
@@ -13,3 +13,4 @@ CLAUDE.md
13
13
  .cursorrules
14
14
  AGENTS.md
15
15
  GEMINI.md
16
+ .dotai-compression-proposal.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotai-cli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Universal AI context for any coding agent. Roles, skills, and rules in ~/.ai/
5
5
  Project-URL: Homepage, https://github.com/dawalama/dotai
6
6
  Project-URL: Repository, https://github.com/dawalama/dotai
@@ -29,29 +29,63 @@ Description-Content-Type: text/markdown
29
29
 
30
30
  # dotai
31
31
 
32
- Universal AI context for any coding agent. Roles, skills, and rules in `~/.ai/`.
32
+ **Your coding agents should remember what you taught them.**
33
33
 
34
- ## The Problem
34
+ dotai gives Claude, Cursor, Gemini, Codex, and other coding agents one shared memory for how you build software. Keep your rules, workflows, and engineering taste in `~/.ai/`; sync them into any project with one command.
35
35
 
36
- Every AI coding tool has its own way to store context:
36
+ ```text
37
+ ┌─ CLAUDE.md + native slash commands
38
+ ~/.ai/ ── dotai sync ───┼─ .cursorrules
39
+ ├─ GEMINI.md
40
+ └─ AGENTS.md
41
+ ```
42
+
43
+ No server. No account. No proprietary memory layer. Just portable Markdown that you own.
44
+
45
+ ## Stop reteaching every agent
46
+
47
+ You tell one agent “never call `useEffect` directly.” Next week, a different agent does exactly that. Your review checklist lives in one tool, your deployment workflow in another, and the correction you made yesterday disappears with the chat.
48
+
49
+ dotai turns those corrections into durable, reusable context:
50
+
51
+ - **Rules are hard constraints.** Capture coding standards and scope them by project or file pattern.
52
+ - **Skills are repeatable workflows.** Plan, investigate, review, verify, and ship the same way every time.
53
+ - **Roles change how the agent thinks.** Review as a security engineer; plan as a product manager.
54
+ - **Preference packs carry your taste.** Share softer choices without weakening hard rules.
55
+ - **Sync speaks each agent's language.** Generate the bootstrap file that Claude, Cursor, Gemini, or Codex already understands.
56
+
57
+ The useful part is the feedback loop:
58
+
59
+ ```text
60
+ agent makes a mistake → capture the correction → sync → every agent learns it
61
+ ```
62
+
63
+ ## See it in action
37
64
 
38
- - Claude Code → `CLAUDE.md`
39
- - Cursor → `.cursorrules`
40
- - Gemini CLI → `GEMINI.md`
41
- - Codex / Copilot → `AGENTS.md`
65
+ ```bash
66
+ # Set up your global knowledge base
67
+ dotai init
42
68
 
43
- Your knowledge about how to write code, review it, ship it, and debug it gets scattered across tool-specific formats — or worse, stays in your head.
69
+ # Teach every agent a correction you never want to repeat
70
+ dotai learn "no-print-debugging" \
71
+ --issue "Debug output was left in production code" \
72
+ --correction "Use structured logging and remove temporary debug output" \
73
+ --globs "*.py"
44
74
 
45
- ## The Solution
75
+ # Bring that knowledge into a project
76
+ cd ~/my-project
77
+ dotai sync
78
+ ```
46
79
 
47
- `dotai` defines a universal `~/.ai/` directory convention that any tool can consume. It ships with:
80
+ Now the rule is available to every supported agent, alongside reusable commands such as:
48
81
 
49
- - **Roles** — Cognitive modes that shift how the AI thinks (reviewer, architect, founder, QA, etc.)
50
- - **Skills** — Reusable workflows with steps, inputs, gotchas, and role references
51
- - **Rules** — Structured coding standards with per-project toggles and file-pattern scoping (hard constraints)
52
- - **Preference packs** — Soft, borrowable taste (CLI stack, design micro-style); never override hard rules
53
- - **Agent sync** — Generate `CLAUDE.md`, `.cursorrules`, `GEMINI.md`, or `AGENTS.md` from your `~/.ai/`
54
- - **Native slash commands** — For Claude Code, skills become real `/run_*` commands automatically
82
+ ```text
83
+ /run_plan as product-manager
84
+ /run_investigate as debugger
85
+ /run_review as security-engineer
86
+ /run_verify
87
+ /run_ship
88
+ ```
55
89
 
56
90
  ## Installation
57
91
 
@@ -125,6 +159,7 @@ After `dotai sync`, skills appear as native slash commands:
125
159
  > /run_careful # Enter production-safety mode
126
160
  > /run_verify # Run tests, types, lint, build
127
161
  > /run_learn # Capture a mistake as a permanent rule
162
+ > /run_compress # Draft and review smaller rule definitions
128
163
  ```
129
164
 
130
165
  ### Using skills outside Claude Code
@@ -177,6 +212,41 @@ dotai rules --check
177
212
 
178
213
  The feedback loop: **vibe-code → agent makes mistake → `/run_learn` → structured rule → `dotai sync` → agent never repeats it.**
179
214
 
215
+ ### Keeping rules healthy
216
+
217
+ Knowledge gets noisy as it grows. `dotai audit` reviews the effective ruleset locally and deterministically—nothing is sent to an external model.
218
+
219
+ ```bash
220
+ # Find weak, generic, preference-like, oversized, or overlapping rules
221
+ dotai audit
222
+ dotai audit --project my-app
223
+
224
+ # Machine-readable output or a CI quality gate
225
+ dotai audit --json
226
+ dotai audit --fail-on high
227
+ ```
228
+
229
+ The report includes stable finding codes, evidence, suggested actions, an estimated prompt-token cost, and a context-concentration summary showing which rules dominate the budget. Large-rule findings show the rule's share of total context, change risk, reviewable sections, and a conservative manual-savings range. Security and other high-consequence rules receive preservation-first guidance and are deliberately protected from “the model probably knows this” recommendations.
230
+
231
+ `dotai compress` turns high-confidence findings into a conservative plan. Semantic rewriting is handled by the active coding agent, not a hidden provider inside the CLI:
232
+
233
+ ```bash
234
+ # Inspect candidates
235
+ dotai compress
236
+
237
+ # Ask the current coding agent to draft, diff, and review changes
238
+ > /run_compress
239
+
240
+ # Low-level application boundary for an approved versioned proposal
241
+ dotai compress apply proposal.json
242
+ ```
243
+
244
+ `/run_compress` reads the JSON plan, drafts shorter complete rules, explains what it preserved and consolidated, shows the full diff in conversation, and asks `Apply this compression? [y/N]`. It then passes only approved changes to dotai. Near-duplicates, generic guidance, and rules that may belong in a preference pack remain suggestions unless the developer explicitly approves a concrete proposal.
245
+
246
+ When no exact duplicates exist, compression says there are no **safe automatic** savings while still listing semantic review candidates and their estimated savings. These estimates are directional—they help prioritize review. The CLI never invents a semantic rewrite or calls an external provider.
247
+
248
+ Proposal files are versioned and bound to the original rule with a SHA-256 hash, so stale or renamed rules are rejected. Before applying approved changes, dotai validates every proposed rule and creates a complete timestamped snapshot under the applicable `.ai/backups/<timestamp>-compress/`. After writing, the manifest is finalized with actual before/after hashes, per-rule token savings, total savings, and completion status. Subsequent audits show recently compressed rules and clearly note when a successful reduction remains above the large-rule threshold. Backups are ordinary local files and are never deleted automatically.
249
+
180
250
  ### Migrating existing agent files
181
251
 
182
252
  Already have a `CLAUDE.md`, `.cursorrules`, or `AGENTS.md`? Import user-authored content into `~/.ai/`:
@@ -298,6 +368,7 @@ dotai init ~/other-project
298
368
  ├── preferences/ # Soft taste packs (borrowable style priors)
299
369
  │ └── cli.md # Example: CLI stack / micro-style
300
370
  ├── preferences-active.json # Which preference packs are active
371
+ ├── backups/ # Local snapshots created before compression writes
301
372
  ├── roles/ # Cognitive modes
302
373
  │ ├── reviewer.md # Paranoid staff engineer
303
374
  │ ├── architect.md # Systems thinker
@@ -322,6 +393,7 @@ dotai init ~/other-project
322
393
  │ ├── verify.md # Run tests, types, lint (/run_verify)
323
394
  │ ├── plan.md # Structured planning workflow (/run_plan)
324
395
  │ ├── learn.md # Capture learnings as rules (/run_learn)
396
+ │ ├── compress.md # Review semantic rule compression (/run_compress)
325
397
  │ └── deploy/ # Folder-based skill (with scripts & assets)
326
398
  │ ├── main.md
327
399
  │ ├── scripts/
@@ -699,6 +771,9 @@ dotai prompt <skill> [--role <role>] # Assemble skill + role prompt for any ag
699
771
  dotai sync [path] [--agents ...] [--full] # Sync ~/.ai/ into agent config files
700
772
  dotai primer [--project <name>] [--full|--compact] # Print agent context to stdout
701
773
  dotai rules [-p project] [-a] [--check] # List rules (or audit quality)
774
+ dotai audit [-p project] [--json] [--fail-on severity] # Read-only rule audit
775
+ dotai compress [-p project] [--json] # Plan compression candidates
776
+ dotai compress apply <proposal.json> [--yes] # Validate, back up, and apply proposals
702
777
  dotai toggle <rule-id> --on/--off # Enable/disable rules globally or per-project
703
778
  dotai learn "title" -i "..." -c "..." # Create structured rule (default)
704
779
  dotai learn "title" -i "..." -c "..." --dry-run|--sync|--force|--append-md
@@ -1,28 +1,62 @@
1
1
  # dotai
2
2
 
3
- Universal AI context for any coding agent. Roles, skills, and rules in `~/.ai/`.
3
+ **Your coding agents should remember what you taught them.**
4
4
 
5
- ## The Problem
5
+ dotai gives Claude, Cursor, Gemini, Codex, and other coding agents one shared memory for how you build software. Keep your rules, workflows, and engineering taste in `~/.ai/`; sync them into any project with one command.
6
6
 
7
- Every AI coding tool has its own way to store context:
7
+ ```text
8
+ ┌─ CLAUDE.md + native slash commands
9
+ ~/.ai/ ── dotai sync ───┼─ .cursorrules
10
+ ├─ GEMINI.md
11
+ └─ AGENTS.md
12
+ ```
13
+
14
+ No server. No account. No proprietary memory layer. Just portable Markdown that you own.
15
+
16
+ ## Stop reteaching every agent
17
+
18
+ You tell one agent “never call `useEffect` directly.” Next week, a different agent does exactly that. Your review checklist lives in one tool, your deployment workflow in another, and the correction you made yesterday disappears with the chat.
19
+
20
+ dotai turns those corrections into durable, reusable context:
21
+
22
+ - **Rules are hard constraints.** Capture coding standards and scope them by project or file pattern.
23
+ - **Skills are repeatable workflows.** Plan, investigate, review, verify, and ship the same way every time.
24
+ - **Roles change how the agent thinks.** Review as a security engineer; plan as a product manager.
25
+ - **Preference packs carry your taste.** Share softer choices without weakening hard rules.
26
+ - **Sync speaks each agent's language.** Generate the bootstrap file that Claude, Cursor, Gemini, or Codex already understands.
27
+
28
+ The useful part is the feedback loop:
29
+
30
+ ```text
31
+ agent makes a mistake → capture the correction → sync → every agent learns it
32
+ ```
33
+
34
+ ## See it in action
8
35
 
9
- - Claude Code → `CLAUDE.md`
10
- - Cursor → `.cursorrules`
11
- - Gemini CLI → `GEMINI.md`
12
- - Codex / Copilot → `AGENTS.md`
36
+ ```bash
37
+ # Set up your global knowledge base
38
+ dotai init
13
39
 
14
- Your knowledge about how to write code, review it, ship it, and debug it gets scattered across tool-specific formats — or worse, stays in your head.
40
+ # Teach every agent a correction you never want to repeat
41
+ dotai learn "no-print-debugging" \
42
+ --issue "Debug output was left in production code" \
43
+ --correction "Use structured logging and remove temporary debug output" \
44
+ --globs "*.py"
15
45
 
16
- ## The Solution
46
+ # Bring that knowledge into a project
47
+ cd ~/my-project
48
+ dotai sync
49
+ ```
17
50
 
18
- `dotai` defines a universal `~/.ai/` directory convention that any tool can consume. It ships with:
51
+ Now the rule is available to every supported agent, alongside reusable commands such as:
19
52
 
20
- - **Roles** — Cognitive modes that shift how the AI thinks (reviewer, architect, founder, QA, etc.)
21
- - **Skills** — Reusable workflows with steps, inputs, gotchas, and role references
22
- - **Rules** — Structured coding standards with per-project toggles and file-pattern scoping (hard constraints)
23
- - **Preference packs** — Soft, borrowable taste (CLI stack, design micro-style); never override hard rules
24
- - **Agent sync** — Generate `CLAUDE.md`, `.cursorrules`, `GEMINI.md`, or `AGENTS.md` from your `~/.ai/`
25
- - **Native slash commands** — For Claude Code, skills become real `/run_*` commands automatically
53
+ ```text
54
+ /run_plan as product-manager
55
+ /run_investigate as debugger
56
+ /run_review as security-engineer
57
+ /run_verify
58
+ /run_ship
59
+ ```
26
60
 
27
61
  ## Installation
28
62
 
@@ -96,6 +130,7 @@ After `dotai sync`, skills appear as native slash commands:
96
130
  > /run_careful # Enter production-safety mode
97
131
  > /run_verify # Run tests, types, lint, build
98
132
  > /run_learn # Capture a mistake as a permanent rule
133
+ > /run_compress # Draft and review smaller rule definitions
99
134
  ```
100
135
 
101
136
  ### Using skills outside Claude Code
@@ -148,6 +183,41 @@ dotai rules --check
148
183
 
149
184
  The feedback loop: **vibe-code → agent makes mistake → `/run_learn` → structured rule → `dotai sync` → agent never repeats it.**
150
185
 
186
+ ### Keeping rules healthy
187
+
188
+ Knowledge gets noisy as it grows. `dotai audit` reviews the effective ruleset locally and deterministically—nothing is sent to an external model.
189
+
190
+ ```bash
191
+ # Find weak, generic, preference-like, oversized, or overlapping rules
192
+ dotai audit
193
+ dotai audit --project my-app
194
+
195
+ # Machine-readable output or a CI quality gate
196
+ dotai audit --json
197
+ dotai audit --fail-on high
198
+ ```
199
+
200
+ The report includes stable finding codes, evidence, suggested actions, an estimated prompt-token cost, and a context-concentration summary showing which rules dominate the budget. Large-rule findings show the rule's share of total context, change risk, reviewable sections, and a conservative manual-savings range. Security and other high-consequence rules receive preservation-first guidance and are deliberately protected from “the model probably knows this” recommendations.
201
+
202
+ `dotai compress` turns high-confidence findings into a conservative plan. Semantic rewriting is handled by the active coding agent, not a hidden provider inside the CLI:
203
+
204
+ ```bash
205
+ # Inspect candidates
206
+ dotai compress
207
+
208
+ # Ask the current coding agent to draft, diff, and review changes
209
+ > /run_compress
210
+
211
+ # Low-level application boundary for an approved versioned proposal
212
+ dotai compress apply proposal.json
213
+ ```
214
+
215
+ `/run_compress` reads the JSON plan, drafts shorter complete rules, explains what it preserved and consolidated, shows the full diff in conversation, and asks `Apply this compression? [y/N]`. It then passes only approved changes to dotai. Near-duplicates, generic guidance, and rules that may belong in a preference pack remain suggestions unless the developer explicitly approves a concrete proposal.
216
+
217
+ When no exact duplicates exist, compression says there are no **safe automatic** savings while still listing semantic review candidates and their estimated savings. These estimates are directional—they help prioritize review. The CLI never invents a semantic rewrite or calls an external provider.
218
+
219
+ Proposal files are versioned and bound to the original rule with a SHA-256 hash, so stale or renamed rules are rejected. Before applying approved changes, dotai validates every proposed rule and creates a complete timestamped snapshot under the applicable `.ai/backups/<timestamp>-compress/`. After writing, the manifest is finalized with actual before/after hashes, per-rule token savings, total savings, and completion status. Subsequent audits show recently compressed rules and clearly note when a successful reduction remains above the large-rule threshold. Backups are ordinary local files and are never deleted automatically.
220
+
151
221
  ### Migrating existing agent files
152
222
 
153
223
  Already have a `CLAUDE.md`, `.cursorrules`, or `AGENTS.md`? Import user-authored content into `~/.ai/`:
@@ -269,6 +339,7 @@ dotai init ~/other-project
269
339
  ├── preferences/ # Soft taste packs (borrowable style priors)
270
340
  │ └── cli.md # Example: CLI stack / micro-style
271
341
  ├── preferences-active.json # Which preference packs are active
342
+ ├── backups/ # Local snapshots created before compression writes
272
343
  ├── roles/ # Cognitive modes
273
344
  │ ├── reviewer.md # Paranoid staff engineer
274
345
  │ ├── architect.md # Systems thinker
@@ -293,6 +364,7 @@ dotai init ~/other-project
293
364
  │ ├── verify.md # Run tests, types, lint (/run_verify)
294
365
  │ ├── plan.md # Structured planning workflow (/run_plan)
295
366
  │ ├── learn.md # Capture learnings as rules (/run_learn)
367
+ │ ├── compress.md # Review semantic rule compression (/run_compress)
296
368
  │ └── deploy/ # Folder-based skill (with scripts & assets)
297
369
  │ ├── main.md
298
370
  │ ├── scripts/
@@ -670,6 +742,9 @@ dotai prompt <skill> [--role <role>] # Assemble skill + role prompt for any ag
670
742
  dotai sync [path] [--agents ...] [--full] # Sync ~/.ai/ into agent config files
671
743
  dotai primer [--project <name>] [--full|--compact] # Print agent context to stdout
672
744
  dotai rules [-p project] [-a] [--check] # List rules (or audit quality)
745
+ dotai audit [-p project] [--json] [--fail-on severity] # Read-only rule audit
746
+ dotai compress [-p project] [--json] # Plan compression candidates
747
+ dotai compress apply <proposal.json> [--yes] # Validate, back up, and apply proposals
673
748
  dotai toggle <rule-id> --on/--off # Enable/disable rules globally or per-project
674
749
  dotai learn "title" -i "..." -c "..." # Create structured rule (default)
675
750
  dotai learn "title" -i "..." -c "..." --dry-run|--sync|--force|--append-md
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dotai-cli"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "Universal AI context for any coding agent. Roles, skills, and rules in ~/.ai/"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """dotai — Universal AI context for any coding agent."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -10,6 +10,261 @@ from rich.table import Table
10
10
  from . import app, console
11
11
 
12
12
 
13
+ @app.command()
14
+ def audit(
15
+ project: Optional[str] = typer.Option(None, "--project", "-p", help="Audit rules effective for a registered project"),
16
+ all: bool = typer.Option(False, "--all", "-a", help="Include disabled rules"),
17
+ json_output: bool = typer.Option(False, "--json", help="Emit a machine-readable report"),
18
+ fail_on: Optional[str] = typer.Option(None, "--fail-on", help="Exit non-zero at or above: low, medium, high"),
19
+ ):
20
+ """Audit rule quality, overlap, scope, and estimated context cost.
21
+
22
+ The audit is deterministic, local, and read-only. No rule content is sent
23
+ to an external service.
24
+ """
25
+ import json
26
+
27
+ from ..maintenance import SEVERITY_ORDER, audit_rules
28
+ from ..store import load_config
29
+
30
+ if fail_on and fail_on not in ("low", "medium", "high"):
31
+ console.print("[red]--fail-on must be one of: low, medium, high[/red]")
32
+ raise typer.Exit(2)
33
+
34
+ config = load_config()
35
+ if project and not config.get_project(project):
36
+ console.print(f"[red]Project '{project}' not found[/red]")
37
+ raise typer.Exit(1)
38
+ report = audit_rules(config, project_name=project, include_disabled=all)
39
+
40
+ if json_output:
41
+ console.print_json(json.dumps(report.to_dict()))
42
+ else:
43
+ console.print(
44
+ f"[bold]Rule audit[/bold] · {report.scope} · "
45
+ f"{report.rule_count} rules · ~{report.estimated_tokens} tokens"
46
+ )
47
+ top_metrics = report.rule_metrics[:3]
48
+ if len(top_metrics) >= 2 and report.estimated_tokens:
49
+ concentrated = sum(metric.estimated_tokens for metric in top_metrics)
50
+ percentage = concentrated / report.estimated_tokens * 100
51
+ names = ", ".join(metric.rule_id for metric in top_metrics)
52
+ console.print(
53
+ f"[bold]Context concentration:[/bold] top {len(top_metrics)} rules consume "
54
+ f"~{concentrated} tokens ({percentage:.0f}%): {names}"
55
+ )
56
+ recent_metrics = [metric for metric in report.rule_metrics if metric.recent_saved_tokens]
57
+ if recent_metrics:
58
+ console.print("[bold]Recent compression:[/bold]")
59
+ for metric in recent_metrics:
60
+ percentage = (
61
+ metric.recent_saved_tokens / metric.recent_before_tokens * 100
62
+ if metric.recent_before_tokens
63
+ else 0
64
+ )
65
+ console.print(
66
+ f" {metric.rule_id}: ~{metric.recent_before_tokens} → "
67
+ f"~{metric.estimated_tokens} tokens "
68
+ f"([green]-{metric.recent_saved_tokens}, {percentage:.0f}%[/green])"
69
+ )
70
+ if not report.findings:
71
+ console.print("[green]No deterministic rule quality issues found.[/green]")
72
+ else:
73
+ console.print("\n[bold]Findings[/bold]")
74
+ for finding in report.findings:
75
+ color = {"high": "red", "medium": "yellow", "low": "blue", "info": "dim"}[finding.severity]
76
+ console.print(
77
+ f"[{color}]{finding.severity.upper()}[/{color}] "
78
+ f"[cyan]{finding.code}[/cyan] · {', '.join(finding.rule_ids)}"
79
+ )
80
+ console.print(f" {finding.message}")
81
+ if finding.evidence:
82
+ console.print(f" [dim]{finding.evidence}[/dim]")
83
+ console.print(f" [bold]Next:[/bold] {finding.suggestion}\n")
84
+
85
+ if fail_on:
86
+ threshold = SEVERITY_ORDER[fail_on]
87
+ if any(SEVERITY_ORDER[finding.severity] >= threshold for finding in report.findings):
88
+ raise typer.Exit(1)
89
+
90
+
91
+ @app.command()
92
+ def compress(
93
+ action: Optional[str] = typer.Argument(None, help="Optional action: apply"),
94
+ proposal: Optional[Path] = typer.Argument(None, help="Versioned JSON proposal file for the apply action"),
95
+ project: Optional[str] = typer.Option(None, "--project", "-p", help="Compress a registered project's local rules"),
96
+ dry_run: bool = typer.Option(False, "--dry-run", help="Show candidates without applying exact duplicates"),
97
+ yes: bool = typer.Option(False, "--yes", "-y", help="Apply an already-approved proposal without another prompt"),
98
+ json_output: bool = typer.Option(False, "--json", help="Emit a machine-readable compression plan"),
99
+ ):
100
+ """Plan compression or safely apply an agent-generated proposal.
101
+
102
+ Run without an action to inspect candidates. Use `compress apply FILE` to
103
+ validate a versioned proposal, show its diffs, back up the rules, and apply
104
+ only approved changes.
105
+ """
106
+ import difflib
107
+ import json
108
+ import sys
109
+
110
+ from ..maintenance import (
111
+ apply_compression_plan,
112
+ build_compression_plan,
113
+ create_compression_backup,
114
+ estimate_tokens,
115
+ finalize_compression_backup,
116
+ load_compression_proposal,
117
+ write_reviewed_rule,
118
+ )
119
+ from ..store import load_config
120
+
121
+ config = load_config()
122
+ if project and not config.get_project(project):
123
+ console.print(f"[red]Project '{project}' not found[/red]")
124
+ raise typer.Exit(1)
125
+ plan = build_compression_plan(config, project_name=project)
126
+
127
+ if action:
128
+ if action != "apply" or not proposal:
129
+ console.print("[red]Usage: dotai compress apply <proposal.json> [--project NAME] [--yes][/red]")
130
+ raise typer.Exit(2)
131
+ try:
132
+ loaded = load_compression_proposal(proposal, config, project_name=project)
133
+ except ValueError as error:
134
+ console.print(f"[red]Invalid compression proposal: {error}[/red]")
135
+ raise typer.Exit(1)
136
+
137
+ approved = []
138
+ for change in loaded.changes:
139
+ original = change.source_path.read_text()
140
+ before = estimate_tokens(original)
141
+ after = estimate_tokens(change.content)
142
+ diff = "".join(difflib.unified_diff(
143
+ original.splitlines(keepends=True),
144
+ change.content.splitlines(keepends=True),
145
+ fromfile=str(change.source_path),
146
+ tofile=f"{change.source_path} (proposed)",
147
+ ))
148
+ console.print(
149
+ f"\n[bold]{change.rule_id}[/bold] · ~{before} → ~{after} tokens "
150
+ f"([green]-{max(0, before - after)}[/green])"
151
+ )
152
+ console.print(diff or "[dim]No textual change.[/dim]")
153
+ if change.content == original:
154
+ continue
155
+ if yes or typer.confirm("Apply this compression?", default=False):
156
+ approved.append(change)
157
+
158
+ if not approved:
159
+ console.print("\n[dim]No changes approved.[/dim]")
160
+ return
161
+ backup_path = create_compression_backup(config, plan, project_name=project)
162
+ console.print(f"[bold]Backup created:[/bold] {backup_path}")
163
+ changed = 0
164
+ for change in approved:
165
+ try:
166
+ write_reviewed_rule(
167
+ config,
168
+ project_name=project,
169
+ rule_path=change.source_path,
170
+ content=change.content,
171
+ backup_path=backup_path,
172
+ expected_sha256=change.original_sha256,
173
+ )
174
+ except (OSError, ValueError) as error:
175
+ console.print(f"[red]Could not apply {change.rule_id}: {error}[/red]")
176
+ continue
177
+ changed += 1
178
+ console.print(f"[green]Applied:[/green] {change.rule_id}")
179
+ manifest = finalize_compression_backup(
180
+ config,
181
+ backup_path,
182
+ project_name=project,
183
+ )
184
+ console.print(f"\n[green]Compression complete: {changed} rule(s) changed.[/green]")
185
+ for change in manifest["changes"]:
186
+ console.print(
187
+ f" {change['rule_id']}: ~{change['before_tokens']} → "
188
+ f"~{change['after_tokens']} tokens "
189
+ f"([green]-{change['saved_tokens']}[/green])"
190
+ )
191
+ console.print(
192
+ f"[bold]Actual context:[/bold] ~{manifest['actual_tokens_before']} → "
193
+ f"~{manifest['actual_tokens_after']} tokens "
194
+ f"([green]-{manifest['actual_tokens_saved']}[/green])"
195
+ )
196
+ console.print(f"[bold]Backup:[/bold] {backup_path}")
197
+ return
198
+
199
+ if json_output:
200
+ console.print_json(json.dumps(plan.to_dict()))
201
+ else:
202
+ saved = plan.estimated_tokens_before - plan.estimated_tokens_after
203
+ manual = [action for action in plan.actions if not action.automatic]
204
+ manual_low = sum(action.estimated_tokens_saved_low for action in manual)
205
+ manual_high = sum(action.estimated_tokens_saved_high for action in manual)
206
+ console.print(
207
+ f"[bold]Compression plan[/bold] · {plan.scope} · {plan.rule_count} rules"
208
+ )
209
+ if saved:
210
+ console.print(
211
+ f"Safe automatic context: ~{plan.estimated_tokens_before} → "
212
+ f"~{plan.estimated_tokens_after} tokens ([green]-{saved}[/green])"
213
+ )
214
+ else:
215
+ console.print(
216
+ f"Estimated context: ~{plan.estimated_tokens_before} tokens · "
217
+ "no safe automatic savings"
218
+ )
219
+ if manual:
220
+ savings = (
221
+ f" · potential manual savings ~{manual_low}-{manual_high} tokens"
222
+ if manual_high
223
+ else ""
224
+ )
225
+ console.print(f"[yellow]{len(manual)} review candidate(s)[/yellow]{savings}")
226
+ if not plan.actions:
227
+ console.print("[green]No safe automatic changes or review candidates found.[/green]")
228
+ else:
229
+ console.print("\n[bold]Actions[/bold]")
230
+ for action in plan.actions:
231
+ if action.estimated_tokens_saved:
232
+ savings = f"~{action.estimated_tokens_saved}"
233
+ elif action.estimated_tokens_saved_high:
234
+ savings = (
235
+ f"~{action.estimated_tokens_saved_low}-"
236
+ f"{action.estimated_tokens_saved_high}"
237
+ )
238
+ else:
239
+ savings = ""
240
+ mode = "[green]APPLY[/green]" if action.automatic else "[yellow]REVIEW[/yellow]"
241
+ savings_text = f" · potential savings {savings} tokens" if savings else ""
242
+ console.print(
243
+ f"{mode} [cyan]{action.action}[/cyan] · "
244
+ f"{', '.join(action.rule_ids)}{savings_text}"
245
+ )
246
+ console.print(f" {action.message}\n")
247
+
248
+ if json_output or dry_run or not plan.automatic_actions:
249
+ if plan.actions and not json_output:
250
+ console.print("[dim]Use `/run_compress` in a synced coding agent to draft and review semantic changes.[/dim]")
251
+ return
252
+ if not sys.stdin.isatty():
253
+ console.print("[dim]Exact duplicates found; run in a terminal to approve their removal.[/dim]")
254
+ return
255
+ if typer.confirm(
256
+ f"Apply {len(plan.automatic_actions)} exact-duplicate action(s)?",
257
+ default=False,
258
+ ):
259
+ try:
260
+ result = apply_compression_plan(config, plan, project_name=project)
261
+ except (OSError, ValueError) as error:
262
+ console.print(f"[red]Compression aborted: {error}[/red]")
263
+ raise typer.Exit(1)
264
+ console.print(f"[green]Removed {len(result.removed_paths)} exact duplicate rule(s).[/green]")
265
+ console.print(f"[bold]Backup:[/bold] {result.backup_path}")
266
+
267
+
13
268
  @app.command()
14
269
  def rules(
15
270
  project: Optional[str] = typer.Option(None, "--project", "-p", help="Show rules resolved for a project"),