@jmylchreest/aide-plugin 0.0.66 → 0.1.0
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/reflect/SKILL.md +289 -0
- package/skills/swarm/SKILL.md +26 -1
- package/skills/swarm-status/SKILL.md +107 -0
- package/src/core/context-guard.ts +2 -1
- package/src/core/mcp-sync.ts +5 -2
- package/src/core/read-tracking.ts +54 -31
- package/src/core/search-enrichment.ts +2 -1
- package/src/core/session-init.ts +74 -14
- package/src/core/skill-matcher.ts +18 -22
- package/src/core/tool-observe.ts +12 -0
- package/src/core/types.ts +15 -0
- package/src/hooks/agent-cleanup.ts +3 -1
- package/src/hooks/agent-signals.ts +249 -0
- package/src/hooks/comment-checker.ts +26 -0
- package/src/hooks/context-guard.ts +34 -5
- package/src/hooks/context-pruning.ts +27 -0
- package/src/hooks/pre-tool-enforcer.ts +23 -3
- package/src/hooks/reflect.ts +69 -0
- package/src/hooks/search-enrichment.ts +12 -4
- package/src/hooks/session-end.ts +35 -4
- package/src/hooks/session-start.ts +51 -41
- package/src/hooks/skill-injector.ts +42 -13
- package/src/hooks/subagent-tracker.ts +33 -3
- package/src/hooks/write-guard.ts +27 -4
- package/src/lib/hook-utils.ts +63 -0
- package/src/lib/hud.ts +5 -2
- package/src/lib/logger.ts +18 -6
- package/src/lib/project-root.ts +68 -31
- package/src/opencode/hooks.ts +46 -5
package/package.json
CHANGED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reflect
|
|
3
|
+
description: Run the instinct parser catalogue against this session's observe events to surface candidate patterns for promotion to memories. Two-pass: gather candidates, judge intent, write proposals.
|
|
4
|
+
triggers:
|
|
5
|
+
- reflect on this
|
|
6
|
+
- reflect on the session
|
|
7
|
+
- find instincts
|
|
8
|
+
- extract instincts
|
|
9
|
+
- propose instincts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Reflect
|
|
13
|
+
|
|
14
|
+
Extract candidate **instincts** — patterns repeatedly observed in this session
|
|
15
|
+
that might be worth promoting to durable memories.
|
|
16
|
+
|
|
17
|
+
## Your role as the agent running this skill
|
|
18
|
+
|
|
19
|
+
You **propose, the user approves**. Nothing this skill does writes a memory
|
|
20
|
+
or marks anything superseded without an explicit yes from the user.
|
|
21
|
+
|
|
22
|
+
Detectors emit proposals into a holding bucket (they're never auto-promoted
|
|
23
|
+
to memories). When this skill runs, your job is to make those proposals
|
|
24
|
+
*reviewable* — to add the judgement that mechanical matching can't:
|
|
25
|
+
|
|
26
|
+
1. **Classify intent** of user prompts in convergence windows — was the
|
|
27
|
+
user actually correcting the previous edit, or just commenting?
|
|
28
|
+
2. **Rewrite the content into a useful memory** — see below. This is the
|
|
29
|
+
most important step; the detector's content is a `[DRAFT — rewrite…]`
|
|
30
|
+
placeholder, never the finished memory.
|
|
31
|
+
3. **Judge semantic conflicts** — which existing memories (if any) does
|
|
32
|
+
this proposal supersede?
|
|
33
|
+
4. **Recommend an action** — accept (with rewritten content + supersedes),
|
|
34
|
+
reject (why), or leave open for the user to think about.
|
|
35
|
+
|
|
36
|
+
Then **stop and ask**. Surface each proposal to the user with your
|
|
37
|
+
recommendation. Wait for explicit approval before running
|
|
38
|
+
`aide reflect accept|reject`. The CLI is the write surface; your role is
|
|
39
|
+
to make the user's approval decision as well-informed as possible, not to
|
|
40
|
+
make it for them.
|
|
41
|
+
|
|
42
|
+
Concretely: do not chain "list proposals → accept proposals" in the same
|
|
43
|
+
turn. List, summarise, recommend, **wait**, then act on user instruction.
|
|
44
|
+
|
|
45
|
+
## Why rewriting is the default, not the exception
|
|
46
|
+
|
|
47
|
+
The detector emits an **observation** ("`cat` was run 5 times in 1 minute").
|
|
48
|
+
That's a structural signal, not a useful memory. A useful memory captures:
|
|
49
|
+
|
|
50
|
+
- **Why** the repetition / convergence happened (the underlying need or
|
|
51
|
+
mistake the agent kept circling around).
|
|
52
|
+
- **What** the canonical alternative is (a specific file path, command,
|
|
53
|
+
pattern, or piece of project knowledge).
|
|
54
|
+
- **Scope** (this codebase / this kind of task / this directory).
|
|
55
|
+
|
|
56
|
+
The agent reviewing the proposal does this synthesis by reading the
|
|
57
|
+
evidence snapshot. The detector can't — it only knows the count.
|
|
58
|
+
|
|
59
|
+
Bad memory (the raw template): *"In this project, `cat` is run repeatedly.
|
|
60
|
+
Cache its output."*
|
|
61
|
+
|
|
62
|
+
Good memory (after agent synthesis): *"When investigating the aide
|
|
63
|
+
README for plugin/config questions, the file is stable per commit and the
|
|
64
|
+
config table sits around lines 11-25 — inject the slice via Read with
|
|
65
|
+
offset/limit instead of re-`cat`-ing the whole file."*
|
|
66
|
+
|
|
67
|
+
The proposal's content field literally starts with `[DRAFT — rewrite with
|
|
68
|
+
concrete context before accepting]` to make this obvious. If you accept
|
|
69
|
+
without rewriting, you've stored noise.
|
|
70
|
+
|
|
71
|
+
## How this skill differs from the automatic Stop hook
|
|
72
|
+
|
|
73
|
+
- The **Stop hook** (`AIDE_REFLECT=1` env or `reflect.enabled=true` in
|
|
74
|
+
`.aide/config/aide.json`) runs `aide reflect run` automatically at session
|
|
75
|
+
end. Deterministic-only: marker matching for convergence, pure counting
|
|
76
|
+
for repetition. No supersession beyond structural `instinct_key:*` matches.
|
|
77
|
+
- **This skill** adds a second pass: it lists user-prompt candidates that
|
|
78
|
+
fall in convergence-relevant windows, you judge intent in-context, you
|
|
79
|
+
search for semantically conflicting memories, then you feed everything
|
|
80
|
+
back. Higher-quality output, no extra API cost — uses tokens you'd be
|
|
81
|
+
spending anyway.
|
|
82
|
+
|
|
83
|
+
## Session resolution
|
|
84
|
+
|
|
85
|
+
All commands below default to the **current session** — `aide` finds it by
|
|
86
|
+
checking, in order: an explicit `--session=<id>` flag, the `AIDE_SESSION_ID`
|
|
87
|
+
env var (set by OpenCode automatically; not by Claude Code), then the
|
|
88
|
+
session of the most recent observe event. Pass `--session=<id>` to target
|
|
89
|
+
a specific session, or run `./.aide/bin/aide reflect current-session` to
|
|
90
|
+
see what would be resolved.
|
|
91
|
+
|
|
92
|
+
## Steps
|
|
93
|
+
|
|
94
|
+
### 1. Get candidate prompts
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
./.aide/bin/aide reflect candidates
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Returns JSON like:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"session_id": "abc123",
|
|
105
|
+
"guidance": "For each prompt, judge whether it was correcting...",
|
|
106
|
+
"asks": ["intent"],
|
|
107
|
+
"prompts": [
|
|
108
|
+
{
|
|
109
|
+
"id": "01JF...A",
|
|
110
|
+
"timestamp": "...",
|
|
111
|
+
"text": "no don't add async — it should stay sync",
|
|
112
|
+
"preceding_edit": "Edit src/api/users.ts",
|
|
113
|
+
"following_edit": "Edit src/api/users.ts",
|
|
114
|
+
"file_path": "src/api/users.ts"
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If `prompts` is empty, skip to step 4 (deterministic run only).
|
|
121
|
+
|
|
122
|
+
### 2. Classify each prompt
|
|
123
|
+
|
|
124
|
+
For each prompt, judge its `intent` using the surrounding edit context:
|
|
125
|
+
|
|
126
|
+
- `corrective` — the user was redirecting the assistant's last action
|
|
127
|
+
- `positive` — the user was affirming the assistant's last action ("perfect", "ship it", "lgtm")
|
|
128
|
+
- `neutral` — neither corrective nor affirming (e.g. a new task, a question)
|
|
129
|
+
|
|
130
|
+
Build a JSON array:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
[
|
|
134
|
+
{"id": "01JF...A", "intent": "corrective", "confidence": 0.95},
|
|
135
|
+
{"id": "01JF...B", "intent": "neutral"}
|
|
136
|
+
]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. Run with classifications
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
./.aide/bin/aide reflect run \
|
|
143
|
+
--classifications-json='[{"id":"01JF...A","intent":"corrective"}]'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Returns a JSON summary: `{"proposals_written": N, "shapes": {...}}`.
|
|
147
|
+
|
|
148
|
+
### 4. (Deterministic fallback) Run without classifications
|
|
149
|
+
|
|
150
|
+
If step 1 returned no candidates, just run deterministically:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
./.aide/bin/aide reflect run
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 5. List proposals and summarise for the user
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
mcp__plugin_aide_aide__instinct_proposals_list { "status": "open" }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Summarise each new proposal's `summary` field for the user. Don't act on
|
|
163
|
+
anything yet — let them decide.
|
|
164
|
+
|
|
165
|
+
### 6. You decide what gets superseded
|
|
166
|
+
|
|
167
|
+
This is where your judgement matters most. The structural auto-supersession
|
|
168
|
+
(same `instinct_key:*` tag) only catches instinct-on-instinct dedup. The
|
|
169
|
+
interesting case is when a new instinct contradicts a **manually-set**
|
|
170
|
+
memory the user wrote earlier — e.g. a "documentation standard: always run
|
|
171
|
+
rustdoc" memory being superseded by a new "rustdoc runs repeatedly, cache
|
|
172
|
+
it" instinct.
|
|
173
|
+
|
|
174
|
+
Mechanical matching can't catch that. You can. Process:
|
|
175
|
+
|
|
176
|
+
1. Extract the key subject from the proposal (e.g. "rustdoc", a file path,
|
|
177
|
+
a command name).
|
|
178
|
+
2. `mcp__plugin_aide_aide__memory_search { "query": "<subject>" }`
|
|
179
|
+
3. Read each result. Judge — **you are the judge**:
|
|
180
|
+
- Does the new instinct's guidance *contradict* this memory's guidance?
|
|
181
|
+
- Does it *replace* it with a better recommendation?
|
|
182
|
+
- Or does it just touch the same topic without conflicting?
|
|
183
|
+
4. Only collect IDs that genuinely conflict. False positives create silent
|
|
184
|
+
memory loss; bias toward "leave it" when uncertain.
|
|
185
|
+
5. Surface your reasoning to the user before acting: "I think proposal X
|
|
186
|
+
supersedes memory Y because Z — accept with `--supersedes=Y`?"
|
|
187
|
+
|
|
188
|
+
### 7. Rewrite the content, then present recommendations — do not auto-act
|
|
189
|
+
|
|
190
|
+
For each proposal:
|
|
191
|
+
|
|
192
|
+
1. **Read the evidence snapshot** via `mcp__plugin_aide_aide__instinct_inspect`.
|
|
193
|
+
2. **Synthesise the underlying lesson**. What was the agent (you, or a past
|
|
194
|
+
you) actually trying to achieve? Why did the pattern repeat / converge?
|
|
195
|
+
What's the canonical alternative for this codebase?
|
|
196
|
+
3. **Draft the rewritten memory content**. Skip the `[DRAFT — …]` template
|
|
197
|
+
text from the proposal; write a useful instinct from scratch.
|
|
198
|
+
4. **Present to the user** with the rewrite inline:
|
|
199
|
+
|
|
200
|
+
> Proposal `01JF…` (repetition, `rustdoc` × 7 in 5 min). Reading the
|
|
201
|
+
> evidence: 6 of the 7 calls were `rustdoc --no-deps` checking the same
|
|
202
|
+
> public crate while iterating on a doctest. I'd accept with this
|
|
203
|
+
> rewritten content and supersede memory `01JD…` ("always run rustdoc")
|
|
204
|
+
> because the new guidance refines, not contradicts, that one:
|
|
205
|
+
>
|
|
206
|
+
> > "When iterating on a single doctest, `cargo test --doc <module>`
|
|
207
|
+
> > re-runs only that doctest in ~1s; reserve `rustdoc --no-deps` for
|
|
208
|
+
> > final pre-commit verification across all crates."
|
|
209
|
+
>
|
|
210
|
+
> Command:
|
|
211
|
+
> `aide reflect accept 01JF… --supersedes=01JD… \
|
|
212
|
+
> --content="When iterating on a single doctest…"`
|
|
213
|
+
|
|
214
|
+
Then **wait**. The user might say:
|
|
215
|
+
- "yes" → run the command verbatim
|
|
216
|
+
- "yes but tweak the wording to …" → adjust `--content=` and re-present
|
|
217
|
+
- "accept but don't supersede" → drop `--supersedes`
|
|
218
|
+
- "reject — that's not actually a pattern, I asked you to repeat for testing"
|
|
219
|
+
→ run reject with that reason
|
|
220
|
+
- "leave them open, I'll review later" → do nothing, you're done
|
|
221
|
+
|
|
222
|
+
If the evidence doesn't support a meaningful rewrite — e.g. the events are
|
|
223
|
+
a test trigger, a one-off, a data artifact — **recommend reject**. A
|
|
224
|
+
proposal with no useful synthesis is noise; promoting it pollutes the
|
|
225
|
+
memory store.
|
|
226
|
+
|
|
227
|
+
### 8. Execute the user's decision via the CLI
|
|
228
|
+
|
|
229
|
+
Only after the user has chosen — writes are CLI-only per aide convention:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Accept with rewritten content (the default — see step 7):
|
|
233
|
+
./.aide/bin/aide reflect accept <proposal-id> --content="<your synthesised memory>"
|
|
234
|
+
|
|
235
|
+
# Accept with rewritten content AND supersession:
|
|
236
|
+
./.aide/bin/aide reflect accept <proposal-id> \
|
|
237
|
+
--content="<your synthesis>" \
|
|
238
|
+
--supersedes=<mem-id1>,<mem-id2>
|
|
239
|
+
|
|
240
|
+
# Accept verbatim (the [DRAFT…] template lands as the memory — almost
|
|
241
|
+
# never what you want, included only for completeness):
|
|
242
|
+
./.aide/bin/aide reflect accept <proposal-id>
|
|
243
|
+
|
|
244
|
+
# Reject (with reason for the audit trail):
|
|
245
|
+
./.aide/bin/aide reflect reject <proposal-id> --reason="not useful"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Supersession unions two sources:
|
|
249
|
+
|
|
250
|
+
1. **Structural (auto)** — instinct memories sharing the same
|
|
251
|
+
`instinct_key:*` tag (cheap dedup; same Bash command for repetition,
|
|
252
|
+
same file path for convergence).
|
|
253
|
+
2. **Semantic (via `--supersedes`)** — IDs from step 6. Works for any
|
|
254
|
+
memory including manually-set ones with no instinct tags.
|
|
255
|
+
|
|
256
|
+
Each superseded record gets `superseded` + `superseded_by:<new-id>` tags;
|
|
257
|
+
the new memory gets `supersedes:<csv>` pointing back. Superseded records
|
|
258
|
+
stay in the bucket for audit but are filtered out of default queries.
|
|
259
|
+
|
|
260
|
+
## Inspecting evidence
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
mcp__plugin_aide_aide__instinct_inspect { "id": "<ulid>" }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The `evidence.snapshot` array contains the observe events that triggered the
|
|
267
|
+
pattern.
|
|
268
|
+
|
|
269
|
+
## Opt-in toggle for the Stop hook
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
export AIDE_REFLECT=1 # also: true, on, yes — any truthy value
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
When unset or set to a falsy value (`0`, `false`, `off`, `no`), the Stop hook
|
|
276
|
+
is a no-op. This skill still works regardless — it's manually invoked, not
|
|
277
|
+
gated by the env var.
|
|
278
|
+
|
|
279
|
+
## Shape catalogue
|
|
280
|
+
|
|
281
|
+
- **repetition** — Bash commands run > N times in a session, suggesting the
|
|
282
|
+
agent forgot it already had the answer. Pure counting, no LLM input needed.
|
|
283
|
+
- **convergence** — `Edit A` → user corrective marker → `Edit B` on the same
|
|
284
|
+
file → optional positive signal. Marker-based by default, upgrades to
|
|
285
|
+
LLM-classified when intent labels are provided via step 3.
|
|
286
|
+
|
|
287
|
+
Detectors declare a `RequiresLLM` capability. The CLI Stop hook
|
|
288
|
+
(`aide reflect run`) runs only `RequiresLLM=false` detectors automatically;
|
|
289
|
+
LLM-required detectors are skipped unless this skill provides classifications.
|
package/skills/swarm/SKILL.md
CHANGED
|
@@ -6,6 +6,10 @@ triggers:
|
|
|
6
6
|
- parallel agents
|
|
7
7
|
- spawn agents
|
|
8
8
|
- multi-agent
|
|
9
|
+
- halt agent
|
|
10
|
+
- pause swarm
|
|
11
|
+
- stop agent
|
|
12
|
+
- resume agent
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
# Swarm Mode
|
|
@@ -221,7 +225,28 @@ When all 5 stages are complete:
|
|
|
221
225
|
});
|
|
222
226
|
```
|
|
223
227
|
|
|
224
|
-
###
|
|
228
|
+
### Mid-flight control
|
|
229
|
+
|
|
230
|
+
Once stories are launched, you can intervene without killing/restarting agents.
|
|
231
|
+
All control writes go through `aide agent` and reach the subagent on its
|
|
232
|
+
**next tool call** (worst case ≈ duration of an in-flight Bash/Edit). The
|
|
233
|
+
signal hook (`src/hooks/agent-signals.ts`) gates on a subagent's
|
|
234
|
+
`parent_session` being set — orchestrator/solo sessions see zero overhead.
|
|
235
|
+
|
|
236
|
+
- **Inspect**: `./.aide/bin/aide agent list --parent=$(./.aide/bin/aide reflect current-session) --json`
|
|
237
|
+
- **Halt cleanly**: `./.aide/bin/aide agent halt <agent-id> --reason="repeated rustdoc — see new instinct"`
|
|
238
|
+
Next tool call is blocked with the reason surfaced to the model.
|
|
239
|
+
- **Pause / resume**: `./.aide/bin/aide agent pause <agent-id>` then `./.aide/bin/aide agent resume <agent-id>`.
|
|
240
|
+
Paused agents can only call `message_send`/`message_list`/`message_ack`/`state_get`.
|
|
241
|
+
- **Mid-flight instruction**: `./.aide/bin/aide message send --from=orchestrator --to=<agent-id> --priority=high "scope drifted — focus on auth.ts only"`.
|
|
242
|
+
Surfaced as `additionalContext` on the subagent's next tool call.
|
|
243
|
+
- **Soft deadline**: `./.aide/bin/aide agent deadline <agent-id> 30m` — warns at < 5min remaining; halts at 0.
|
|
244
|
+
|
|
245
|
+
When to intervene vs. let it run: prefer letting agents finish a stage and
|
|
246
|
+
review at the next checkpoint. Use mid-flight halt only for clearly-wrong
|
|
247
|
+
directions (infinite loops, scope drift, depleted budget with no progress).
|
|
248
|
+
|
|
249
|
+
### Monitor Progress
|
|
225
250
|
|
|
226
251
|
Use TaskList to see all story progress:
|
|
227
252
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swarm-status
|
|
3
|
+
description: Inspect a running swarm — show the agent tree, current tools, halts/pauses, and recent task/message activity for the orchestrator's own swarm.
|
|
4
|
+
triggers:
|
|
5
|
+
- swarm status
|
|
6
|
+
- what are the agents doing
|
|
7
|
+
- show swarm
|
|
8
|
+
- agent status
|
|
9
|
+
- check agents
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Swarm Status
|
|
13
|
+
|
|
14
|
+
Quick orchestrator-side inspection of a live swarm. Run this when you want
|
|
15
|
+
to see what your spawned subagents are doing right now without halting them
|
|
16
|
+
or opening the aide-web dashboard.
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
|
|
20
|
+
### 1. Resolve current session (your own)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
./.aide/bin/aide reflect current-session
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Use that ID as `<parent>` below — it's the orchestrator session that owns
|
|
27
|
+
the swarm.
|
|
28
|
+
|
|
29
|
+
### 2. List agents in this swarm
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
./.aide/bin/aide agent list --parent=<parent> --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Returns one record per registered subagent with `parent_session`,
|
|
36
|
+
`namespace`, `status`, `halt`, `paused`, `deadline`.
|
|
37
|
+
|
|
38
|
+
### 3. Active tasks for this swarm
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./.aide/bin/aide task list --parent-session=<parent> --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Filters the project-wide task bucket to ones tagged with this swarm's
|
|
45
|
+
parent. Look for tasks stuck in `claimed` without progress.
|
|
46
|
+
|
|
47
|
+
### 4. Recent messages
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
./.aide/bin/aide message list --parent-session=<parent>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Cross-agent comms within this swarm. High-priority messages from the
|
|
54
|
+
orchestrator are surfaced to subagents on their next tool call by the
|
|
55
|
+
signals hook.
|
|
56
|
+
|
|
57
|
+
### 5. Summarise for the user
|
|
58
|
+
|
|
59
|
+
Group by agent. For each, report:
|
|
60
|
+
|
|
61
|
+
- Status (running / paused / halted)
|
|
62
|
+
- Current tool (read `agent:<id>:currentTool` via `aide state get currentTool --agent=<id>` if you want this)
|
|
63
|
+
- Any flags (halt with reason, pause)
|
|
64
|
+
- Open tasks they hold
|
|
65
|
+
- Deadline if set
|
|
66
|
+
|
|
67
|
+
Example output:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Swarm <parent-id-short> — 3 agents
|
|
71
|
+
|
|
72
|
+
agent-auth (running):
|
|
73
|
+
current: Edit src/auth/handler.ts
|
|
74
|
+
tasks: 2 in_progress
|
|
75
|
+
no flags
|
|
76
|
+
|
|
77
|
+
agent-payments (paused):
|
|
78
|
+
reason: scope drift — investigating
|
|
79
|
+
tasks: 1 claimed (#42)
|
|
80
|
+
paused 3m ago
|
|
81
|
+
|
|
82
|
+
agent-docs (halted):
|
|
83
|
+
reason: repeated rustdoc — see new instinct
|
|
84
|
+
halted 12m ago
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### When to use halts vs. messages
|
|
88
|
+
|
|
89
|
+
- **Halt** if the agent is clearly off-track or burning budget with no
|
|
90
|
+
progress. The halt blocks tool calls; the model will respond once with
|
|
91
|
+
the halt reason then stop.
|
|
92
|
+
- **High-priority message** if you want to redirect without stopping —
|
|
93
|
+
arrives as `additionalContext` on the next tool call so the model can
|
|
94
|
+
read it and adjust.
|
|
95
|
+
|
|
96
|
+
Both are sent via `aide` CLI:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
./.aide/bin/aide agent halt <agent-id> --reason="..."
|
|
100
|
+
./.aide/bin/aide message send --from=orchestrator --to=<agent-id> \
|
|
101
|
+
--priority=high "redirect: focus on auth.ts only"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Not for
|
|
105
|
+
|
|
106
|
+
This skill is read-only orchestration. Use the `swarm` skill itself to
|
|
107
|
+
launch new agents or resolve worktrees at the end.
|
|
@@ -19,6 +19,7 @@ import { resolve, isAbsolute, normalize, extname } from "path";
|
|
|
19
19
|
import { tmpdir } from "os";
|
|
20
20
|
import { join } from "path";
|
|
21
21
|
import { debug } from "../lib/logger.js";
|
|
22
|
+
import { isTruthy } from "../lib/hook-utils.js";
|
|
22
23
|
import { getPreviousRead, checkFileReadFreshness } from "./read-tracking.js";
|
|
23
24
|
|
|
24
25
|
const SOURCE = "context-guard";
|
|
@@ -248,7 +249,7 @@ export function checkSmartReadHint(
|
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
// Require code watcher to be enabled
|
|
251
|
-
if (process.env.AIDE_CODE_WATCH
|
|
252
|
+
if (!isTruthy(process.env.AIDE_CODE_WATCH)) {
|
|
252
253
|
return { shouldHint: false };
|
|
253
254
|
}
|
|
254
255
|
|
package/src/core/mcp-sync.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { join, dirname } from "path";
|
|
27
27
|
import { homedir } from "os";
|
|
28
28
|
import * as TOML from "smol-toml";
|
|
29
|
+
import { findProjectRoot } from "../lib/project-root.js";
|
|
29
30
|
|
|
30
31
|
// =============================================================================
|
|
31
32
|
// Types
|
|
@@ -178,11 +179,13 @@ function aideUserMcpPath(): string {
|
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
function aideProjectMcpPath(cwd: string): string {
|
|
181
|
-
|
|
182
|
+
const { root } = findProjectRoot(cwd);
|
|
183
|
+
return join(root, ".aide", "config", "mcp.json");
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
function journalPath(cwd: string): string {
|
|
185
|
-
|
|
187
|
+
const { root } = findProjectRoot(cwd);
|
|
188
|
+
return join(root, ".aide", "config", "mcp-sync.journal.json");
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
function userJournalPath(): string {
|
|
@@ -13,6 +13,7 @@ import { execFileSync } from "child_process";
|
|
|
13
13
|
import { isAbsolute, relative, resolve } from "path";
|
|
14
14
|
import { setState, getState } from "./aide-client.js";
|
|
15
15
|
import { debug } from "../lib/logger.js";
|
|
16
|
+
import { isTruthy } from "../lib/hook-utils.js";
|
|
16
17
|
|
|
17
18
|
const SOURCE = "read-tracking";
|
|
18
19
|
|
|
@@ -50,7 +51,7 @@ export function recordFileRead(
|
|
|
50
51
|
cwd: string,
|
|
51
52
|
filePath: string,
|
|
52
53
|
): void {
|
|
53
|
-
if (process.env.AIDE_CODE_WATCH
|
|
54
|
+
if (!isTruthy(process.env.AIDE_CODE_WATCH)) return;
|
|
54
55
|
|
|
55
56
|
try {
|
|
56
57
|
const relPath = toRelativePath(cwd, filePath);
|
|
@@ -73,7 +74,7 @@ export function getPreviousRead(
|
|
|
73
74
|
cwd: string,
|
|
74
75
|
filePath: string,
|
|
75
76
|
): string | null {
|
|
76
|
-
if (process.env.AIDE_CODE_WATCH
|
|
77
|
+
if (!isTruthy(process.env.AIDE_CODE_WATCH)) return null;
|
|
77
78
|
|
|
78
79
|
try {
|
|
79
80
|
const relPath = toRelativePath(cwd, filePath);
|
|
@@ -113,39 +114,17 @@ export function checkFileReadFreshness(
|
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
export function recordTokenEvent(
|
|
121
|
-
binary: string,
|
|
122
|
-
cwd: string,
|
|
123
|
-
eventType: string,
|
|
124
|
-
tool: string,
|
|
125
|
-
filePath: string,
|
|
126
|
-
tokens: number,
|
|
127
|
-
tokensSaved: number = 0,
|
|
128
|
-
): void {
|
|
129
|
-
try {
|
|
130
|
-
const args = ["token", "record", eventType, tool, filePath, String(tokens)];
|
|
131
|
-
if (tokensSaved > 0) {
|
|
132
|
-
args.push(String(tokensSaved));
|
|
133
|
-
}
|
|
134
|
-
execFileSync(binary, args, {
|
|
135
|
-
cwd,
|
|
136
|
-
timeout: 3000,
|
|
137
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
138
|
-
});
|
|
139
|
-
debug(SOURCE, `Token event: ${eventType} ${tool} ${filePath} tokens=${tokens} saved=${tokensSaved}`);
|
|
140
|
-
} catch (err) {
|
|
141
|
-
debug(SOURCE, `Failed to record token event: ${err}`);
|
|
142
|
-
}
|
|
117
|
+
export function previewContent(text: string, maxChars = 300): string {
|
|
118
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
119
|
+
if (collapsed.length <= maxChars) return collapsed;
|
|
120
|
+
return collapsed.slice(0, maxChars - 1) + "…";
|
|
143
121
|
}
|
|
144
122
|
|
|
145
123
|
/**
|
|
146
124
|
* Record an arbitrary observe event via `aide observe record`.
|
|
147
|
-
*
|
|
148
|
-
*
|
|
125
|
+
* Prefer `emitInjectionEvent` for `kind=injection` callers — this raw
|
|
126
|
+
* recorder is reserved for non-injection kinds (e.g. `hook` user_prompt
|
|
127
|
+
* events). Fire-and-forget.
|
|
149
128
|
*/
|
|
150
129
|
export function recordObserveEvent(
|
|
151
130
|
binary: string,
|
|
@@ -183,3 +162,47 @@ export function recordObserveEvent(
|
|
|
183
162
|
debug(SOURCE, `Failed to record observe event: ${err}`);
|
|
184
163
|
}
|
|
185
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Emit a `kind=injection` observe event for any hook that pushes
|
|
168
|
+
* `additionalContext` back to the harness. Centralises field naming so the
|
|
169
|
+
* Injections page can group/colour consistently.
|
|
170
|
+
*
|
|
171
|
+
* `subtype` should come from a small fixed taxonomy:
|
|
172
|
+
* memory | decision | session_memory | skill | enrichment | guard |
|
|
173
|
+
* signal | pruning
|
|
174
|
+
*
|
|
175
|
+
* `source` is the emitting hook name (e.g. "search-enrichment"); it lands in
|
|
176
|
+
* both `file` and `name` so the UI can show "who injected this" without
|
|
177
|
+
* forcing every caller to invent a unique `name`.
|
|
178
|
+
*
|
|
179
|
+
* Fire-and-forget; failures are logged at debug level and never thrown.
|
|
180
|
+
*/
|
|
181
|
+
export function emitInjectionEvent(
|
|
182
|
+
binary: string,
|
|
183
|
+
cwd: string,
|
|
184
|
+
opts: {
|
|
185
|
+
source: string;
|
|
186
|
+
subtype: string;
|
|
187
|
+
content: string;
|
|
188
|
+
sessionId?: string;
|
|
189
|
+
name?: string;
|
|
190
|
+
attrs?: Record<string, string>;
|
|
191
|
+
},
|
|
192
|
+
): void {
|
|
193
|
+
const baseAttrs: Record<string, string> = {
|
|
194
|
+
source_id: opts.source,
|
|
195
|
+
source_kind: opts.subtype,
|
|
196
|
+
content_preview: previewContent(opts.content, 2000),
|
|
197
|
+
};
|
|
198
|
+
recordObserveEvent(binary, cwd, {
|
|
199
|
+
kind: "injection",
|
|
200
|
+
name: opts.name ?? opts.source,
|
|
201
|
+
category: "inject",
|
|
202
|
+
subtype: opts.subtype,
|
|
203
|
+
tokens: Math.round(opts.content.length / 3.0),
|
|
204
|
+
file: opts.source,
|
|
205
|
+
session: opts.sessionId,
|
|
206
|
+
attrs: { ...baseAttrs, ...(opts.attrs ?? {}) },
|
|
207
|
+
});
|
|
208
|
+
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import { execFileSync } from "child_process";
|
|
23
23
|
import { debug } from "../lib/logger.js";
|
|
24
|
+
import { isTruthy } from "../lib/hook-utils.js";
|
|
24
25
|
|
|
25
26
|
const SOURCE = "search-enrichment";
|
|
26
27
|
|
|
@@ -73,7 +74,7 @@ export function checkSearchEnrichment(
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
// Require code watcher to be enabled (implies code index exists)
|
|
76
|
-
if (process.env.AIDE_CODE_WATCH
|
|
77
|
+
if (!isTruthy(process.env.AIDE_CODE_WATCH)) {
|
|
77
78
|
return { shouldEnrich: false };
|
|
78
79
|
}
|
|
79
80
|
|