pi-harness-runtime 0.2.0 → 0.3.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/README.md +117 -158
- package/harness/agent-handoff.ts +189 -0
- package/harness/blackboard.ts +291 -0
- package/harness/context-window-manager.ts +210 -0
- package/harness/e2e/playwright-runner.ts +252 -0
- package/harness/e2e/test-engine.ts +402 -0
- package/harness/job-state-machine.ts +363 -0
- package/harness/loop-runtime.ts +337 -0
- package/harness/master-planner.ts +327 -0
- package/harness/project-detector/detector.ts +328 -0
- package/harness/repair-engine.ts +340 -0
- package/harness/task-graph.ts +336 -0
- package/index.ts +294 -2
- package/package.json +5 -2
- package/packages/checkpoint/README.md +3 -0
- package/packages/checkpoint/src/checkpoint-manager.ts +38 -0
- package/packages/provider-router/src/provider-router.ts +42 -0
- package/packages/providers/README.md +3 -0
- package/packages/providers/adapters.ts +261 -0
- package/packages/quota-manager/README.md +3 -0
- package/packages/quota-manager/quota-manager.ts +328 -0
- package/packages/runtime/README.md +3 -0
- package/packages/scheduler/README.md +3 -0
- package/packages/scheduler/src/scheduler.ts +50 -0
- package/packages/shared-context/README.md +3 -0
- package/packages/shared-context/src/shared-context.ts +42 -0
- package/packages/tui/README.md +3 -0
- package/packages/types/src/runtime-types.ts +439 -0
- package/packages/worktree/README.md +3 -0
- package/packages/worktree/worktree.ts +293 -0
- package/skills/harness-runtime/SKILL.md +186 -72
package/README.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# Pi Harness Runtime
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Autonomous AI coding harness for pi: local token tracking + provider mirror + task orchestration.**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/pi-harness-runtime)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[]()
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Two Flavors
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### 1. `/usage` — Token Tracking (v0.1+)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Codex-style usage tracking with local tokens + provider mirror.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### 2. `/harness` — Autonomous Coding Loop (v0.3+)
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Give the runtime a requirement. It keeps working, survives interruption, and returns with code ready for review.
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
@@ -22,186 +22,145 @@
|
|
|
22
22
|
pi install npm:pi-harness-runtime
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Or install locally for development:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
git clone https://github.com/ManotLuijiu/pi-harness-runtime.git
|
|
29
|
-
ln -s pi-harness-runtime ~/.pi/agent/extensions/pi-harness-runtime
|
|
30
|
-
pi reload
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Important**: Symlink the **directory** into `~/.pi/agent/extensions/`. Pi's loader scans that directory and reads `package.json`'s `pi.extensions` field for multi-file extensions. Don't symlink a single file (relative imports break) or symlink into `node_modules/` (pi doesn't auto-scan there).
|
|
34
|
-
|
|
35
|
-
No API keys required. No build step (Bun runs `.ts` directly).
|
|
36
|
-
|
|
37
25
|
Requires Pi v0.37.3+.
|
|
38
26
|
|
|
39
|
-
##
|
|
27
|
+
## Usage Commands
|
|
40
28
|
|
|
41
29
|
```bash
|
|
42
|
-
/usage # show full status (
|
|
43
|
-
/usage sync #
|
|
44
|
-
/usage today #
|
|
45
|
-
/usage week #
|
|
46
|
-
/usage reset # clear provider mirror
|
|
30
|
+
/usage # show full status (local tracking + provider mirror)
|
|
31
|
+
/usage sync # mirror provider quota from console.minimax.io
|
|
32
|
+
/usage today # today's usage + 5h window
|
|
33
|
+
/usage week # this week's usage + lifetime totals
|
|
34
|
+
/usage reset # clear provider mirror
|
|
47
35
|
```
|
|
48
36
|
|
|
49
|
-
## Commands
|
|
37
|
+
## Harness Commands
|
|
50
38
|
|
|
51
|
-
|
|
39
|
+
```bash
|
|
40
|
+
/harness start <requirement> # Start a new harness job
|
|
41
|
+
/harness status # Show current job status
|
|
42
|
+
/harness tasks # List all tasks
|
|
43
|
+
/harness pause # Pause when quota low
|
|
44
|
+
/harness resume # Resume after quota reset
|
|
45
|
+
/harness cancel # Cancel job
|
|
46
|
+
```
|
|
52
47
|
|
|
53
|
-
|
|
48
|
+
## Example Session
|
|
54
49
|
|
|
55
50
|
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
/harness start Build a REST API with JWT authentication
|
|
52
|
+
|
|
53
|
+
# Runtime creates task graph:
|
|
54
|
+
# task-001: Analyze requirements → ready
|
|
55
|
+
# task-002: Implement API → depends on task-001
|
|
56
|
+
# task-003: Write tests → depends on task-002
|
|
57
|
+
# task-004: Code review → depends on task-003
|
|
58
|
+
|
|
59
|
+
/harness status
|
|
60
|
+
# Job: job-123, Status: running
|
|
61
|
+
# Tasks: 1/4 done, 1 running
|
|
62
|
+
|
|
63
|
+
# When quota runs low:
|
|
64
|
+
/harness pause
|
|
65
|
+
# Job paused. Resume when quota resets.
|
|
66
|
+
|
|
67
|
+
# When quota resets:
|
|
68
|
+
/harness resume
|
|
69
|
+
# Job resumed. Continue working.
|
|
70
|
+
|
|
71
|
+
# When all tasks complete:
|
|
72
|
+
/harness status
|
|
73
|
+
# Job: job-123, Status: ready_for_client
|
|
74
|
+
# Tasks: 4/4 done ✓
|
|
79
75
|
```
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Open a 6-prompt form to mirror provider-side quota:
|
|
77
|
+
## Architecture
|
|
84
78
|
|
|
85
79
|
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
pi-harness-runtime/
|
|
81
|
+
├── index.ts # Extension entry point
|
|
82
|
+
├── harness/
|
|
83
|
+
│ ├── job-state-machine.ts # 14-state lifecycle machine
|
|
84
|
+
│ ├── task-graph.ts # DAG-based task management
|
|
85
|
+
│ ├── master-planner.ts # Requirement → task graph
|
|
86
|
+
│ ├── loop-runtime.ts # Core execution loop
|
|
87
|
+
│ ├── repair-engine.ts # Auto-fix failures
|
|
88
|
+
│ ├── blackboard.ts # Agent coordination
|
|
89
|
+
│ ├── context-window-manager.ts # Context tracking
|
|
90
|
+
│ ├── agent-handoff.ts # Clean agent transitions
|
|
91
|
+
│ ├── e2e/
|
|
92
|
+
│ │ ├── test-engine.ts # E2E test runner
|
|
93
|
+
│ │ └── playwright-runner.ts # Browser automation
|
|
94
|
+
│ └── project-detector/
|
|
95
|
+
│ └── detector.ts # Auto-detect project type
|
|
96
|
+
├── packages/
|
|
97
|
+
│ ├── providers/adapters.ts # MiniMax, OpenAI adapters
|
|
98
|
+
│ ├── quota-manager/ # Quota signal collection
|
|
99
|
+
│ └── worktree/ # Git worktree per task
|
|
100
|
+
└── skills/
|
|
101
|
+
└── harness-runtime/
|
|
102
|
+
└── SKILL.md # Skill documentation
|
|
92
103
|
```
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
### `/usage today`
|
|
97
|
-
|
|
98
|
-
Focused view: today's usage + 5h window. Quick check before starting a long session.
|
|
99
|
-
|
|
100
|
-
### `/usage week`
|
|
101
|
-
|
|
102
|
-
Focused view: this week's usage + lifetime totals. Good for end-of-week review.
|
|
103
|
-
|
|
104
|
-
### `/usage reset`
|
|
105
|
-
|
|
106
|
-
Clear the provider mirror. Local usage log is preserved. Asks for confirmation.
|
|
107
|
-
|
|
108
|
-
## How It Works
|
|
109
|
-
|
|
110
|
-
### Data Sources (3-source model)
|
|
111
|
-
|
|
112
|
-
1. **Local tracked** — every assistant message is logged to `~/.pi/usage-status/usage.jsonl`
|
|
113
|
-
- Auto-tracked via `message_end` event
|
|
114
|
-
- Contains: timestamp, model, input/output/cache tokens, cost
|
|
115
|
-
- Real-time, exact, but only counts THIS pi session
|
|
116
|
-
|
|
117
|
-
2. **Provider mirror** — manually entered from `https://platform.minimax.io/console/usage`
|
|
118
|
-
- Stored at `~/.pi/usage-status/mirror.json`
|
|
119
|
-
- Synced via `/usage sync` form
|
|
120
|
-
- Ground truth for TOTAL quota (across all clients)
|
|
105
|
+
## Job State Machine
|
|
121
106
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
| Path | Contents |
|
|
130
|
-
|---|---|
|
|
131
|
-
| `~/.pi/usage-status/usage.jsonl` | Append-only usage log |
|
|
132
|
-
| `~/.pi/usage-status/mirror.json` | Manual provider mirror |
|
|
133
|
-
| (none — pure functions) | CLI helpers in `cli.ts` |
|
|
134
|
-
|
|
135
|
-
Override location with `PI_USAGE_DIR` env var (useful for testing).
|
|
136
|
-
|
|
137
|
-
### What's NOT Stored
|
|
138
|
-
|
|
139
|
-
- Your password (we don't ask for it, ever)
|
|
140
|
-
- Provider session tokens (we don't read browser cookies)
|
|
141
|
-
- Telemetry or remote calls (all local)
|
|
142
|
-
- Auto-send quota (you always confirm any sync)
|
|
143
|
-
|
|
144
|
-
## Why Manual Mirror?
|
|
107
|
+
```
|
|
108
|
+
created → planning → queued → running → testing → reviewing
|
|
109
|
+
↓ ↓ ↓ ↓ ↓
|
|
110
|
+
cancelled blocked waiting_human repairing ready_for_client
|
|
111
|
+
↓ ↓
|
|
112
|
+
paused_quota archived
|
|
113
|
+
```
|
|
145
114
|
|
|
146
|
-
|
|
115
|
+
## Key Features
|
|
147
116
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
117
|
+
- **Resumable**: Every state change is checkpointed to disk
|
|
118
|
+
- **Quota-aware**: Detects quota exhaustion, pauses, resumes after reset
|
|
119
|
+
- **Provider-agnostic**: MiniMax, OpenAI, Claude adapters
|
|
120
|
+
- **Task DAG**: Dependencies tracked, topological execution
|
|
121
|
+
- **Auto-repair**: Failure classification + retry with exponential backoff
|
|
122
|
+
- **E2E testing**: Scenario-based Playwright integration
|
|
123
|
+
- **Project detection**: Auto-detects Frappe, Next.js, React, Django, Laravel
|
|
153
124
|
|
|
154
|
-
|
|
125
|
+
## Data Directory
|
|
155
126
|
|
|
156
|
-
|
|
157
|
-
2. Cookie extraction has ongoing maintenance burden
|
|
158
|
-
3. Password storage is a security anti-pattern
|
|
127
|
+
All data stored locally in `~/.pi/`:
|
|
159
128
|
|
|
160
|
-
|
|
129
|
+
```
|
|
130
|
+
~/.pi/
|
|
131
|
+
├── usage-status/ # /usage data
|
|
132
|
+
│ ├── usage.jsonl
|
|
133
|
+
│ └── mirror.json
|
|
134
|
+
└── harness/ # /harness data
|
|
135
|
+
└── jobs/
|
|
136
|
+
└── <job-id>/
|
|
137
|
+
├── checkpoint.json
|
|
138
|
+
├── events.jsonl
|
|
139
|
+
├── task-graph.json
|
|
140
|
+
├── blackboard/
|
|
141
|
+
└── repair-tasks.jsonl
|
|
142
|
+
```
|
|
161
143
|
|
|
162
144
|
## Safety Properties
|
|
163
145
|
|
|
164
|
-
- ✅ **
|
|
165
|
-
- ✅ **No
|
|
166
|
-
- ✅ **
|
|
167
|
-
- ✅ **
|
|
168
|
-
- ✅ **
|
|
169
|
-
- ✅ **No credentials stored** — passwords, tokens, cookies: none
|
|
146
|
+
- ✅ **Local-first**: All data stays on disk
|
|
147
|
+
- ✅ **No credentials stored**: No passwords, tokens, or cookies
|
|
148
|
+
- ✅ **Human-on-the-loop**: Clear intervention points
|
|
149
|
+
- ✅ **Checkpointed**: Resume from any state
|
|
150
|
+
- ✅ **Idempotent**: Safe to run multiple times
|
|
170
151
|
|
|
171
|
-
##
|
|
152
|
+
## Testing
|
|
172
153
|
|
|
154
|
+
```bash
|
|
155
|
+
bun test # 131+ tests passing
|
|
173
156
|
```
|
|
174
|
-
pi-harness-runtime/
|
|
175
|
-
├── index.ts # entry — exports default (pi: ExtensionAPI)
|
|
176
|
-
├── tracker.ts # UsageTracker — JSONL writer/reader
|
|
177
|
-
├── mirror.ts # MirrorStore — JSON read/write + freshness check
|
|
178
|
-
├── windows.ts # WindowAggregator — pure aggregation functions
|
|
179
|
-
├── renderer.ts # StatusRenderer — Codex-style progress bars
|
|
180
|
-
├── sync-form.ts # /usage sync form handling
|
|
181
|
-
├── cli.ts # shared pure helpers (paths, formatting, IO)
|
|
182
|
-
├── skills/
|
|
183
|
-
│ └── harness-runtime/
|
|
184
|
-
│ └── SKILL.md # bundled skill (auto-loaded)
|
|
185
|
-
├── test/ # node --test
|
|
186
|
-
│ ├── cli.test.mjs
|
|
187
|
-
│ ├── tracker.test.mjs
|
|
188
|
-
│ ├── mirror.test.mjs
|
|
189
|
-
│ ├── windows.test.mjs
|
|
190
|
-
│ ├── renderer.test.mjs
|
|
191
|
-
│ └── sync-form.test.mjs
|
|
192
|
-
├── package.json # "pi" field declares extensions + skills
|
|
193
|
-
├── README.md
|
|
194
|
-
├── CHANGELOG.md
|
|
195
|
-
├── LICENSE # MIT
|
|
196
|
-
└── .gitignore
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
## Related
|
|
200
|
-
|
|
201
|
-
- [pi-web-access](https://github.com/nicobailon/pi-web-access) — web search + URL fetching for pi (inspiration for this package's structure)
|
|
202
|
-
- [context-mode](https://github.com/MiniMax-AI/context-mode) — context-window tracking (complementary)
|
|
203
|
-
- [pi-coding-agent](https://github.com/earendil-works/pi-coding-agent) — the underlying pi agent
|
|
204
157
|
|
|
205
158
|
## License
|
|
206
159
|
|
|
207
160
|
MIT © 2026 MooCoding
|
|
161
|
+
|
|
162
|
+
## Related
|
|
163
|
+
|
|
164
|
+
- [pi-coding-agent](https://github.com/earendil-works/pi-coding-agent) — Underlying pi agent
|
|
165
|
+
- [context-mode](https://github.com/MiniMax-AI/context-mode) — Context window tracking
|
|
166
|
+
- [pi-web-access](https://github.com/nicobailon/pi-web-access) — Web search for pi
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Handoff Protocol — RFC-0012
|
|
3
|
+
*
|
|
4
|
+
* Clean handoff between agents with context transfer.
|
|
5
|
+
* Ensures continuity when switching agents mid-task.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
HandoffContext,
|
|
10
|
+
HandoffEvent,
|
|
11
|
+
} from "../packages/types/src/runtime-types.ts";
|
|
12
|
+
import { writeJson, readJson } from "../cli.ts";
|
|
13
|
+
// @ts-expect-error - Bun has built-in Node.js types
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
export interface HandoffData {
|
|
17
|
+
fromAgent: string;
|
|
18
|
+
toAgent: string;
|
|
19
|
+
taskId: string;
|
|
20
|
+
taskSummary: string;
|
|
21
|
+
contextFiles: string[];
|
|
22
|
+
recentHistory: string[];
|
|
23
|
+
currentState: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class AgentHandoffProtocol {
|
|
27
|
+
private readonly rootDir: string;
|
|
28
|
+
|
|
29
|
+
constructor(rootDir: string) {
|
|
30
|
+
this.rootDir = rootDir;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a handoff context for switching agents
|
|
35
|
+
*/
|
|
36
|
+
createHandoff(
|
|
37
|
+
jobId: string,
|
|
38
|
+
taskId: string,
|
|
39
|
+
fromAgent: string,
|
|
40
|
+
toAgent: string,
|
|
41
|
+
currentState?: Record<string, unknown>,
|
|
42
|
+
): HandoffContext {
|
|
43
|
+
const events = this.loadHandoffHistory(jobId, taskId);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
jobId,
|
|
47
|
+
taskId,
|
|
48
|
+
fromAgent,
|
|
49
|
+
toAgent,
|
|
50
|
+
sharedFiles: [],
|
|
51
|
+
taskHistory: events,
|
|
52
|
+
summary: this.generateSummary(taskId, currentState),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Record a handoff event
|
|
58
|
+
*/
|
|
59
|
+
recordHandoff(context: HandoffContext, result?: string): void {
|
|
60
|
+
const path = join(
|
|
61
|
+
this.rootDir,
|
|
62
|
+
"jobs",
|
|
63
|
+
context.jobId,
|
|
64
|
+
"handoffs",
|
|
65
|
+
`${context.taskId}.json`,
|
|
66
|
+
);
|
|
67
|
+
const event: HandoffEvent = {
|
|
68
|
+
ts: new Date().toISOString(),
|
|
69
|
+
agentId: context.toAgent,
|
|
70
|
+
action: "handoff_received",
|
|
71
|
+
result,
|
|
72
|
+
};
|
|
73
|
+
context.taskHistory.push(event);
|
|
74
|
+
writeJson(path, context);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generate handoff prompt for the receiving agent
|
|
79
|
+
*/
|
|
80
|
+
generateHandoffPrompt(context: HandoffContext): string {
|
|
81
|
+
const lines = [
|
|
82
|
+
`## Agent Handoff`,
|
|
83
|
+
``,
|
|
84
|
+
`**From Agent:** ${context.fromAgent}`,
|
|
85
|
+
`**To Agent:** ${context.toAgent}`,
|
|
86
|
+
`**Task:** ${context.taskId}`,
|
|
87
|
+
``,
|
|
88
|
+
`### Task History`,
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
for (const event of context.taskHistory) {
|
|
92
|
+
lines.push(`- [${event.ts}] ${event.agentId}: ${event.action}`);
|
|
93
|
+
if (event.result) {
|
|
94
|
+
lines.push(` Result: ${event.result}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lines.push(``);
|
|
99
|
+
lines.push(`### Summary`);
|
|
100
|
+
lines.push(context.summary);
|
|
101
|
+
|
|
102
|
+
if (context.sharedFiles.length > 0) {
|
|
103
|
+
lines.push(``);
|
|
104
|
+
lines.push(`### Shared Files`);
|
|
105
|
+
for (const file of context.sharedFiles) {
|
|
106
|
+
lines.push(`- ${file}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate handoff readiness
|
|
115
|
+
*/
|
|
116
|
+
validateHandoff(context: HandoffContext): {
|
|
117
|
+
valid: boolean;
|
|
118
|
+
issues: string[];
|
|
119
|
+
} {
|
|
120
|
+
const issues: string[] = [];
|
|
121
|
+
|
|
122
|
+
if (!context.summary) {
|
|
123
|
+
issues.push("Task summary is empty");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (context.taskHistory.length === 0) {
|
|
127
|
+
issues.push("No task history recorded");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check for recent handoffs
|
|
131
|
+
const recentHandoffs = context.taskHistory.filter((h) => {
|
|
132
|
+
const age = Date.now() - Date.parse(h.ts);
|
|
133
|
+
return age < 5 * 60 * 1000; // 5 minutes
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (recentHandoffs.length > 3) {
|
|
137
|
+
issues.push(
|
|
138
|
+
`Too many recent handoffs (${recentHandoffs.length}). Possible ping-pong.`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { valid: issues.length === 0, issues };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Load handoff history for a task
|
|
147
|
+
*/
|
|
148
|
+
private loadHandoffHistory(jobId: string, taskId: string): HandoffEvent[] {
|
|
149
|
+
const path = join(
|
|
150
|
+
this.rootDir,
|
|
151
|
+
"jobs",
|
|
152
|
+
jobId,
|
|
153
|
+
"handoffs",
|
|
154
|
+
`${taskId}.json`,
|
|
155
|
+
);
|
|
156
|
+
const data = readJson(path) as HandoffContext | null;
|
|
157
|
+
return data?.taskHistory ?? [];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Generate a summary of the task state
|
|
162
|
+
*/
|
|
163
|
+
private generateSummary(
|
|
164
|
+
taskId: string,
|
|
165
|
+
currentState?: Record<string, unknown>,
|
|
166
|
+
): string {
|
|
167
|
+
if (!currentState) {
|
|
168
|
+
return `Task ${taskId} requires continuation. Check task files for current state.`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const lines = [`Task ${taskId} is in progress.`];
|
|
172
|
+
|
|
173
|
+
if (currentState.filesModified) {
|
|
174
|
+
lines.push(
|
|
175
|
+
`Files modified: ${(currentState.filesModified as string[]).join(", ")}`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (currentState.lastAction) {
|
|
180
|
+
lines.push(`Last action: ${currentState.lastAction}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (currentState.blockers) {
|
|
184
|
+
lines.push(`Blockers: ${currentState.blockers}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return lines.join("\n");
|
|
188
|
+
}
|
|
189
|
+
}
|