@jmylchreest/aide-plugin 0.0.38 → 0.0.40
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/code-search/SKILL.md +41 -1
- package/skills/debug/SKILL.md +40 -20
- package/skills/forget/SKILL.md +224 -0
- package/skills/git/SKILL.md +10 -0
- package/skills/implement/SKILL.md +10 -1
- package/skills/memorise/SKILL.md +89 -11
- package/skills/patterns/SKILL.md +177 -0
- package/skills/perf/SKILL.md +56 -32
- package/skills/plan-swarm/SKILL.md +5 -0
- package/skills/ralph/SKILL.md +15 -8
- package/skills/review/SKILL.md +53 -18
- package/skills/swarm/SKILL.md +75 -24
- package/skills/test/SKILL.md +38 -22
- package/src/core/context-guard.ts +214 -0
- package/src/core/persistence-logic.ts +26 -4
- package/src/core/todo-checker.ts +53 -18
- package/src/core/types.ts +21 -0
- package/src/opencode/hooks.ts +5 -1
- package/src/opencode/index.ts +3 -3
package/skills/swarm/SKILL.md
CHANGED
|
@@ -432,43 +432,90 @@ message_ack: message_id=42, agent_id="agent-auth"
|
|
|
432
432
|
./.aide/bin/aide memory add --category=discovery "User model needs email validation"
|
|
433
433
|
```
|
|
434
434
|
|
|
435
|
-
|
|
435
|
+
## OpenCode Mode
|
|
436
436
|
|
|
437
|
-
|
|
438
|
-
./.aide/bin/aide memory add --category=discovery "User model needs email validation"
|
|
439
|
-
```
|
|
437
|
+
OpenCode has native `todowrite`/`todoread` for per-agent progress tracking, and a `task` tool for spawning subagents. However, OpenCode's todos are **session-private** — they are NOT shared across agents. For multi-agent coordination, use **aide tasks** (MCP tools) as the shared task system.
|
|
440
438
|
|
|
441
|
-
|
|
439
|
+
### Task System Roles (OpenCode)
|
|
442
440
|
|
|
443
|
-
|
|
441
|
+
| System | Role | Scope |
|
|
442
|
+
| ------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------- |
|
|
443
|
+
| **aide tasks** (MCP: `task_create`, `task_list`, `task_claim`, `task_complete`) | Shared coordination — all agents see the same board | Cross-session, persistent |
|
|
444
|
+
| **todowrite** (native) | Personal progress tracking within each agent | Session-private, per-agent |
|
|
445
|
+
| **aide messages** (MCP: `message_send`, `message_list`) | Real-time coordination, status broadcasts, blockers | Cross-session |
|
|
444
446
|
|
|
445
|
-
|
|
447
|
+
### Setup
|
|
446
448
|
|
|
447
449
|
1. Create worktrees as normal (one per story)
|
|
448
450
|
2. Launch separate OpenCode terminal sessions, one per story
|
|
449
451
|
3. Each session works in its assigned worktree directory
|
|
450
452
|
|
|
451
|
-
|
|
453
|
+
### Orchestrator Workflow
|
|
452
454
|
|
|
453
|
-
|
|
454
|
-
- **Use aide messages** as the primary coordination mechanism:
|
|
455
|
-
- Each session uses `message_send` to report status, blockers, and completion
|
|
456
|
-
- Check `message_list` at each stage transition
|
|
457
|
-
- The orchestrator monitors all agents via `message_list` with their own agent_id
|
|
458
|
-
- **Use aide state** for progress tracking:
|
|
459
|
-
```bash
|
|
460
|
-
./.aide/bin/aide state set "agent-auth:stage" "TEST"
|
|
461
|
-
./.aide/bin/aide state set "agent-auth:status" "running"
|
|
462
|
-
```
|
|
463
|
-
- Monitor all agents: `mcp__plugin_aide_aide__state_list`
|
|
464
|
-
|
|
465
|
-
**Orchestrator role (human or primary session):**
|
|
455
|
+
The orchestrator (human or primary session):
|
|
466
456
|
|
|
467
457
|
1. Decompose stories (use `/aide:plan-swarm` first)
|
|
468
458
|
2. Create worktrees
|
|
469
|
-
3.
|
|
470
|
-
|
|
471
|
-
|
|
459
|
+
3. Create aide tasks for all SDLC stages upfront:
|
|
460
|
+
```
|
|
461
|
+
task_create: title="[story-auth][DESIGN] Design auth module"
|
|
462
|
+
task_create: title="[story-auth][TEST] Write auth tests"
|
|
463
|
+
task_create: title="[story-auth][DEV] Implement auth"
|
|
464
|
+
task_create: title="[story-auth][VERIFY] Verify auth"
|
|
465
|
+
task_create: title="[story-auth][DOCS] Document auth"
|
|
466
|
+
```
|
|
467
|
+
4. Launch terminal sessions with instructions (include agent ID and story assignment)
|
|
468
|
+
5. Monitor progress via `task_list` (MCP tool) or `./.aide/bin/aide task list` (CLI)
|
|
469
|
+
6. When all tasks show `done`, run `/aide:worktree-resolve`
|
|
470
|
+
|
|
471
|
+
### Story Agent Workflow (OpenCode)
|
|
472
|
+
|
|
473
|
+
Each story agent follows the same SDLC pipeline. Use aide tasks for shared tracking and native `todowrite` for personal step-by-step progress:
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
## Per SDLC Stage:
|
|
477
|
+
|
|
478
|
+
1. Claim the stage task:
|
|
479
|
+
task_claim: task_id=<id>, agent_id=agent-auth
|
|
480
|
+
|
|
481
|
+
2. Use todowrite for personal tracking:
|
|
482
|
+
todowrite: [{"content": "Design interfaces for auth", "status": "in_progress", "priority": "high"}]
|
|
483
|
+
|
|
484
|
+
3. Execute the stage (use appropriate /aide: skill)
|
|
485
|
+
|
|
486
|
+
4. Complete the aide task:
|
|
487
|
+
task_complete: task_id=<id>, result="Designed JWT auth with refresh tokens"
|
|
488
|
+
|
|
489
|
+
5. Send status message:
|
|
490
|
+
message_send: from="agent-auth", type="status", content="[DESIGN] complete"
|
|
491
|
+
|
|
492
|
+
6. Check for messages from other agents:
|
|
493
|
+
message_list: agent_id="agent-auth"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Note:** aide tasks do not have `blockedBy` dependency chaining like Claude Code native tasks. Stage ordering is enforced by the SDLC pipeline instructions — each agent processes stages sequentially (DESIGN → TEST → DEV → VERIFY → DOCS).
|
|
497
|
+
|
|
498
|
+
### Coordination (OpenCode)
|
|
499
|
+
|
|
500
|
+
```
|
|
501
|
+
# Shared task board — all agents see the same tasks
|
|
502
|
+
task_list # View all tasks
|
|
503
|
+
task_list: status="pending" # View unclaimed work
|
|
504
|
+
|
|
505
|
+
# Messages — real-time coordination
|
|
506
|
+
message_send: from="agent-auth", type="status", content="[DESIGN] complete, starting TEST"
|
|
507
|
+
message_send: from="agent-auth", to="agent-payments", type="request", content="Need payment API schema"
|
|
508
|
+
message_list: agent_id="agent-auth"
|
|
509
|
+
message_ack: message_id=42, agent_id="agent-auth"
|
|
510
|
+
|
|
511
|
+
# State — supplementary progress tracking
|
|
512
|
+
./.aide/bin/aide state set "agent-auth:stage" "TEST"
|
|
513
|
+
|
|
514
|
+
# Decisions and discoveries — shared knowledge
|
|
515
|
+
mcp__plugin_aide_aide__decision_get with topic="auth-strategy"
|
|
516
|
+
./.aide/bin/aide decision set "auth-strategy" "JWT with refresh tokens"
|
|
517
|
+
./.aide/bin/aide memory add --category=discovery "User model needs email validation"
|
|
518
|
+
```
|
|
472
519
|
|
|
473
520
|
## Completion (MANDATORY STEPS)
|
|
474
521
|
|
|
@@ -477,7 +524,11 @@ Swarm completion checklist - ALL REQUIRED:
|
|
|
477
524
|
### Step 1: Verify All Stories Complete
|
|
478
525
|
|
|
479
526
|
```
|
|
527
|
+
# Claude Code:
|
|
480
528
|
TaskList # All story tasks must show [completed]
|
|
529
|
+
|
|
530
|
+
# OpenCode:
|
|
531
|
+
task_list # All aide tasks must show [done]
|
|
481
532
|
```
|
|
482
533
|
|
|
483
534
|
- Every story must have completed all 5 SDLC stages
|
package/skills/test/SKILL.md
CHANGED
|
@@ -17,6 +17,7 @@ Write comprehensive tests and run test suites.
|
|
|
17
17
|
## Prerequisites
|
|
18
18
|
|
|
19
19
|
Before starting:
|
|
20
|
+
|
|
20
21
|
- Identify the code to be tested (function, module, feature)
|
|
21
22
|
- Understand the testing framework used in the project
|
|
22
23
|
|
|
@@ -27,6 +28,7 @@ Before starting:
|
|
|
27
28
|
Use the `mcp__plugin_aide_aide__decision_get` tool with topic `testing` to check for testing framework decisions.
|
|
28
29
|
|
|
29
30
|
Common frameworks by language:
|
|
31
|
+
|
|
30
32
|
- **TypeScript/JavaScript:** Vitest, Jest, Mocha
|
|
31
33
|
- **Go:** built-in `go test`
|
|
32
34
|
- **Python:** pytest, unittest
|
|
@@ -34,13 +36,18 @@ Common frameworks by language:
|
|
|
34
36
|
### Step 2: Discover Existing Test Patterns
|
|
35
37
|
|
|
36
38
|
Use `Glob` to find test files:
|
|
39
|
+
|
|
37
40
|
- Pattern: `**/*.test.ts`, `**/*.spec.ts` (TypeScript)
|
|
38
41
|
- Pattern: `**/*_test.go` (Go)
|
|
39
42
|
- Pattern: `**/test_*.py`, `**/*_test.py` (Python)
|
|
40
43
|
|
|
41
|
-
Use
|
|
44
|
+
Use **Grep** to find test patterns in existing test files:
|
|
45
|
+
|
|
46
|
+
- `Grep pattern="describe\(" include="*.test.*"` — find test suites
|
|
47
|
+
- `Grep pattern="it\(|test\(" include="*.test.*"` — find test cases
|
|
42
48
|
|
|
43
49
|
Read an existing test file to understand:
|
|
50
|
+
|
|
44
51
|
- Import patterns
|
|
45
52
|
- Setup/teardown patterns
|
|
46
53
|
- Mocking approach
|
|
@@ -52,6 +59,7 @@ Use `mcp__plugin_aide_aide__code_symbols` with the target file path to get funct
|
|
|
52
59
|
Use `mcp__plugin_aide_aide__code_search` to find related types.
|
|
53
60
|
|
|
54
61
|
Identify:
|
|
62
|
+
|
|
55
63
|
- Input parameters and types
|
|
56
64
|
- Return type
|
|
57
65
|
- Side effects
|
|
@@ -63,12 +71,14 @@ Identify:
|
|
|
63
71
|
Follow the project's testing conventions. Cover these scenarios:
|
|
64
72
|
|
|
65
73
|
**Test Categories:**
|
|
74
|
+
|
|
66
75
|
1. **Happy path** - Normal, expected inputs
|
|
67
76
|
2. **Edge cases** - Empty, null, boundary values
|
|
68
77
|
3. **Error cases** - Invalid inputs, expected failures
|
|
69
78
|
4. **Async behavior** - If applicable
|
|
70
79
|
|
|
71
80
|
**Naming convention:**
|
|
81
|
+
|
|
72
82
|
- Descriptive names that explain what is being tested
|
|
73
83
|
- Format: "should [expected behavior] when [condition]"
|
|
74
84
|
|
|
@@ -103,51 +113,52 @@ go tool cover -html=coverage.out
|
|
|
103
113
|
```
|
|
104
114
|
|
|
105
115
|
**Coverage targets:**
|
|
116
|
+
|
|
106
117
|
- New code: aim for >80%
|
|
107
118
|
- Critical paths: aim for >90%
|
|
108
119
|
- Focus on meaningful tests, not just coverage numbers
|
|
109
120
|
|
|
110
121
|
## Failure Handling
|
|
111
122
|
|
|
112
|
-
| Failure
|
|
113
|
-
|
|
114
|
-
| Test imports fail
|
|
115
|
-
| Mock not working
|
|
116
|
-
| Async test timeout | Add proper await, increase timeout if needed
|
|
117
|
-
| Flaky test
|
|
118
|
-
| Coverage too low
|
|
123
|
+
| Failure | Action |
|
|
124
|
+
| ------------------ | ------------------------------------------------------- |
|
|
125
|
+
| Test imports fail | Check path aliases, ensure test config matches main |
|
|
126
|
+
| Mock not working | Verify mock setup, check dependency injection |
|
|
127
|
+
| Async test timeout | Add proper await, increase timeout if needed |
|
|
128
|
+
| Flaky test | Check for shared state, timing issues, or external deps |
|
|
129
|
+
| Coverage too low | Add edge case tests, error path tests |
|
|
119
130
|
|
|
120
131
|
## Test Structure Templates
|
|
121
132
|
|
|
122
133
|
### TypeScript/JavaScript (Vitest/Jest)
|
|
123
134
|
|
|
124
135
|
```typescript
|
|
125
|
-
import { describe, it, expect, beforeEach, vi } from
|
|
126
|
-
import { functionToTest } from
|
|
136
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
137
|
+
import { functionToTest } from "./module";
|
|
127
138
|
|
|
128
|
-
describe(
|
|
139
|
+
describe("functionToTest", () => {
|
|
129
140
|
beforeEach(() => {
|
|
130
141
|
// Reset state before each test
|
|
131
142
|
vi.clearAllMocks();
|
|
132
143
|
});
|
|
133
144
|
|
|
134
|
-
it(
|
|
135
|
-
const result = functionToTest(
|
|
136
|
-
expect(result).toBe(
|
|
145
|
+
it("should return expected result for valid input", () => {
|
|
146
|
+
const result = functionToTest("valid input");
|
|
147
|
+
expect(result).toBe("expected output");
|
|
137
148
|
});
|
|
138
149
|
|
|
139
|
-
it(
|
|
140
|
-
const result = functionToTest(
|
|
141
|
-
expect(result).toBe(
|
|
150
|
+
it("should handle empty input", () => {
|
|
151
|
+
const result = functionToTest("");
|
|
152
|
+
expect(result).toBe("");
|
|
142
153
|
});
|
|
143
154
|
|
|
144
|
-
it(
|
|
145
|
-
expect(() => functionToTest(null)).toThrow(
|
|
155
|
+
it("should throw error for null input", () => {
|
|
156
|
+
expect(() => functionToTest(null)).toThrow("Input required");
|
|
146
157
|
});
|
|
147
158
|
|
|
148
|
-
it(
|
|
149
|
-
const result = await functionToTest(
|
|
150
|
-
expect(result).resolves.toBe(
|
|
159
|
+
it("should handle async operation", async () => {
|
|
160
|
+
const result = await functionToTest("async input");
|
|
161
|
+
expect(result).resolves.toBe("async output");
|
|
151
162
|
});
|
|
152
163
|
});
|
|
153
164
|
```
|
|
@@ -213,6 +224,7 @@ class TestFunctionToTest:
|
|
|
213
224
|
## Verification Criteria
|
|
214
225
|
|
|
215
226
|
Before completing:
|
|
227
|
+
|
|
216
228
|
- [ ] All new tests pass
|
|
217
229
|
- [ ] Existing tests still pass
|
|
218
230
|
- [ ] Coverage meets project standards
|
|
@@ -225,9 +237,11 @@ Before completing:
|
|
|
225
237
|
## Tests Added
|
|
226
238
|
|
|
227
239
|
### Files
|
|
240
|
+
|
|
228
241
|
- `path/to/file.test.ts` - 5 tests for UserService
|
|
229
242
|
|
|
230
243
|
### Test Cases
|
|
244
|
+
|
|
231
245
|
1. should create user with valid data
|
|
232
246
|
2. should reject duplicate email
|
|
233
247
|
3. should hash password before saving
|
|
@@ -235,10 +249,12 @@ Before completing:
|
|
|
235
249
|
5. should validate email format
|
|
236
250
|
|
|
237
251
|
### Coverage
|
|
252
|
+
|
|
238
253
|
- New code: 92%
|
|
239
254
|
- Total project: 84%
|
|
240
255
|
|
|
241
256
|
### Verification
|
|
257
|
+
|
|
242
258
|
- All tests: PASS
|
|
243
259
|
- No flaky tests observed
|
|
244
260
|
```
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Guard — platform-agnostic core logic.
|
|
3
|
+
*
|
|
4
|
+
* Monitors Read tool calls and advises agents to use code_outline
|
|
5
|
+
* before reading large files. This preserves context window for
|
|
6
|
+
* the actual task by avoiding dumping entire files into conversation.
|
|
7
|
+
*
|
|
8
|
+
* Behaviour:
|
|
9
|
+
* - Triggers on Read tool calls for files > 5KB (~150 lines)
|
|
10
|
+
* - Tracks which files have been outlined (code_outline/code_symbols)
|
|
11
|
+
* - Returns an advisory message (never blocks)
|
|
12
|
+
* - Also tracks code_outline/code_symbols calls to mark files as "known"
|
|
13
|
+
*
|
|
14
|
+
* Used by both Claude Code hooks (PreToolUse) and OpenCode plugin.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { statSync, readFileSync, writeFileSync, existsSync } from "fs";
|
|
18
|
+
import { resolve, isAbsolute, normalize } from "path";
|
|
19
|
+
import { tmpdir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
import { debug } from "../lib/logger.js";
|
|
22
|
+
|
|
23
|
+
const SOURCE = "context-guard";
|
|
24
|
+
|
|
25
|
+
/** Default size threshold in bytes (~150 lines) */
|
|
26
|
+
const DEFAULT_SIZE_THRESHOLD = 5120;
|
|
27
|
+
|
|
28
|
+
/** File extensions that are typically not source code (skip advisory) */
|
|
29
|
+
const SKIP_EXTENSIONS = new Set([
|
|
30
|
+
".json",
|
|
31
|
+
".lock",
|
|
32
|
+
".sum",
|
|
33
|
+
".mod",
|
|
34
|
+
".yaml",
|
|
35
|
+
".yml",
|
|
36
|
+
".toml",
|
|
37
|
+
".env",
|
|
38
|
+
".md",
|
|
39
|
+
".txt",
|
|
40
|
+
".csv",
|
|
41
|
+
".svg",
|
|
42
|
+
".png",
|
|
43
|
+
".jpg",
|
|
44
|
+
".gif",
|
|
45
|
+
".ico",
|
|
46
|
+
".woff",
|
|
47
|
+
".woff2",
|
|
48
|
+
".ttf",
|
|
49
|
+
".eot",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
export interface ContextGuardResult {
|
|
53
|
+
/** Whether to inject an advisory message */
|
|
54
|
+
shouldAdvise: boolean;
|
|
55
|
+
/** Advisory message to inject */
|
|
56
|
+
advisory?: string;
|
|
57
|
+
/** Whether this call should be tracked (code_outline/code_symbols) */
|
|
58
|
+
tracked?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get the path to the tracking file for this session.
|
|
63
|
+
*/
|
|
64
|
+
function getTrackingPath(sessionId: string): string {
|
|
65
|
+
return join(tmpdir(), `aide-context-guard-${sessionId}.json`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Load the set of files that have been outlined in this session.
|
|
70
|
+
*/
|
|
71
|
+
function loadOutlinedFiles(sessionId: string): Set<string> {
|
|
72
|
+
const trackingPath = getTrackingPath(sessionId);
|
|
73
|
+
try {
|
|
74
|
+
if (existsSync(trackingPath)) {
|
|
75
|
+
const data = JSON.parse(readFileSync(trackingPath, "utf-8"));
|
|
76
|
+
return new Set(data.files || []);
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
// Corrupted file, start fresh
|
|
80
|
+
}
|
|
81
|
+
return new Set();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Save a file as "outlined" in the tracking file.
|
|
86
|
+
*/
|
|
87
|
+
function trackOutlinedFile(sessionId: string, filePath: string): void {
|
|
88
|
+
const files = loadOutlinedFiles(sessionId);
|
|
89
|
+
files.add(filePath);
|
|
90
|
+
const trackingPath = getTrackingPath(sessionId);
|
|
91
|
+
try {
|
|
92
|
+
writeFileSync(
|
|
93
|
+
trackingPath,
|
|
94
|
+
JSON.stringify({ files: Array.from(files) }),
|
|
95
|
+
"utf-8",
|
|
96
|
+
);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
debug(SOURCE, `Failed to write tracking file: ${err}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Estimate line count from file size (rough: ~35 bytes per line average).
|
|
104
|
+
*/
|
|
105
|
+
function estimateLines(sizeBytes: number): number {
|
|
106
|
+
return Math.round(sizeBytes / 35);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check whether a Read call should receive a context-efficiency advisory.
|
|
111
|
+
*
|
|
112
|
+
* Also handles tracking code_outline/code_symbols calls.
|
|
113
|
+
*/
|
|
114
|
+
export function checkContextGuard(
|
|
115
|
+
toolName: string,
|
|
116
|
+
toolInput: Record<string, unknown>,
|
|
117
|
+
cwd: string,
|
|
118
|
+
sessionId: string,
|
|
119
|
+
): ContextGuardResult {
|
|
120
|
+
const normalizedTool = toolName.toLowerCase();
|
|
121
|
+
|
|
122
|
+
// Track code_outline and code_symbols calls
|
|
123
|
+
if (
|
|
124
|
+
normalizedTool.includes("code_outline") ||
|
|
125
|
+
normalizedTool.includes("code_symbols")
|
|
126
|
+
) {
|
|
127
|
+
const filePath =
|
|
128
|
+
(toolInput.file as string) ||
|
|
129
|
+
(toolInput.filePath as string) ||
|
|
130
|
+
(toolInput.file_path as string);
|
|
131
|
+
if (filePath && sessionId) {
|
|
132
|
+
const resolved = normalize(
|
|
133
|
+
isAbsolute(filePath) ? filePath : resolve(cwd, filePath),
|
|
134
|
+
);
|
|
135
|
+
trackOutlinedFile(sessionId, resolved);
|
|
136
|
+
debug(SOURCE, `Tracked outline for: ${filePath}`);
|
|
137
|
+
}
|
|
138
|
+
return { shouldAdvise: false, tracked: true };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Only advise on Read tool calls
|
|
142
|
+
if (normalizedTool !== "read") {
|
|
143
|
+
return { shouldAdvise: false };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Extract file path from tool input
|
|
147
|
+
const filePath =
|
|
148
|
+
(toolInput.filePath as string) ||
|
|
149
|
+
(toolInput.file_path as string) ||
|
|
150
|
+
(toolInput.path as string);
|
|
151
|
+
|
|
152
|
+
if (!filePath) {
|
|
153
|
+
return { shouldAdvise: false };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Resolve to absolute path
|
|
157
|
+
const resolvedPath = normalize(
|
|
158
|
+
isAbsolute(filePath) ? filePath : resolve(cwd, filePath),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Skip non-source-code files
|
|
162
|
+
const ext = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
|
|
163
|
+
if (SKIP_EXTENSIONS.has(ext)) {
|
|
164
|
+
return { shouldAdvise: false };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check if the agent is already using offset/limit (targeted read)
|
|
168
|
+
const offset = toolInput.offset as number | undefined;
|
|
169
|
+
const limit = toolInput.limit as number | undefined;
|
|
170
|
+
if (offset !== undefined && offset > 1) {
|
|
171
|
+
// Agent is doing a targeted read — no advisory needed
|
|
172
|
+
return { shouldAdvise: false };
|
|
173
|
+
}
|
|
174
|
+
if (limit !== undefined && limit < 100) {
|
|
175
|
+
// Agent is limiting the read — no advisory needed
|
|
176
|
+
return { shouldAdvise: false };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check file size
|
|
180
|
+
let fileSize: number;
|
|
181
|
+
try {
|
|
182
|
+
const stat = statSync(resolvedPath);
|
|
183
|
+
fileSize = stat.size;
|
|
184
|
+
} catch {
|
|
185
|
+
// Can't stat file — don't advise (file might not exist yet)
|
|
186
|
+
return { shouldAdvise: false };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Skip small files
|
|
190
|
+
if (fileSize < DEFAULT_SIZE_THRESHOLD) {
|
|
191
|
+
return { shouldAdvise: false };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check if this file has already been outlined in this session
|
|
195
|
+
if (sessionId) {
|
|
196
|
+
const outlinedFiles = loadOutlinedFiles(sessionId);
|
|
197
|
+
if (outlinedFiles.has(resolvedPath)) {
|
|
198
|
+
debug(SOURCE, `File already outlined, skipping advisory: ${filePath}`);
|
|
199
|
+
return { shouldAdvise: false };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Generate advisory
|
|
204
|
+
const estLines = estimateLines(fileSize);
|
|
205
|
+
const sizeKB = (fileSize / 1024).toFixed(1);
|
|
206
|
+
|
|
207
|
+
const advisory =
|
|
208
|
+
`[aide:context] This file is ~${estLines} lines (${sizeKB}KB). Consider using \`code_outline\` ` +
|
|
209
|
+
`first to see its structure, then \`Read\` with offset/limit for specific sections. ` +
|
|
210
|
+
`This preserves your context window for the full task.`;
|
|
211
|
+
|
|
212
|
+
debug(SOURCE, `Advisory for ${filePath}: ${estLines} lines, ${sizeKB}KB`);
|
|
213
|
+
return { shouldAdvise: true, advisory };
|
|
214
|
+
}
|
|
@@ -74,17 +74,23 @@ export function buildReinforcement(
|
|
|
74
74
|
* Returns null if stop is allowed, or { reason } if stop should be blocked.
|
|
75
75
|
* When a persistence mode is active and todos exist, the reinforcement
|
|
76
76
|
* message includes the specific incomplete tasks.
|
|
77
|
+
*
|
|
78
|
+
* When agentId is provided, only tasks claimed by that agent are considered
|
|
79
|
+
* for blocking. This prevents subagents from being blocked by tasks that
|
|
80
|
+
* belong to other agents. Global (unclaimed) tasks still count for all agents.
|
|
77
81
|
*/
|
|
78
82
|
export function checkPersistence(
|
|
79
83
|
binary: string,
|
|
80
84
|
cwd: string,
|
|
85
|
+
agentId?: string,
|
|
81
86
|
): { reason: string } | null {
|
|
82
87
|
const mode = getActiveMode(binary, cwd);
|
|
83
88
|
if (!mode) return null;
|
|
84
89
|
|
|
85
|
-
// Get and increment iteration counter
|
|
90
|
+
// Get and increment iteration counter (guard against NaN from corrupted state)
|
|
86
91
|
const iterStr = getState(binary, cwd, `${mode}_iterations`) || "0";
|
|
87
|
-
const
|
|
92
|
+
const parsed = parseInt(iterStr, 10);
|
|
93
|
+
const iteration = (Number.isNaN(parsed) ? 0 : parsed) + 1;
|
|
88
94
|
setState(binary, cwd, `${mode}_iterations`, String(iteration));
|
|
89
95
|
|
|
90
96
|
if (iteration > MAX_PERSISTENCE_ITERATIONS) {
|
|
@@ -94,21 +100,37 @@ export function checkPersistence(
|
|
|
94
100
|
return null;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
|
-
// Fetch todos and build a specific continuation message if incomplete tasks exist
|
|
103
|
+
// Fetch todos and build a specific continuation message if incomplete tasks exist.
|
|
104
|
+
// If all tasks are complete (or no tasks exist), auto-release: allow stop.
|
|
98
105
|
let todoSummary: string | undefined;
|
|
106
|
+
let allTasksComplete = false;
|
|
99
107
|
try {
|
|
100
108
|
const todos = fetchTodosFromAide(binary, cwd);
|
|
101
|
-
const todoResult = checkTodos(todos);
|
|
109
|
+
const todoResult = checkTodos(todos, agentId);
|
|
102
110
|
if (todoResult.hasIncomplete) {
|
|
103
111
|
todoSummary = todoResult.message;
|
|
104
112
|
debug(
|
|
105
113
|
SOURCE,
|
|
106
114
|
`Found ${todoResult.incompleteCount} incomplete todos for persistence reinforcement`,
|
|
107
115
|
);
|
|
116
|
+
} else if (todoResult.totalCount > 0) {
|
|
117
|
+
// All tasks exist and are in terminal states — work is done
|
|
118
|
+
allTasksComplete = true;
|
|
119
|
+
debug(
|
|
120
|
+
SOURCE,
|
|
121
|
+
`All ${todoResult.totalCount} tasks complete — auto-releasing ${mode} mode`,
|
|
122
|
+
);
|
|
108
123
|
}
|
|
109
124
|
} catch (err) {
|
|
110
125
|
debug(SOURCE, `Failed to fetch todos for persistence (non-fatal): ${err}`);
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
// Auto-release: if tasks exist and all are complete, allow stop
|
|
129
|
+
if (allTasksComplete) {
|
|
130
|
+
setState(binary, cwd, "mode", "");
|
|
131
|
+
setState(binary, cwd, `${mode}_iterations`, "0");
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
return { reason: buildReinforcement(mode, iteration, todoSummary) };
|
|
114
136
|
}
|