@jmylchreest/aide-plugin 0.0.40 → 0.0.43
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/package.json +1 -1
- package/skills/assess-findings/SKILL.md +167 -0
- package/skills/context-usage/SKILL.md +157 -0
- package/skills/patterns/SKILL.md +13 -8
- package/src/cli/config.ts +4 -1
- package/src/core/aide-client.ts +15 -12
- package/src/core/context-pruning/dedup.ts +173 -0
- package/src/core/context-pruning/index.ts +19 -0
- package/src/core/context-pruning/purge.ts +80 -0
- package/src/core/context-pruning/supersede.ts +67 -0
- package/src/core/context-pruning/tracker.ts +179 -0
- package/src/core/context-pruning/types.ts +63 -0
- package/src/core/mcp-sync.ts +19 -3
- package/src/core/partial-memory.ts +46 -55
- package/src/core/persistence-logic.ts +3 -1
- package/src/core/session-init.ts +22 -12
- package/src/core/session-summary-logic.ts +25 -15
- package/src/core/skill-matcher.ts +28 -8
- package/src/core/tool-tracking.ts +3 -5
- package/src/core/types.ts +4 -0
- package/src/lib/aide-downloader.ts +0 -8
- package/src/lib/hook-utils.ts +47 -115
- package/src/lib/hud.ts +2 -2
- package/src/lib/skills-registry.ts +32 -4
- package/src/lib/worktree.ts +11 -37
- package/src/opencode/hooks.ts +138 -12
- package/src/lib/aide-memory.ts +0 -400
package/package.json
CHANGED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: assess-findings
|
|
3
|
+
description: Triage static analysis findings, assess merit, and accept noise or irrelevant items
|
|
4
|
+
triggers:
|
|
5
|
+
- assess findings
|
|
6
|
+
- analyse findings
|
|
7
|
+
- analyze findings
|
|
8
|
+
- triage findings
|
|
9
|
+
- review findings
|
|
10
|
+
- accept findings
|
|
11
|
+
- dismiss findings
|
|
12
|
+
- clean up findings
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Assess Findings
|
|
16
|
+
|
|
17
|
+
**Recommended model tier:** balanced (sonnet) - this skill requires reading code and making judgement calls
|
|
18
|
+
|
|
19
|
+
Triage static analysis findings by reading the actual code, assessing whether each finding
|
|
20
|
+
is genuine or noise, and accepting (dismissing) irrelevant ones using `findings_accept`.
|
|
21
|
+
Accepted findings are hidden from future output by default.
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
- Findings must already exist. If `findings_stats` returns zero counts, tell the user to run:
|
|
26
|
+
```bash
|
|
27
|
+
./.aide/bin/aide findings run --path .
|
|
28
|
+
```
|
|
29
|
+
- The `findings_accept` tool must be available (provided by the aide MCP server).
|
|
30
|
+
|
|
31
|
+
## Available Tools
|
|
32
|
+
|
|
33
|
+
### Read-only (shared with `patterns` skill)
|
|
34
|
+
|
|
35
|
+
| Tool | Purpose |
|
|
36
|
+
| ----------------- | ------------------------------------------------------- |
|
|
37
|
+
| `findings_stats` | Counts by analyzer and severity — start here |
|
|
38
|
+
| `findings_list` | Browse findings with filters (analyzer, severity, file) |
|
|
39
|
+
| `findings_search` | Full-text search across finding titles and details |
|
|
40
|
+
|
|
41
|
+
### Write (unique to this skill)
|
|
42
|
+
|
|
43
|
+
| Tool | Purpose |
|
|
44
|
+
| ----------------- | --------------------------------------------------- |
|
|
45
|
+
| `findings_accept` | Mark findings as accepted/dismissed by ID or filter |
|
|
46
|
+
|
|
47
|
+
### Code inspection
|
|
48
|
+
|
|
49
|
+
| Tool | Purpose |
|
|
50
|
+
| -------------- | --------------------------------------------------- |
|
|
51
|
+
| `code_outline` | Get collapsed file structure to understand context |
|
|
52
|
+
| `Read` | Read specific line ranges to evaluate finding merit |
|
|
53
|
+
|
|
54
|
+
## Workflow
|
|
55
|
+
|
|
56
|
+
### 1. Get the Landscape
|
|
57
|
+
|
|
58
|
+
Call `findings_stats` to understand the scope:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
findings_stats
|
|
62
|
+
-> Returns: counts per analyzer (complexity, coupling, secrets, clones) and severity
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If the user asked to focus on a specific analyzer or severity, note that and filter accordingly.
|
|
66
|
+
Otherwise, work through all findings systematically.
|
|
67
|
+
|
|
68
|
+
### 2. Prioritise Review Order
|
|
69
|
+
|
|
70
|
+
Work through findings in this order:
|
|
71
|
+
|
|
72
|
+
1. **Secrets** (critical first) — these need immediate attention; false positives are common in test fixtures
|
|
73
|
+
2. **Complexity** (critical, then warning) — assess whether high complexity is inherent or decomposable
|
|
74
|
+
3. **Clones** (all) — determine if duplication is extractable or structural boilerplate
|
|
75
|
+
4. **Coupling** (all) — assess whether high fan-in/fan-out is expected for the file's role
|
|
76
|
+
|
|
77
|
+
### 3. Assess Each Finding
|
|
78
|
+
|
|
79
|
+
For each finding or group of related findings:
|
|
80
|
+
|
|
81
|
+
1. **Read the finding details** — note the file, line range, and metric values
|
|
82
|
+
2. **Read the actual code** — use `code_outline` first, then `Read` with offset/limit on the flagged section
|
|
83
|
+
3. **Make a judgement call** using these criteria:
|
|
84
|
+
|
|
85
|
+
#### Accept (dismiss) when:
|
|
86
|
+
|
|
87
|
+
- **Complexity**: The function is inherently complex (CLI dispatch, protocol handling, state machines) and cannot be meaningfully decomposed without harming readability
|
|
88
|
+
- **Clones**: The duplication is structural boilerplate (e.g., CLI subcommand wiring, store method patterns) where extraction would require framework-level abstraction
|
|
89
|
+
- **Coupling**: High fan-in/fan-out is expected for the file's architectural role (e.g., a main entry point, a facade, a registry)
|
|
90
|
+
- **Secrets**: The flagged string is a test fixture, example config, documentation placeholder, or env var name (not an actual secret)
|
|
91
|
+
|
|
92
|
+
#### Keep (do NOT accept) when:
|
|
93
|
+
|
|
94
|
+
- The finding points to a genuine problem that should be fixed
|
|
95
|
+
- Complexity can be reduced by extracting helper functions
|
|
96
|
+
- Duplication can be resolved by creating a shared utility
|
|
97
|
+
- A coupling cycle exists that indicates poor module boundaries
|
|
98
|
+
- A string looks like it could be a real secret or credential
|
|
99
|
+
|
|
100
|
+
### 4. Accept Findings
|
|
101
|
+
|
|
102
|
+
Use `findings_accept` to dismiss noise. You can accept:
|
|
103
|
+
|
|
104
|
+
- **By IDs** — for individual findings after assessment:
|
|
105
|
+
```
|
|
106
|
+
findings_accept ids=["finding-id-1", "finding-id-2"]
|
|
107
|
+
```
|
|
108
|
+
- **By filter** — for bulk dismissal of an entire category:
|
|
109
|
+
```
|
|
110
|
+
findings_accept analyzer="clones" file="cmd/"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Always explain **why** each finding is being accepted before calling the tool.
|
|
114
|
+
|
|
115
|
+
### 5. Report Summary
|
|
116
|
+
|
|
117
|
+
After completing the triage, produce a summary:
|
|
118
|
+
|
|
119
|
+
```markdown
|
|
120
|
+
## Findings Triage Summary
|
|
121
|
+
|
|
122
|
+
### Before
|
|
123
|
+
|
|
124
|
+
- Total: X findings (Y critical, Z warnings, W info)
|
|
125
|
+
|
|
126
|
+
### Accepted (Dismissed)
|
|
127
|
+
|
|
128
|
+
- N findings accepted as noise/irrelevant
|
|
129
|
+
- Complexity: X (inherent complexity in [files])
|
|
130
|
+
- Clones: Y (structural boilerplate in [area])
|
|
131
|
+
- Coupling: Z (expected for [role])
|
|
132
|
+
- Secrets: W (test fixtures / placeholders)
|
|
133
|
+
|
|
134
|
+
### Remaining (Genuine)
|
|
135
|
+
|
|
136
|
+
- M findings require attention
|
|
137
|
+
- [List each with file:line and brief description]
|
|
138
|
+
|
|
139
|
+
### Recommendations
|
|
140
|
+
|
|
141
|
+
1. [Prioritised action items for genuine findings]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Decision Criteria Reference
|
|
145
|
+
|
|
146
|
+
| Analyzer | Accept If | Keep If |
|
|
147
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
|
148
|
+
| complexity | Cyclomatic complexity is inherent to the problem domain; function handles unavoidable branching (CLI dispatch, protocol negotiation) | Function can be decomposed into smaller, testable units |
|
|
149
|
+
| clones | Duplication is cross-cutting boilerplate (CLI wiring, store CRUD patterns) | A shared utility or abstraction would reduce maintenance burden |
|
|
150
|
+
| coupling | File is an intentional integration point (main, facade, registry) | Circular dependencies or unexpected transitive coupling exists |
|
|
151
|
+
| secrets | Test fixture, documentation example, env var name, or placeholder | Looks like a real credential, API key, or connection string |
|
|
152
|
+
|
|
153
|
+
## Failure Handling
|
|
154
|
+
|
|
155
|
+
1. **No findings** — Tell user to run `./.aide/bin/aide findings run --path .` first
|
|
156
|
+
2. **`findings_accept` not available** — The aide MCP server may not expose this tool; tell the user to update aide
|
|
157
|
+
3. **Uncertain about a finding** — When in doubt, **keep it**. It's better to flag a false positive for human review than to dismiss a real issue
|
|
158
|
+
4. **Large number of findings** — Work in batches by analyzer. Accept obvious noise first, then do detailed code review for borderline cases
|
|
159
|
+
|
|
160
|
+
## Verification
|
|
161
|
+
|
|
162
|
+
- [ ] Called `findings_stats` for baseline counts
|
|
163
|
+
- [ ] Reviewed each finding category (secrets, complexity, clones, coupling)
|
|
164
|
+
- [ ] Read actual code for every finding before accepting
|
|
165
|
+
- [ ] Provided rationale for each acceptance
|
|
166
|
+
- [ ] Produced summary with before/after counts
|
|
167
|
+
- [ ] Remaining findings are genuinely actionable
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-usage
|
|
3
|
+
description: Analyze current session context and token usage from OpenCode SQLite database
|
|
4
|
+
platforms:
|
|
5
|
+
- opencode
|
|
6
|
+
triggers:
|
|
7
|
+
- context usage
|
|
8
|
+
- token usage
|
|
9
|
+
- session stats
|
|
10
|
+
- how much context
|
|
11
|
+
- context budget
|
|
12
|
+
- how big is this session
|
|
13
|
+
- session size
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Context Usage Analysis
|
|
17
|
+
|
|
18
|
+
**Recommended model tier:** balanced (sonnet) - straightforward SQL queries
|
|
19
|
+
|
|
20
|
+
Analyze the current session's context window consumption, tool usage breakdown,
|
|
21
|
+
and token costs by querying the OpenCode SQLite database directly.
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
- This skill **only works on OpenCode**. Verify by checking the environment:
|
|
26
|
+
- `$OPENCODE=1` — set by the OpenCode runtime
|
|
27
|
+
- `$AIDE_PLATFORM=opencode` — set by aide when running under OpenCode
|
|
28
|
+
- `$AIDE_SESSION_ID` — the current session ID (injected by aide)
|
|
29
|
+
- The OpenCode database is at `~/.local/share/opencode/opencode.db`.
|
|
30
|
+
- `sqlite3` must be available on the system.
|
|
31
|
+
|
|
32
|
+
If `$OPENCODE` is not `1` or `$AIDE_PLATFORM` is not `opencode`, abort immediately
|
|
33
|
+
and inform the user that this skill is only supported on OpenCode.
|
|
34
|
+
Do **not** attempt to query other databases (e.g. Claude Code's storage) — the
|
|
35
|
+
schema is OpenCode-specific.
|
|
36
|
+
|
|
37
|
+
If `$AIDE_SESSION_ID` is not set, abort with a message explaining that the
|
|
38
|
+
session ID could not be determined.
|
|
39
|
+
|
|
40
|
+
## Workflow
|
|
41
|
+
|
|
42
|
+
Run the following queries **sequentially** in a single Bash call (chain with `&&`).
|
|
43
|
+
Present results to the user in a formatted summary after all queries complete.
|
|
44
|
+
|
|
45
|
+
### Step 1: Validate environment
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
test "$OPENCODE" = "1" && echo "Platform: OpenCode" || echo "ERROR: Not running on OpenCode (OPENCODE=$OPENCODE)"
|
|
49
|
+
test "$AIDE_PLATFORM" = "opencode" && echo "AIDE Platform: opencode" || echo "WARNING: AIDE_PLATFORM=$AIDE_PLATFORM"
|
|
50
|
+
test -n "$AIDE_SESSION_ID" && echo "Session: $AIDE_SESSION_ID" || echo "ERROR: AIDE_SESSION_ID not set"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If `OPENCODE` is not `1`, stop immediately — this skill cannot work outside OpenCode.
|
|
54
|
+
If `AIDE_SESSION_ID` is not set, stop and inform the user.
|
|
55
|
+
|
|
56
|
+
### Step 2: Session overview
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
sqlite3 ~/.local/share/opencode/opencode.db "
|
|
60
|
+
SELECT
|
|
61
|
+
s.title,
|
|
62
|
+
s.slug,
|
|
63
|
+
ROUND((julianday('now') - julianday(datetime(s.time_created/1000, 'unixepoch'))) * 24, 1) as hours_old,
|
|
64
|
+
(SELECT COUNT(*) FROM message m WHERE m.session_id = s.id) as messages,
|
|
65
|
+
CASE WHEN s.time_compacting IS NOT NULL THEN 'yes' ELSE 'no' END as compacted
|
|
66
|
+
FROM session s
|
|
67
|
+
WHERE s.id = '$AIDE_SESSION_ID';
|
|
68
|
+
"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Step 3: Token totals
|
|
72
|
+
|
|
73
|
+
Sum tokens from `step-finish` parts (each represents one LLM turn):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sqlite3 ~/.local/share/opencode/opencode.db "
|
|
77
|
+
SELECT
|
|
78
|
+
SUM(json_extract(data, '$.tokens.input')) as input_tokens,
|
|
79
|
+
SUM(json_extract(data, '$.tokens.output')) as output_tokens,
|
|
80
|
+
SUM(json_extract(data, '$.tokens.cache.read')) as cache_read_tokens,
|
|
81
|
+
SUM(json_extract(data, '$.tokens.cache.write')) as cache_write_tokens,
|
|
82
|
+
SUM(json_extract(data, '$.tokens.total')) as total_tokens,
|
|
83
|
+
COUNT(*) as llm_turns
|
|
84
|
+
FROM part
|
|
85
|
+
WHERE session_id = '$AIDE_SESSION_ID'
|
|
86
|
+
AND json_extract(data, '$.type') = 'step-finish';
|
|
87
|
+
"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 4: Tool output breakdown
|
|
91
|
+
|
|
92
|
+
Show tool usage ranked by total output size:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
sqlite3 ~/.local/share/opencode/opencode.db "
|
|
96
|
+
SELECT
|
|
97
|
+
json_extract(data, '$.tool') as tool,
|
|
98
|
+
COUNT(*) as calls,
|
|
99
|
+
SUM(length(json_extract(data, '$.state.output'))) as total_output_bytes,
|
|
100
|
+
ROUND(AVG(length(json_extract(data, '$.state.output')))) as avg_bytes,
|
|
101
|
+
MAX(length(json_extract(data, '$.state.output'))) as max_bytes
|
|
102
|
+
FROM part
|
|
103
|
+
WHERE session_id = '$AIDE_SESSION_ID'
|
|
104
|
+
AND json_extract(data, '$.type') = 'tool'
|
|
105
|
+
GROUP BY tool
|
|
106
|
+
ORDER BY total_output_bytes DESC;
|
|
107
|
+
"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Step 5: Total session size
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
sqlite3 ~/.local/share/opencode/opencode.db "
|
|
114
|
+
SELECT
|
|
115
|
+
SUM(length(json_extract(data, '$.state.output'))) as tool_output_bytes,
|
|
116
|
+
SUM(length(json_extract(data, '$.state.input'))) as tool_input_bytes,
|
|
117
|
+
SUM(length(data)) as total_part_bytes
|
|
118
|
+
FROM part
|
|
119
|
+
WHERE session_id = '$AIDE_SESSION_ID'
|
|
120
|
+
AND json_extract(data, '$.type') = 'tool';
|
|
121
|
+
"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Output Format
|
|
125
|
+
|
|
126
|
+
Present the results as a structured summary:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
## Session Context Usage
|
|
130
|
+
|
|
131
|
+
**Session:** <title> (<slug>)
|
|
132
|
+
**Age:** <hours> hours | **Messages:** <count> | **Compacted:** yes/no
|
|
133
|
+
|
|
134
|
+
### Token Usage
|
|
135
|
+
| Metric | Count |
|
|
136
|
+
|--------|-------|
|
|
137
|
+
| Input tokens | <n> |
|
|
138
|
+
| Output tokens | <n> |
|
|
139
|
+
| Cache read | <n> |
|
|
140
|
+
| Cache write | <n> |
|
|
141
|
+
| **Total tokens** | **<n>** |
|
|
142
|
+
| LLM turns | <n> |
|
|
143
|
+
|
|
144
|
+
### Tool Output Breakdown (by total bytes)
|
|
145
|
+
| Tool | Calls | Total Output | Avg/call | Max |
|
|
146
|
+
|------|-------|-------------|----------|-----|
|
|
147
|
+
| ... | ... | ... | ... | ... |
|
|
148
|
+
|
|
149
|
+
### Session Size
|
|
150
|
+
- Tool outputs: <n> KB
|
|
151
|
+
- Tool inputs: <n> KB
|
|
152
|
+
- Total part storage: <n> KB (includes JSON metadata overhead)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Format byte values as KB (divide by 1024, round to 1 decimal).
|
|
156
|
+
Highlight the top 3 tools by total output as the biggest context consumers.
|
|
157
|
+
If any single tool call exceeds 20KB, flag it as a potential optimization target.
|
package/skills/patterns/SKILL.md
CHANGED
|
@@ -10,7 +10,6 @@ triggers:
|
|
|
10
10
|
- clones
|
|
11
11
|
- secrets
|
|
12
12
|
- coupling
|
|
13
|
-
- findings
|
|
14
13
|
- static analysis
|
|
15
14
|
- code health
|
|
16
15
|
---
|
|
@@ -47,6 +46,7 @@ Findings must be generated first by running analyzers via the CLI:
|
|
|
47
46
|
Full-text search across all findings. Supports Bleve query syntax for advanced searches.
|
|
48
47
|
|
|
49
48
|
**Parameters:**
|
|
49
|
+
|
|
50
50
|
- `query` (required) — Search term or Bleve query
|
|
51
51
|
- `analyzer` (optional) — Filter to one analyzer: `complexity`, `coupling`, `secrets`, `clones`
|
|
52
52
|
- `severity` (optional) — Filter by severity: `info`, `warning`, `critical`
|
|
@@ -66,6 +66,7 @@ Search for: "high complexity"
|
|
|
66
66
|
List findings with filters. Use when you want to browse rather than search.
|
|
67
67
|
|
|
68
68
|
**Parameters:**
|
|
69
|
+
|
|
69
70
|
- `analyzer` (optional) — Filter to one analyzer
|
|
70
71
|
- `severity` (optional) — Filter by severity
|
|
71
72
|
- `file` (optional) — Filter by file path substring
|
|
@@ -133,13 +134,13 @@ How healthy is the codebase?
|
|
|
133
134
|
|
|
134
135
|
Beyond the automated analyzers, look for these patterns using findings as starting points:
|
|
135
136
|
|
|
136
|
-
| Finding
|
|
137
|
-
|
|
138
|
-
| Complexity > 20 | God function
|
|
139
|
-
| Fan-out > 15
|
|
140
|
-
| Fan-in > 20
|
|
141
|
-
| Multiple clones | Copy-paste programming | Extract shared utility
|
|
142
|
-
| Import cycle
|
|
137
|
+
| Finding | Likely Anti-Pattern | Action |
|
|
138
|
+
| --------------- | ---------------------- | -------------------------------- |
|
|
139
|
+
| Complexity > 20 | God function | Decompose into smaller functions |
|
|
140
|
+
| Fan-out > 15 | Kitchen sink module | Split responsibilities |
|
|
141
|
+
| Fan-in > 20 | Fragile dependency | Consider interface/abstraction |
|
|
142
|
+
| Multiple clones | Copy-paste programming | Extract shared utility |
|
|
143
|
+
| Import cycle | Circular dependency | Restructure module boundaries |
|
|
143
144
|
|
|
144
145
|
## Output Format
|
|
145
146
|
|
|
@@ -147,18 +148,22 @@ Beyond the automated analyzers, look for these patterns using findings as starti
|
|
|
147
148
|
## Code Health Report
|
|
148
149
|
|
|
149
150
|
### Overview
|
|
151
|
+
|
|
150
152
|
- Total findings: X (Y critical, Z warnings)
|
|
151
153
|
- Top concern: [area/file with most issues]
|
|
152
154
|
|
|
153
155
|
### Hotspots
|
|
156
|
+
|
|
154
157
|
1. **`file:line`** - [description] (severity)
|
|
155
158
|
- Impact: [why this matters]
|
|
156
159
|
- Recommendation: [what to do]
|
|
157
160
|
|
|
158
161
|
### Patterns Detected
|
|
162
|
+
|
|
159
163
|
- [List of anti-patterns found with evidence]
|
|
160
164
|
|
|
161
165
|
### Recommendations
|
|
166
|
+
|
|
162
167
|
1. [Prioritized action items]
|
|
163
168
|
```
|
|
164
169
|
|
package/src/cli/config.ts
CHANGED
|
@@ -51,7 +51,10 @@ export function readConfig(configPath: string): OpenCodeConfig {
|
|
|
51
51
|
}
|
|
52
52
|
try {
|
|
53
53
|
const raw = readFileSync(configPath, "utf-8");
|
|
54
|
-
|
|
54
|
+
const parsed: unknown = JSON.parse(raw);
|
|
55
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
56
|
+
return {};
|
|
57
|
+
return parsed as OpenCodeConfig;
|
|
55
58
|
} catch {
|
|
56
59
|
return {};
|
|
57
60
|
}
|
package/src/core/aide-client.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* platform-specific options instead of relying on CLAUDE_PLUGIN_ROOT.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
12
|
import { existsSync, realpathSync } from "fs";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
import type { FindBinaryOptions } from "./types.js";
|
|
@@ -64,7 +64,10 @@ export function findAideBinary(opts: FindBinaryOptions = {}): string | null {
|
|
|
64
64
|
|
|
65
65
|
// 4. PATH fallback
|
|
66
66
|
try {
|
|
67
|
-
const result =
|
|
67
|
+
const result = execFileSync("which", ["aide"], {
|
|
68
|
+
stdio: "pipe",
|
|
69
|
+
timeout: 2000,
|
|
70
|
+
})
|
|
68
71
|
.toString()
|
|
69
72
|
.trim();
|
|
70
73
|
if (result) return result;
|
|
@@ -187,15 +190,15 @@ export function clearAgentState(
|
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
/**
|
|
190
|
-
*
|
|
193
|
+
* Sanitize a string for safe inclusion in log messages and CLI arguments.
|
|
194
|
+
*
|
|
195
|
+
* Strips control characters and limits length. This is NOT shell escaping —
|
|
196
|
+
* use execFileSync (which avoids shells entirely) for subprocess execution.
|
|
191
197
|
*/
|
|
192
|
-
export function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.replace(/"/g, '\\"')
|
|
196
|
-
.replace(/\$/g, "\\$")
|
|
197
|
-
.replace(/`/g, "\\`")
|
|
198
|
-
.replace(/!/g, "\\!")
|
|
199
|
-
.replace(/\n/g, " ")
|
|
200
|
-
.slice(0, 1000);
|
|
198
|
+
export function sanitizeForLog(str: string): string {
|
|
199
|
+
// eslint-disable-next-line no-control-regex
|
|
200
|
+
return str.replace(/[\x00-\x1f\x7f]/g, " ").slice(0, 1000);
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
/** @deprecated Use sanitizeForLog instead */
|
|
204
|
+
export const shellEscape = sanitizeForLog;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedup strategy: replace repeated identical tool outputs with a short pointer.
|
|
3
|
+
*
|
|
4
|
+
* Safe-to-dedup tools: Read (with mtime check), Glob, Grep, and aide MCP tools
|
|
5
|
+
* like code_search, code_symbols, code_outline, code_references,
|
|
6
|
+
* findings_list, findings_search, memory_list, memory_search.
|
|
7
|
+
*
|
|
8
|
+
* NEVER dedup: Bash, Write, Edit, or any tool with side effects.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PruneResult, PruneStrategy, ToolRecord } from "./types.js";
|
|
12
|
+
import { statSync } from "fs";
|
|
13
|
+
import { resolve, isAbsolute } from "path";
|
|
14
|
+
|
|
15
|
+
/** Tools that are safe to deduplicate. */
|
|
16
|
+
const SAFE_DEDUP_TOOLS = new Set([
|
|
17
|
+
// Host built-in read-only tools
|
|
18
|
+
"read",
|
|
19
|
+
"glob",
|
|
20
|
+
"grep",
|
|
21
|
+
// aide MCP tools (read-only)
|
|
22
|
+
"mcp__aide__code_search",
|
|
23
|
+
"mcp__aide__code_symbols",
|
|
24
|
+
"mcp__aide__code_outline",
|
|
25
|
+
"mcp__aide__code_references",
|
|
26
|
+
"mcp__aide__code_stats",
|
|
27
|
+
"mcp__aide__findings_list",
|
|
28
|
+
"mcp__aide__findings_search",
|
|
29
|
+
"mcp__aide__findings_stats",
|
|
30
|
+
"mcp__aide__memory_list",
|
|
31
|
+
"mcp__aide__memory_search",
|
|
32
|
+
"mcp__aide__decision_list",
|
|
33
|
+
"mcp__aide__decision_get",
|
|
34
|
+
"mcp__aide__decision_history",
|
|
35
|
+
"mcp__aide__state_get",
|
|
36
|
+
"mcp__aide__state_list",
|
|
37
|
+
"mcp__aide__task_list",
|
|
38
|
+
"mcp__aide__task_get",
|
|
39
|
+
"mcp__aide__message_list",
|
|
40
|
+
// Claude Code naming convention (no mcp__ prefix)
|
|
41
|
+
"code_search",
|
|
42
|
+
"code_symbols",
|
|
43
|
+
"code_outline",
|
|
44
|
+
"code_references",
|
|
45
|
+
"code_stats",
|
|
46
|
+
"findings_list",
|
|
47
|
+
"findings_search",
|
|
48
|
+
"findings_stats",
|
|
49
|
+
"memory_list",
|
|
50
|
+
"memory_search",
|
|
51
|
+
"decision_list",
|
|
52
|
+
"decision_get",
|
|
53
|
+
"decision_history",
|
|
54
|
+
"state_get",
|
|
55
|
+
"state_list",
|
|
56
|
+
"task_list",
|
|
57
|
+
"task_get",
|
|
58
|
+
"message_list",
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
/** Extract the dedup key from tool args (the args that define "same call"). */
|
|
62
|
+
function dedupKey(toolName: string, args: Record<string, unknown>): string {
|
|
63
|
+
const normalized = toolName.toLowerCase();
|
|
64
|
+
// For Read, the key is filePath + offset + limit
|
|
65
|
+
if (normalized === "read") {
|
|
66
|
+
return JSON.stringify({
|
|
67
|
+
tool: "read",
|
|
68
|
+
filePath: args.filePath ?? args.file_path ?? args.path,
|
|
69
|
+
offset: args.offset ?? 0,
|
|
70
|
+
limit: args.limit ?? 2000,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// For Glob, the key is pattern + path
|
|
74
|
+
if (normalized === "glob") {
|
|
75
|
+
return JSON.stringify({
|
|
76
|
+
tool: "glob",
|
|
77
|
+
pattern: args.pattern,
|
|
78
|
+
path: args.path,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// For Grep, the key is pattern + path + include
|
|
82
|
+
if (normalized === "grep") {
|
|
83
|
+
return JSON.stringify({
|
|
84
|
+
tool: "grep",
|
|
85
|
+
pattern: args.pattern,
|
|
86
|
+
path: args.path,
|
|
87
|
+
include: args.include,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// For MCP tools, use all args as the key
|
|
91
|
+
return JSON.stringify({ tool: normalized, ...args });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Check file mtime for Read dedup safety. */
|
|
95
|
+
function getFileMtime(
|
|
96
|
+
args: Record<string, unknown>,
|
|
97
|
+
cwd?: string,
|
|
98
|
+
): number | undefined {
|
|
99
|
+
const filePath =
|
|
100
|
+
(args.filePath as string) ??
|
|
101
|
+
(args.file_path as string) ??
|
|
102
|
+
(args.path as string);
|
|
103
|
+
if (!filePath) return undefined;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const resolved = isAbsolute(filePath)
|
|
107
|
+
? filePath
|
|
108
|
+
: resolve(cwd || process.cwd(), filePath);
|
|
109
|
+
return statSync(resolved).mtimeMs;
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export class DedupStrategy implements PruneStrategy {
|
|
116
|
+
name = "dedup" as const;
|
|
117
|
+
private cwd?: string;
|
|
118
|
+
|
|
119
|
+
constructor(cwd?: string) {
|
|
120
|
+
this.cwd = cwd;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
apply(
|
|
124
|
+
toolName: string,
|
|
125
|
+
args: Record<string, unknown>,
|
|
126
|
+
output: string,
|
|
127
|
+
history: ToolRecord[],
|
|
128
|
+
): PruneResult {
|
|
129
|
+
const normalized = toolName.toLowerCase();
|
|
130
|
+
|
|
131
|
+
// Only apply to safe tools
|
|
132
|
+
if (!SAFE_DEDUP_TOOLS.has(normalized)) {
|
|
133
|
+
return { output, modified: false, bytesSaved: 0 };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const key = dedupKey(toolName, args);
|
|
137
|
+
|
|
138
|
+
// Find the most recent matching call in history
|
|
139
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
140
|
+
const prev = history[i];
|
|
141
|
+
const prevKey = dedupKey(prev.toolName, prev.args);
|
|
142
|
+
|
|
143
|
+
if (prevKey !== key) continue;
|
|
144
|
+
|
|
145
|
+
// For Read: check mtime hasn't changed (file might have been edited)
|
|
146
|
+
if (normalized === "read") {
|
|
147
|
+
const currentMtime = getFileMtime(args, this.cwd);
|
|
148
|
+
if (
|
|
149
|
+
currentMtime !== undefined &&
|
|
150
|
+
prev.fileMtime !== undefined &&
|
|
151
|
+
currentMtime !== prev.fileMtime
|
|
152
|
+
) {
|
|
153
|
+
// File changed — don't dedup
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check if output is identical
|
|
159
|
+
const prevOutput = prev.prunedOutput ?? prev.originalOutput;
|
|
160
|
+
if (output === prevOutput) {
|
|
161
|
+
const replacement = `[aide:dedup] Identical to previous ${toolName} call (callId: ${prev.callId}). Output unchanged.`;
|
|
162
|
+
return {
|
|
163
|
+
output: replacement,
|
|
164
|
+
modified: true,
|
|
165
|
+
strategy: "dedup",
|
|
166
|
+
bytesSaved: output.length - replacement.length,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { output, modified: false, bytesSaved: 0 };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Pruning — reduces context/token usage by deduplicating,
|
|
3
|
+
* superseding, and purging tool outputs.
|
|
4
|
+
*
|
|
5
|
+
* Platform adapters integrate via the ContextPruningTracker:
|
|
6
|
+
* - OpenCode: tool.execute.after hook modifies output.output
|
|
7
|
+
* - Claude Code: PostToolUse hook returns updatedMCPToolOutput
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { ContextPruningTracker } from "./tracker.js";
|
|
11
|
+
export { DedupStrategy } from "./dedup.js";
|
|
12
|
+
export { SupersedeStrategy } from "./supersede.js";
|
|
13
|
+
export { PurgeErrorsStrategy } from "./purge.js";
|
|
14
|
+
export type {
|
|
15
|
+
ToolRecord,
|
|
16
|
+
PruneResult,
|
|
17
|
+
PruneStrategy,
|
|
18
|
+
PruningStats,
|
|
19
|
+
} from "./types.js";
|