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.
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.gitignore +1 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/PKG-INFO +92 -17
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/README.md +91 -16
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/pyproject.toml +1 -1
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/__init__.py +1 -1
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/rules_cmd.py +255 -0
- dotai_cli-0.4.0/src/dotai/maintenance.py +821 -0
- dotai_cli-0.4.0/src/dotai/seed/skills/compress.md +59 -0
- dotai_cli-0.4.0/tests/test_maintenance.py +362 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/uv.lock +1 -1
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.github/workflows/ci.yml +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/LICENSE +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/rdc.yaml +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/__main__.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/__init__.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/__main__.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/prefs_cmd.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/roles_cmd.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/skills_cmd.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/sync_cmd.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/cli/watch_cmd.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/converter.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/import_agent.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/indexer.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/models.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/preferences.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/roles.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/rules.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/architect.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/debugger.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/founder.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/mentor.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/product-manager.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/qa.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/reviewer.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/security.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/ship.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/roles/writer.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/careful.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/commit-helper.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/context-dump.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/investigate.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/learn.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/parallel-work.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/plan.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/review.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/scaffold.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/ship.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/techdebt.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/seed/skills/verify.md +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/skills.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/store.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/sync.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/__init__.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/base.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/tools/registry.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/utils.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/src/dotai/vet.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/__init__.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/conftest.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_converter.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_import_agent.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_models.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_preferences.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_roles.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_rules.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_skills.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_store.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_sync.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_utils.py +0 -0
- {dotai_cli-0.3.0 → dotai_cli-0.4.0}/tests/test_vet.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dotai-cli
|
|
3
|
-
Version: 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
|
-
|
|
32
|
+
**Your coding agents should remember what you taught them.**
|
|
33
33
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- Codex / Copilot → `AGENTS.md`
|
|
65
|
+
```bash
|
|
66
|
+
# Set up your global knowledge base
|
|
67
|
+
dotai init
|
|
42
68
|
|
|
43
|
-
|
|
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
|
-
|
|
75
|
+
# Bring that knowledge into a project
|
|
76
|
+
cd ~/my-project
|
|
77
|
+
dotai sync
|
|
78
|
+
```
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
Now the rule is available to every supported agent, alongside reusable commands such as:
|
|
48
81
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
3
|
+
**Your coding agents should remember what you taught them.**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Codex / Copilot → `AGENTS.md`
|
|
36
|
+
```bash
|
|
37
|
+
# Set up your global knowledge base
|
|
38
|
+
dotai init
|
|
13
39
|
|
|
14
|
-
|
|
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
|
-
|
|
46
|
+
# Bring that knowledge into a project
|
|
47
|
+
cd ~/my-project
|
|
48
|
+
dotai sync
|
|
49
|
+
```
|
|
17
50
|
|
|
18
|
-
|
|
51
|
+
Now the rule is available to every supported agent, alongside reusable commands such as:
|
|
19
52
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
@@ -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"),
|