agents-harness 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/README.md +317 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +26 -0
- package/dist/commands/config.js +91 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +67 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/resume.d.ts +6 -0
- package/dist/commands/resume.js +88 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/run.d.ts +8 -0
- package/dist/commands/run.js +106 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +38 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/core/context-manager.d.ts +19 -0
- package/dist/core/context-manager.js +118 -0
- package/dist/core/context-manager.js.map +1 -0
- package/dist/core/file-protocol.d.ts +14 -0
- package/dist/core/file-protocol.js +119 -0
- package/dist/core/file-protocol.js.map +1 -0
- package/dist/core/orchestrator.d.ts +30 -0
- package/dist/core/orchestrator.js +238 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/types.d.ts +136 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/dashboard/server.d.ts +15 -0
- package/dist/dashboard/server.js +61 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/dashboard/socket.d.ts +8 -0
- package/dist/dashboard/socket.js +22 -0
- package/dist/dashboard/socket.js.map +1 -0
- package/dist/defaults/criteria.d.ts +1 -0
- package/dist/defaults/criteria.js +23 -0
- package/dist/defaults/criteria.js.map +1 -0
- package/dist/defaults/prompts.d.ts +15 -0
- package/dist/defaults/prompts.js +123 -0
- package/dist/defaults/prompts.js.map +1 -0
- package/dist/discovery/config-loader.d.ts +12 -0
- package/dist/discovery/config-loader.js +64 -0
- package/dist/discovery/config-loader.js.map +1 -0
- package/dist/discovery/project-context.d.ts +12 -0
- package/dist/discovery/project-context.js +56 -0
- package/dist/discovery/project-context.js.map +1 -0
- package/dist/discovery/stack-detector.d.ts +15 -0
- package/dist/discovery/stack-detector.js +372 -0
- package/dist/discovery/stack-detector.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { ContextManager } from "./context-manager.js";
|
|
3
|
+
import { FileProtocol } from "./file-protocol.js";
|
|
4
|
+
import { buildProjectContext } from "../discovery/project-context.js";
|
|
5
|
+
export class Harness extends EventEmitter {
|
|
6
|
+
contextManager;
|
|
7
|
+
fileProtocol;
|
|
8
|
+
progress;
|
|
9
|
+
aborted = false;
|
|
10
|
+
options;
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
super();
|
|
13
|
+
const projectContext = buildProjectContext(opts.root, opts.scope ?? null);
|
|
14
|
+
// Apply config overrides from .harness/config.yaml
|
|
15
|
+
const config = projectContext.config;
|
|
16
|
+
this.options = {
|
|
17
|
+
maxAttemptsPerSprint: opts.maxAttemptsPerSprint ?? config?.maxAttemptsPerSprint ?? 3,
|
|
18
|
+
maxBudgetPerSprintUsd: opts.maxBudgetPerSprintUsd ?? config?.maxBudgetPerSprintUsd ?? 5,
|
|
19
|
+
maxTotalBudgetUsd: opts.maxTotalBudgetUsd ?? config?.maxTotalBudgetUsd ?? 50,
|
|
20
|
+
};
|
|
21
|
+
this.contextManager = new ContextManager(opts.apiKey, projectContext);
|
|
22
|
+
this.fileProtocol = new FileProtocol(opts.root);
|
|
23
|
+
this.progress = this.initProgress("");
|
|
24
|
+
}
|
|
25
|
+
initProgress(runSpec) {
|
|
26
|
+
return {
|
|
27
|
+
status: "running",
|
|
28
|
+
runSpec,
|
|
29
|
+
currentSprint: 0,
|
|
30
|
+
totalSprints: 0,
|
|
31
|
+
currentAttempt: 0,
|
|
32
|
+
currentPhase: "plan",
|
|
33
|
+
startedAt: new Date().toISOString(),
|
|
34
|
+
costUsd: 0,
|
|
35
|
+
maxBudgetUsd: this.options.maxTotalBudgetUsd,
|
|
36
|
+
sprints: {},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
emitEvent(event) {
|
|
40
|
+
this.emit(event.type, event.data);
|
|
41
|
+
this.emit("event", event);
|
|
42
|
+
}
|
|
43
|
+
updatePhase(phase, sprint = 0, attempt = 0) {
|
|
44
|
+
this.progress.currentPhase = phase;
|
|
45
|
+
this.progress.currentSprint = sprint;
|
|
46
|
+
this.progress.currentAttempt = attempt;
|
|
47
|
+
this.emitEvent({
|
|
48
|
+
type: "phase:start",
|
|
49
|
+
data: { sprint, phase, attempt },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async runAgentPhase(role, prompt, sprint) {
|
|
53
|
+
const result = await this.contextManager.runAgent({
|
|
54
|
+
role,
|
|
55
|
+
prompt,
|
|
56
|
+
onActivity: (tool, summary) => {
|
|
57
|
+
this.emitEvent({
|
|
58
|
+
type: "agent:activity",
|
|
59
|
+
data: { sprint, role, tool, summary, timestamp: Date.now() },
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
this.progress.costUsd += result.costUsd;
|
|
64
|
+
if (this.progress.sprints[sprint]) {
|
|
65
|
+
this.progress.sprints[sprint].costUsd += result.costUsd;
|
|
66
|
+
}
|
|
67
|
+
this.emitEvent({
|
|
68
|
+
type: "cost:update",
|
|
69
|
+
data: {
|
|
70
|
+
sprintCostUsd: this.progress.sprints[sprint]?.costUsd ?? 0,
|
|
71
|
+
totalCostUsd: this.progress.costUsd,
|
|
72
|
+
budgetUsd: this.options.maxTotalBudgetUsd,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
this.fileProtocol.writeProgress(this.progress);
|
|
76
|
+
return result.response;
|
|
77
|
+
}
|
|
78
|
+
isBudgetExceeded() {
|
|
79
|
+
return this.progress.costUsd >= this.options.maxTotalBudgetUsd;
|
|
80
|
+
}
|
|
81
|
+
parseTotalSprints(_plannerResponse) {
|
|
82
|
+
// Read sprints.md to count how many sprints were planned
|
|
83
|
+
const sprints = this.fileProtocol.readFile("sprints.md");
|
|
84
|
+
if (!sprints)
|
|
85
|
+
return 1;
|
|
86
|
+
// Count sprint headers (## Sprint N or ### Sprint N)
|
|
87
|
+
const matches = sprints.match(/^#{2,3}\s+Sprint\s+\d+/gim);
|
|
88
|
+
return matches ? matches.length : 1;
|
|
89
|
+
}
|
|
90
|
+
async run(spec) {
|
|
91
|
+
this.fileProtocol.ensureDir();
|
|
92
|
+
this.fileProtocol.ensureGitignore();
|
|
93
|
+
this.progress = this.initProgress(spec);
|
|
94
|
+
this.aborted = false;
|
|
95
|
+
try {
|
|
96
|
+
// Phase 1: Plan — write spec to file, have planner create full spec
|
|
97
|
+
this.updatePhase("plan");
|
|
98
|
+
this.fileProtocol.writeFile("spec.md", spec);
|
|
99
|
+
await this.runAgentPhase("planner", `Read .harness/spec.md. Create a comprehensive product specification. Write the full specification back to .harness/spec.md — expand and refine the user's original request into a complete spec.`, 0);
|
|
100
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
101
|
+
return this.finalize();
|
|
102
|
+
// Phase 2: Decompose — break spec into sprints
|
|
103
|
+
this.updatePhase("decompose");
|
|
104
|
+
await this.runAgentPhase("planner", `Read .harness/spec.md. Decompose this specification into ordered sprints. Each sprint must be independently testable. Write the sprint plan to .harness/sprints.md.`, 0);
|
|
105
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
106
|
+
return this.finalize();
|
|
107
|
+
const totalSprints = this.parseTotalSprints("");
|
|
108
|
+
this.progress.totalSprints = totalSprints;
|
|
109
|
+
// Execute sprints
|
|
110
|
+
for (let s = 1; s <= totalSprints; s++) {
|
|
111
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
112
|
+
break;
|
|
113
|
+
await this.executeSprint(s);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.progress.status = "failed";
|
|
118
|
+
this.fileProtocol.writeProgress(this.progress);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
return this.finalize();
|
|
122
|
+
}
|
|
123
|
+
async executeSprint(sprintNum) {
|
|
124
|
+
this.progress.sprints[sprintNum] = {
|
|
125
|
+
status: "in_progress",
|
|
126
|
+
attempts: 0,
|
|
127
|
+
costUsd: 0,
|
|
128
|
+
};
|
|
129
|
+
// Contract phase — have planner write a contract for this sprint
|
|
130
|
+
this.updatePhase("contract", sprintNum, 0);
|
|
131
|
+
await this.runAgentPhase("planner", `Read .harness/sprints.md. Write a sprint contract for Sprint ${sprintNum} to .harness/contract.md. Include specific, testable success criteria.`, sprintNum);
|
|
132
|
+
// Attempt loop
|
|
133
|
+
const maxAttempts = this.options.maxAttemptsPerSprint;
|
|
134
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
135
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
136
|
+
break;
|
|
137
|
+
this.progress.sprints[sprintNum].attempts = attempt;
|
|
138
|
+
// Generate phase
|
|
139
|
+
this.updatePhase("generate", sprintNum, attempt);
|
|
140
|
+
await this.runAgentPhase("generator", `Implement the sprint contract. Read .harness/contract.md for requirements.${attempt > 1 ? " Read .harness/evaluation.md for feedback from your previous attempt." : ""}`, sprintNum);
|
|
141
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
142
|
+
break;
|
|
143
|
+
// Evaluate phase
|
|
144
|
+
this.updatePhase("evaluate", sprintNum, attempt);
|
|
145
|
+
await this.runAgentPhase("evaluator", `Evaluate the implementation against the sprint contract. Read .harness/contract.md for requirements. Write your evaluation to .harness/evaluation.md.`, sprintNum);
|
|
146
|
+
// Parse evaluation
|
|
147
|
+
const evalResult = this.fileProtocol.parseEvaluation();
|
|
148
|
+
this.emitEvent({
|
|
149
|
+
type: "evaluation",
|
|
150
|
+
data: { sprint: sprintNum, attempt, result: evalResult },
|
|
151
|
+
});
|
|
152
|
+
if (evalResult.passed) {
|
|
153
|
+
this.progress.sprints[sprintNum].status = "passed";
|
|
154
|
+
this.emitEvent({
|
|
155
|
+
type: "sprint:complete",
|
|
156
|
+
data: {
|
|
157
|
+
sprint: sprintNum,
|
|
158
|
+
status: "passed",
|
|
159
|
+
attempts: attempt,
|
|
160
|
+
costUsd: this.progress.sprints[sprintNum].costUsd,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
// Handoff phase — write context for next sprint
|
|
164
|
+
if (sprintNum < this.progress.totalSprints) {
|
|
165
|
+
this.updatePhase("handoff", sprintNum);
|
|
166
|
+
await this.runAgentPhase("planner", `Sprint ${sprintNum} passed. Write a handoff document to .harness/handoff.md summarizing what was done, what changed, and key context the next sprint needs.`, sprintNum);
|
|
167
|
+
}
|
|
168
|
+
return; // sprint passed, move to next
|
|
169
|
+
}
|
|
170
|
+
// If last attempt and still failing
|
|
171
|
+
if (attempt === maxAttempts) {
|
|
172
|
+
this.progress.sprints[sprintNum].status = "failed";
|
|
173
|
+
this.emitEvent({
|
|
174
|
+
type: "sprint:complete",
|
|
175
|
+
data: {
|
|
176
|
+
sprint: sprintNum,
|
|
177
|
+
status: "failed",
|
|
178
|
+
attempts: attempt,
|
|
179
|
+
costUsd: this.progress.sprints[sprintNum].costUsd,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
finalize() {
|
|
186
|
+
if (this.aborted) {
|
|
187
|
+
this.progress.status = "stopped";
|
|
188
|
+
this.progress.stoppedAt = new Date().toISOString();
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Check if all sprints passed
|
|
192
|
+
const allPassed = Object.values(this.progress.sprints).every((s) => s.status === "passed");
|
|
193
|
+
this.progress.status = allPassed ? "completed" : "failed";
|
|
194
|
+
}
|
|
195
|
+
this.fileProtocol.writeProgress(this.progress);
|
|
196
|
+
const startMs = new Date(this.progress.startedAt).getTime();
|
|
197
|
+
const endMs = Date.now();
|
|
198
|
+
this.emitEvent({
|
|
199
|
+
type: "run:complete",
|
|
200
|
+
data: {
|
|
201
|
+
status: this.progress.status,
|
|
202
|
+
totalSprints: this.progress.totalSprints,
|
|
203
|
+
totalCostUsd: this.progress.costUsd,
|
|
204
|
+
durationMs: endMs - startMs,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async resume() {
|
|
209
|
+
const saved = this.fileProtocol.readProgress();
|
|
210
|
+
if (!saved) {
|
|
211
|
+
throw new Error("No progress file found. Nothing to resume.");
|
|
212
|
+
}
|
|
213
|
+
this.progress = { ...saved, status: "running" };
|
|
214
|
+
this.aborted = false;
|
|
215
|
+
delete this.progress.stoppedAt;
|
|
216
|
+
// Resume from the current sprint
|
|
217
|
+
const startSprint = this.progress.currentSprint || 1;
|
|
218
|
+
for (let s = startSprint; s <= this.progress.totalSprints; s++) {
|
|
219
|
+
if (this.aborted || this.isBudgetExceeded())
|
|
220
|
+
break;
|
|
221
|
+
const sprintProgress = this.progress.sprints[s];
|
|
222
|
+
if (sprintProgress?.status === "passed")
|
|
223
|
+
continue; // skip completed sprints
|
|
224
|
+
await this.executeSprint(s);
|
|
225
|
+
}
|
|
226
|
+
return this.finalize();
|
|
227
|
+
}
|
|
228
|
+
stop() {
|
|
229
|
+
this.aborted = true;
|
|
230
|
+
this.progress.status = "stopped";
|
|
231
|
+
this.progress.stoppedAt = new Date().toISOString();
|
|
232
|
+
this.fileProtocol.writeProgress(this.progress);
|
|
233
|
+
}
|
|
234
|
+
getProgress() {
|
|
235
|
+
return { ...this.progress };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export interface Stack {
|
|
2
|
+
language: string;
|
|
3
|
+
framework: string | null;
|
|
4
|
+
testRunner: string | null;
|
|
5
|
+
testCommand: string;
|
|
6
|
+
lintCommand: string | null;
|
|
7
|
+
buildCommand: string | null;
|
|
8
|
+
devServer: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface Workspace {
|
|
11
|
+
path: string;
|
|
12
|
+
stack: Stack;
|
|
13
|
+
claudeMd: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ProjectContext {
|
|
16
|
+
repoType: "single" | "monorepo";
|
|
17
|
+
workspaces: Workspace[];
|
|
18
|
+
rootClaudeMd: string | null;
|
|
19
|
+
config: HarnessConfig | null;
|
|
20
|
+
criteria: string | null;
|
|
21
|
+
scope: string[] | null;
|
|
22
|
+
root: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AgentConfig {
|
|
25
|
+
model?: string;
|
|
26
|
+
maxTurns?: number;
|
|
27
|
+
systemPromptAppend?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface WorkspaceConfig {
|
|
30
|
+
path: string;
|
|
31
|
+
language?: string;
|
|
32
|
+
framework?: string;
|
|
33
|
+
testCommand?: string;
|
|
34
|
+
lintCommand?: string;
|
|
35
|
+
buildCommand?: string;
|
|
36
|
+
devServer?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface HarnessConfig {
|
|
39
|
+
agents?: {
|
|
40
|
+
planner?: AgentConfig;
|
|
41
|
+
generator?: AgentConfig;
|
|
42
|
+
evaluator?: AgentConfig;
|
|
43
|
+
};
|
|
44
|
+
workspaces?: Record<string, WorkspaceConfig>;
|
|
45
|
+
maxAttemptsPerSprint?: number;
|
|
46
|
+
maxBudgetPerSprintUsd?: number;
|
|
47
|
+
maxTotalBudgetUsd?: number;
|
|
48
|
+
}
|
|
49
|
+
export type SprintStatus = "pending" | "in_progress" | "passed" | "failed";
|
|
50
|
+
export type RunStatus = "running" | "stopped" | "completed" | "failed";
|
|
51
|
+
export type Phase = "plan" | "decompose" | "contract" | "generate" | "evaluate" | "handoff";
|
|
52
|
+
export interface SprintProgress {
|
|
53
|
+
status: SprintStatus;
|
|
54
|
+
attempts: number;
|
|
55
|
+
costUsd: number;
|
|
56
|
+
}
|
|
57
|
+
export interface Progress {
|
|
58
|
+
status: RunStatus;
|
|
59
|
+
runSpec: string;
|
|
60
|
+
currentSprint: number;
|
|
61
|
+
totalSprints: number;
|
|
62
|
+
currentAttempt: number;
|
|
63
|
+
currentPhase: Phase;
|
|
64
|
+
startedAt: string;
|
|
65
|
+
stoppedAt?: string;
|
|
66
|
+
costUsd: number;
|
|
67
|
+
maxBudgetUsd: number;
|
|
68
|
+
sprints: Record<number, SprintProgress>;
|
|
69
|
+
}
|
|
70
|
+
export interface EvalResult {
|
|
71
|
+
passed: boolean;
|
|
72
|
+
critique: string;
|
|
73
|
+
failedCriteria: string[];
|
|
74
|
+
passedCriteria: string[];
|
|
75
|
+
}
|
|
76
|
+
export type AgentRole = "planner" | "generator" | "evaluator";
|
|
77
|
+
export interface AgentDefinition {
|
|
78
|
+
role: AgentRole;
|
|
79
|
+
systemPrompt: string;
|
|
80
|
+
tools: string[];
|
|
81
|
+
model: string;
|
|
82
|
+
maxTurns: number;
|
|
83
|
+
}
|
|
84
|
+
export interface PhaseStartEvent {
|
|
85
|
+
sprint: number;
|
|
86
|
+
phase: Phase;
|
|
87
|
+
attempt: number;
|
|
88
|
+
}
|
|
89
|
+
export interface AgentActivityEvent {
|
|
90
|
+
sprint: number;
|
|
91
|
+
role: AgentRole;
|
|
92
|
+
tool: string;
|
|
93
|
+
summary: string;
|
|
94
|
+
timestamp: number;
|
|
95
|
+
}
|
|
96
|
+
export interface EvaluationEvent {
|
|
97
|
+
sprint: number;
|
|
98
|
+
attempt: number;
|
|
99
|
+
result: EvalResult;
|
|
100
|
+
}
|
|
101
|
+
export interface CostUpdateEvent {
|
|
102
|
+
sprintCostUsd: number;
|
|
103
|
+
totalCostUsd: number;
|
|
104
|
+
budgetUsd: number;
|
|
105
|
+
}
|
|
106
|
+
export interface SprintCompleteEvent {
|
|
107
|
+
sprint: number;
|
|
108
|
+
status: SprintStatus;
|
|
109
|
+
attempts: number;
|
|
110
|
+
costUsd: number;
|
|
111
|
+
}
|
|
112
|
+
export interface RunCompleteEvent {
|
|
113
|
+
status: RunStatus;
|
|
114
|
+
totalSprints: number;
|
|
115
|
+
totalCostUsd: number;
|
|
116
|
+
durationMs: number;
|
|
117
|
+
}
|
|
118
|
+
export type HarnessEvent = {
|
|
119
|
+
type: "phase:start";
|
|
120
|
+
data: PhaseStartEvent;
|
|
121
|
+
} | {
|
|
122
|
+
type: "agent:activity";
|
|
123
|
+
data: AgentActivityEvent;
|
|
124
|
+
} | {
|
|
125
|
+
type: "evaluation";
|
|
126
|
+
data: EvaluationEvent;
|
|
127
|
+
} | {
|
|
128
|
+
type: "cost:update";
|
|
129
|
+
data: CostUpdateEvent;
|
|
130
|
+
} | {
|
|
131
|
+
type: "sprint:complete";
|
|
132
|
+
data: SprintCompleteEvent;
|
|
133
|
+
} | {
|
|
134
|
+
type: "run:complete";
|
|
135
|
+
data: RunCompleteEvent;
|
|
136
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,kCAAkC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HarnessEvent } from "../core/types.js";
|
|
2
|
+
export declare class DashboardServer {
|
|
3
|
+
private server;
|
|
4
|
+
private broadcaster;
|
|
5
|
+
private port;
|
|
6
|
+
private running;
|
|
7
|
+
constructor(port?: number);
|
|
8
|
+
private handleRequest;
|
|
9
|
+
start(): Promise<void>;
|
|
10
|
+
stop(): Promise<void>;
|
|
11
|
+
isRunning(): boolean;
|
|
12
|
+
getUrl(): string;
|
|
13
|
+
getPort(): number;
|
|
14
|
+
broadcast(event: HarnessEvent): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { WebSocketBroadcaster } from "./socket.js";
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
export class DashboardServer {
|
|
8
|
+
server;
|
|
9
|
+
broadcaster;
|
|
10
|
+
port;
|
|
11
|
+
running = false;
|
|
12
|
+
constructor(port = 3117) {
|
|
13
|
+
this.port = port;
|
|
14
|
+
this.server = createServer(this.handleRequest.bind(this));
|
|
15
|
+
this.broadcaster = new WebSocketBroadcaster(this.server);
|
|
16
|
+
}
|
|
17
|
+
handleRequest(req, res) {
|
|
18
|
+
// Serve the dashboard HTML for any request
|
|
19
|
+
try {
|
|
20
|
+
const htmlPath = join(__dirname, "static", "index.html");
|
|
21
|
+
const html = readFileSync(htmlPath, "utf-8");
|
|
22
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
23
|
+
res.end(html);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
27
|
+
res.end("Dashboard file not found");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async start() {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
this.server.listen(this.port, () => {
|
|
33
|
+
this.running = true;
|
|
34
|
+
resolve();
|
|
35
|
+
});
|
|
36
|
+
this.server.on("error", reject);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async stop() {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
this.broadcaster.close();
|
|
42
|
+
this.server.close(() => {
|
|
43
|
+
this.running = false;
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
isRunning() {
|
|
49
|
+
return this.running;
|
|
50
|
+
}
|
|
51
|
+
getUrl() {
|
|
52
|
+
return `http://localhost:${this.port}`;
|
|
53
|
+
}
|
|
54
|
+
getPort() {
|
|
55
|
+
return this.port;
|
|
56
|
+
}
|
|
57
|
+
broadcast(event) {
|
|
58
|
+
this.broadcaster.broadcast(event);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,MAAM,OAAO,eAAe;IAClB,MAAM,CAAS;IACf,WAAW,CAAuB;IAClC,IAAI,CAAS;IACb,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,IAAI,GAAG,IAAI;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC;IAEO,aAAa,CAAC,GAAoB,EAAE,GAAmB;QAC7D,2CAA2C;QAC3C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC7C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;YACpD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;gBACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,MAAM;QACJ,OAAO,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,SAAS,CAAC,KAAmB;QAC3B,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
2
|
+
export class WebSocketBroadcaster {
|
|
3
|
+
wss;
|
|
4
|
+
constructor(server) {
|
|
5
|
+
this.wss = new WebSocketServer({ server });
|
|
6
|
+
}
|
|
7
|
+
broadcast(event) {
|
|
8
|
+
const data = JSON.stringify(event);
|
|
9
|
+
for (const client of this.wss.clients) {
|
|
10
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
11
|
+
client.send(data);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
close() {
|
|
16
|
+
for (const client of this.wss.clients) {
|
|
17
|
+
client.close();
|
|
18
|
+
}
|
|
19
|
+
this.wss.close();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/dashboard/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAIhD,MAAM,OAAO,oBAAoB;IACvB,GAAG,CAAkB;IAE7B,YAAY,MAAc;QACxB,IAAI,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,SAAS,CAAC,KAAmB;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACtC,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DEFAULT_CRITERIA = "## Default Evaluation Criteria\n\n### Correctness\n- All features specified in the contract are implemented and functional\n- No placeholder, stubbed, or mocked implementations in production code\n- Code runs without runtime errors\n\n### Testing\n- New features have corresponding tests\n- All tests pass when the test suite is run\n- Tests cover the primary success path and key edge cases\n\n### Code Quality\n- Code follows project conventions (from CLAUDE.md if present)\n- No leftover TODO or FIXME comments\n- No debug logging or commented-out code left in place\n- Imports are clean \u2014 no unused imports\n\n### Integration\n- New code integrates with existing codebase without breaking existing functionality\n- Existing tests still pass after changes\n";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const DEFAULT_CRITERIA = `## Default Evaluation Criteria
|
|
2
|
+
|
|
3
|
+
### Correctness
|
|
4
|
+
- All features specified in the contract are implemented and functional
|
|
5
|
+
- No placeholder, stubbed, or mocked implementations in production code
|
|
6
|
+
- Code runs without runtime errors
|
|
7
|
+
|
|
8
|
+
### Testing
|
|
9
|
+
- New features have corresponding tests
|
|
10
|
+
- All tests pass when the test suite is run
|
|
11
|
+
- Tests cover the primary success path and key edge cases
|
|
12
|
+
|
|
13
|
+
### Code Quality
|
|
14
|
+
- Code follows project conventions (from CLAUDE.md if present)
|
|
15
|
+
- No leftover TODO or FIXME comments
|
|
16
|
+
- No debug logging or commented-out code left in place
|
|
17
|
+
- Imports are clean — no unused imports
|
|
18
|
+
|
|
19
|
+
### Integration
|
|
20
|
+
- New code integrates with existing codebase without breaking existing functionality
|
|
21
|
+
- Existing tests still pass after changes
|
|
22
|
+
`;
|
|
23
|
+
//# sourceMappingURL=criteria.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"criteria.js","sourceRoot":"","sources":["../../src/defaults/criteria.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ProjectContext, AgentRole } from "../core/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Format a ProjectContext into a readable text block for inclusion in system prompts.
|
|
4
|
+
*/
|
|
5
|
+
export declare function formatProjectContext(ctx: ProjectContext): string;
|
|
6
|
+
/**
|
|
7
|
+
* Build a full system prompt for an agent role.
|
|
8
|
+
*
|
|
9
|
+
* Assembles:
|
|
10
|
+
* 1. Base prompt for the role
|
|
11
|
+
* 2. PROJECT CONTEXT section (formatted project context)
|
|
12
|
+
* 3. EVALUATION CRITERIA section (evaluator only — default + custom criteria)
|
|
13
|
+
* 4. ADDITIONAL INSTRUCTIONS section (appendPrompt if provided)
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildSystemPrompt(role: AgentRole, ctx: ProjectContext, appendPrompt?: string): string;
|