@zhijiewang/openharness 1.0.0 → 1.2.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 +741 -741
- package/dist/commands/index.js +22 -1
- package/dist/harness/config.d.ts +17 -0
- package/dist/harness/telemetry.d.ts +59 -0
- package/dist/harness/telemetry.js +129 -0
- package/dist/main.js +40 -40
- package/dist/providers/router.d.ts +48 -0
- package/dist/providers/router.js +61 -0
- package/dist/query/compress.d.ts +5 -0
- package/dist/query/compress.js +45 -4
- package/dist/remote/auth.d.ts +25 -0
- package/dist/remote/auth.js +73 -0
- package/dist/remote/server.d.ts +18 -2
- package/dist/remote/server.js +168 -39
- package/dist/repl.js +8 -0
- package/dist/services/PipelineExecutor.d.ts +48 -0
- package/dist/services/PipelineExecutor.js +179 -0
- package/dist/services/a2a.d.ts +119 -0
- package/dist/services/a2a.js +176 -0
- package/dist/tools/PipelineTool/index.d.ts +40 -0
- package/dist/tools/PipelineTool/index.js +53 -0
- package/dist/tools/WebFetchTool/index.js +2 -2
- package/dist/tools.js +3 -0
- package/package.json +73 -73
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Protocol — Agent-to-Agent discovery and routing.
|
|
3
|
+
*
|
|
4
|
+
* Enables agents running in separate processes (or machines) to:
|
|
5
|
+
* - Advertise their capabilities via Agent Cards
|
|
6
|
+
* - Discover other agents via a shared registry
|
|
7
|
+
* - Route messages to agents by name or capability
|
|
8
|
+
* - Delegate tasks with typed request/response
|
|
9
|
+
*
|
|
10
|
+
* Registry is file-based (~/.oh/agents/) for same-machine agents.
|
|
11
|
+
* Each running agent writes a card file on startup and removes it on exit.
|
|
12
|
+
*
|
|
13
|
+
* Based on the emerging A2A (Agent-to-Agent) protocol standard.
|
|
14
|
+
*/
|
|
15
|
+
export type AgentCard = {
|
|
16
|
+
/** Unique agent instance ID */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Human-readable name */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Agent version */
|
|
21
|
+
version: string;
|
|
22
|
+
/** What this agent can do */
|
|
23
|
+
capabilities: AgentCapability[];
|
|
24
|
+
/** How to reach this agent */
|
|
25
|
+
endpoint: AgentEndpoint;
|
|
26
|
+
/** When this card was published */
|
|
27
|
+
registeredAt: number;
|
|
28
|
+
/** PID of the agent process */
|
|
29
|
+
pid: number;
|
|
30
|
+
/** Provider and model info */
|
|
31
|
+
provider?: string;
|
|
32
|
+
model?: string;
|
|
33
|
+
/** Working directory */
|
|
34
|
+
workingDir?: string;
|
|
35
|
+
};
|
|
36
|
+
export type AgentCapability = {
|
|
37
|
+
/** Capability identifier (e.g., 'code-review', 'test-generation') */
|
|
38
|
+
name: string;
|
|
39
|
+
/** Human description */
|
|
40
|
+
description: string;
|
|
41
|
+
/** Input schema (JSON Schema format) */
|
|
42
|
+
inputSchema?: Record<string, unknown>;
|
|
43
|
+
/** Output schema */
|
|
44
|
+
outputSchema?: Record<string, unknown>;
|
|
45
|
+
};
|
|
46
|
+
export type AgentEndpoint = {
|
|
47
|
+
/** Transport type */
|
|
48
|
+
type: 'http' | 'ipc' | 'stdio';
|
|
49
|
+
/** Address (URL for http, socket path for ipc, pid for stdio) */
|
|
50
|
+
address: string;
|
|
51
|
+
/** Port for HTTP transport */
|
|
52
|
+
port?: number;
|
|
53
|
+
};
|
|
54
|
+
export type A2AMessage = {
|
|
55
|
+
/** Message ID */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Source agent ID */
|
|
58
|
+
from: string;
|
|
59
|
+
/** Target agent ID or capability name */
|
|
60
|
+
to: string;
|
|
61
|
+
/** Message type */
|
|
62
|
+
type: 'task' | 'result' | 'status' | 'cancel' | 'discover';
|
|
63
|
+
/** Payload */
|
|
64
|
+
payload: A2APayload;
|
|
65
|
+
/** Timestamp */
|
|
66
|
+
timestamp: number;
|
|
67
|
+
};
|
|
68
|
+
export type A2APayload = {
|
|
69
|
+
kind: 'task';
|
|
70
|
+
capability: string;
|
|
71
|
+
input: unknown;
|
|
72
|
+
timeout?: number;
|
|
73
|
+
} | {
|
|
74
|
+
kind: 'result';
|
|
75
|
+
taskId: string;
|
|
76
|
+
output: unknown;
|
|
77
|
+
error?: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'status';
|
|
80
|
+
state: 'idle' | 'working' | 'done' | 'error';
|
|
81
|
+
progress?: string;
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'cancel';
|
|
84
|
+
taskId: string;
|
|
85
|
+
reason?: string;
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'discover';
|
|
88
|
+
filter?: {
|
|
89
|
+
capability?: string;
|
|
90
|
+
name?: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/** Publish an agent card to the shared registry */
|
|
94
|
+
export declare function publishCard(card: AgentCard): void;
|
|
95
|
+
/** Remove an agent card from the registry */
|
|
96
|
+
export declare function unpublishCard(agentId: string): void;
|
|
97
|
+
/** Discover all registered agents */
|
|
98
|
+
export declare function discoverAgents(): AgentCard[];
|
|
99
|
+
/** Find agents by capability name */
|
|
100
|
+
export declare function findAgentsByCapability(capabilityName: string): AgentCard[];
|
|
101
|
+
/** Find an agent by name */
|
|
102
|
+
export declare function findAgentByName(name: string): AgentCard | null;
|
|
103
|
+
/**
|
|
104
|
+
* Route a message to an agent.
|
|
105
|
+
* For HTTP endpoints: sends via fetch.
|
|
106
|
+
* For IPC/stdio: writes to the agent's inbox file.
|
|
107
|
+
*/
|
|
108
|
+
export declare function routeMessage(message: A2AMessage): Promise<A2AMessage | null>;
|
|
109
|
+
/** Read pending messages from an agent's inbox */
|
|
110
|
+
export declare function readInbox(agentId: string): A2AMessage[];
|
|
111
|
+
/** Generate a unique message ID */
|
|
112
|
+
export declare function generateMessageId(): string;
|
|
113
|
+
/** Create a standard agent card for the current openHarness session */
|
|
114
|
+
export declare function createSessionCard(sessionId: string, opts?: {
|
|
115
|
+
provider?: string;
|
|
116
|
+
model?: string;
|
|
117
|
+
port?: number;
|
|
118
|
+
}): AgentCard;
|
|
119
|
+
//# sourceMappingURL=a2a.d.ts.map
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Protocol — Agent-to-Agent discovery and routing.
|
|
3
|
+
*
|
|
4
|
+
* Enables agents running in separate processes (or machines) to:
|
|
5
|
+
* - Advertise their capabilities via Agent Cards
|
|
6
|
+
* - Discover other agents via a shared registry
|
|
7
|
+
* - Route messages to agents by name or capability
|
|
8
|
+
* - Delegate tasks with typed request/response
|
|
9
|
+
*
|
|
10
|
+
* Registry is file-based (~/.oh/agents/) for same-machine agents.
|
|
11
|
+
* Each running agent writes a card file on startup and removes it on exit.
|
|
12
|
+
*
|
|
13
|
+
* Based on the emerging A2A (Agent-to-Agent) protocol standard.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, existsSync, unlinkSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
const AGENT_REGISTRY_DIR = join(homedir(), '.oh', 'agents');
|
|
19
|
+
// ── Registry Operations ──
|
|
20
|
+
/** Publish an agent card to the shared registry */
|
|
21
|
+
export function publishCard(card) {
|
|
22
|
+
mkdirSync(AGENT_REGISTRY_DIR, { recursive: true });
|
|
23
|
+
const filePath = join(AGENT_REGISTRY_DIR, `${card.id}.json`);
|
|
24
|
+
writeFileSync(filePath, JSON.stringify(card, null, 2));
|
|
25
|
+
}
|
|
26
|
+
/** Remove an agent card from the registry */
|
|
27
|
+
export function unpublishCard(agentId) {
|
|
28
|
+
const filePath = join(AGENT_REGISTRY_DIR, `${agentId}.json`);
|
|
29
|
+
try {
|
|
30
|
+
unlinkSync(filePath);
|
|
31
|
+
}
|
|
32
|
+
catch { /* ignore */ }
|
|
33
|
+
}
|
|
34
|
+
/** Discover all registered agents */
|
|
35
|
+
export function discoverAgents() {
|
|
36
|
+
if (!existsSync(AGENT_REGISTRY_DIR))
|
|
37
|
+
return [];
|
|
38
|
+
const cards = [];
|
|
39
|
+
for (const file of readdirSync(AGENT_REGISTRY_DIR).filter(f => f.endsWith('.json'))) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = readFileSync(join(AGENT_REGISTRY_DIR, file), 'utf-8');
|
|
42
|
+
const card = JSON.parse(raw);
|
|
43
|
+
// Check if the agent process is still alive
|
|
44
|
+
if (isProcessAlive(card.pid)) {
|
|
45
|
+
cards.push(card);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Stale card — clean up
|
|
49
|
+
try {
|
|
50
|
+
unlinkSync(join(AGENT_REGISTRY_DIR, file));
|
|
51
|
+
}
|
|
52
|
+
catch { /* ignore */ }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch { /* skip malformed cards */ }
|
|
56
|
+
}
|
|
57
|
+
return cards;
|
|
58
|
+
}
|
|
59
|
+
/** Find agents by capability name */
|
|
60
|
+
export function findAgentsByCapability(capabilityName) {
|
|
61
|
+
return discoverAgents().filter(card => card.capabilities.some(c => c.name.toLowerCase() === capabilityName.toLowerCase()));
|
|
62
|
+
}
|
|
63
|
+
/** Find an agent by name */
|
|
64
|
+
export function findAgentByName(name) {
|
|
65
|
+
return discoverAgents().find(c => c.name.toLowerCase() === name.toLowerCase()) ?? null;
|
|
66
|
+
}
|
|
67
|
+
// ── Message Routing ──
|
|
68
|
+
/**
|
|
69
|
+
* Route a message to an agent.
|
|
70
|
+
* For HTTP endpoints: sends via fetch.
|
|
71
|
+
* For IPC/stdio: writes to the agent's inbox file.
|
|
72
|
+
*/
|
|
73
|
+
export async function routeMessage(message) {
|
|
74
|
+
// Find the target agent
|
|
75
|
+
let targetCard = null;
|
|
76
|
+
// Try by agent ID first
|
|
77
|
+
const agents = discoverAgents();
|
|
78
|
+
targetCard = agents.find(a => a.id === message.to) ?? null;
|
|
79
|
+
// Try by name
|
|
80
|
+
if (!targetCard) {
|
|
81
|
+
targetCard = agents.find(a => a.name.toLowerCase() === message.to.toLowerCase()) ?? null;
|
|
82
|
+
}
|
|
83
|
+
// Try by capability
|
|
84
|
+
if (!targetCard && message.type === 'task' && message.payload.kind === 'task') {
|
|
85
|
+
const capable = findAgentsByCapability(message.payload.capability);
|
|
86
|
+
if (capable.length > 0)
|
|
87
|
+
targetCard = capable[0];
|
|
88
|
+
}
|
|
89
|
+
if (!targetCard)
|
|
90
|
+
return null;
|
|
91
|
+
// Route based on endpoint type
|
|
92
|
+
switch (targetCard.endpoint.type) {
|
|
93
|
+
case 'http': {
|
|
94
|
+
try {
|
|
95
|
+
const url = `${targetCard.endpoint.address}${targetCard.endpoint.port ? ':' + targetCard.endpoint.port : ''}/a2a`;
|
|
96
|
+
const res = await fetch(url, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
body: JSON.stringify(message),
|
|
100
|
+
signal: AbortSignal.timeout(30_000),
|
|
101
|
+
});
|
|
102
|
+
if (res.ok) {
|
|
103
|
+
return await res.json();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch { /* delivery failed */ }
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
case 'ipc': {
|
|
110
|
+
// File-based inbox for local IPC
|
|
111
|
+
const inboxDir = join(AGENT_REGISTRY_DIR, 'inboxes', targetCard.id);
|
|
112
|
+
mkdirSync(inboxDir, { recursive: true });
|
|
113
|
+
const msgFile = join(inboxDir, `${message.id}.json`);
|
|
114
|
+
writeFileSync(msgFile, JSON.stringify(message, null, 2));
|
|
115
|
+
return null; // Async — no immediate response
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Read pending messages from an agent's inbox */
|
|
122
|
+
export function readInbox(agentId) {
|
|
123
|
+
const inboxDir = join(AGENT_REGISTRY_DIR, 'inboxes', agentId);
|
|
124
|
+
if (!existsSync(inboxDir))
|
|
125
|
+
return [];
|
|
126
|
+
const messages = [];
|
|
127
|
+
for (const file of readdirSync(inboxDir).filter(f => f.endsWith('.json'))) {
|
|
128
|
+
try {
|
|
129
|
+
const raw = readFileSync(join(inboxDir, file), 'utf-8');
|
|
130
|
+
messages.push(JSON.parse(raw));
|
|
131
|
+
// Remove after reading
|
|
132
|
+
unlinkSync(join(inboxDir, file));
|
|
133
|
+
}
|
|
134
|
+
catch { /* skip */ }
|
|
135
|
+
}
|
|
136
|
+
return messages.sort((a, b) => a.timestamp - b.timestamp);
|
|
137
|
+
}
|
|
138
|
+
// ── Helpers ──
|
|
139
|
+
/** Check if a process is still alive */
|
|
140
|
+
function isProcessAlive(pid) {
|
|
141
|
+
try {
|
|
142
|
+
process.kill(pid, 0); // Signal 0 = check existence only
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Generate a unique message ID */
|
|
150
|
+
export function generateMessageId() {
|
|
151
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
152
|
+
}
|
|
153
|
+
/** Create a standard agent card for the current openHarness session */
|
|
154
|
+
export function createSessionCard(sessionId, opts = {}) {
|
|
155
|
+
return {
|
|
156
|
+
id: `oh-${sessionId}`,
|
|
157
|
+
name: `openharness-${sessionId.slice(0, 6)}`,
|
|
158
|
+
version: '1.0.0',
|
|
159
|
+
capabilities: [
|
|
160
|
+
{ name: 'code-generation', description: 'Generate, edit, and review code' },
|
|
161
|
+
{ name: 'code-review', description: 'Review code for bugs and quality' },
|
|
162
|
+
{ name: 'test-generation', description: 'Write tests for existing code' },
|
|
163
|
+
{ name: 'file-operations', description: 'Read, write, search files' },
|
|
164
|
+
{ name: 'bash-execution', description: 'Run shell commands' },
|
|
165
|
+
],
|
|
166
|
+
endpoint: opts.port
|
|
167
|
+
? { type: 'http', address: 'http://localhost', port: opts.port }
|
|
168
|
+
: { type: 'ipc', address: join(AGENT_REGISTRY_DIR, 'inboxes', `oh-${sessionId}`) },
|
|
169
|
+
registeredAt: Date.now(),
|
|
170
|
+
pid: process.pid,
|
|
171
|
+
provider: opts.provider,
|
|
172
|
+
model: opts.model,
|
|
173
|
+
workingDir: process.cwd(),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=a2a.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Tool } from "../../Tool.js";
|
|
3
|
+
declare const inputSchema: z.ZodObject<{
|
|
4
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
5
|
+
id: z.ZodString;
|
|
6
|
+
tool: z.ZodString;
|
|
7
|
+
args: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
8
|
+
dependsOn: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
tool: string;
|
|
11
|
+
args: Record<string, unknown>;
|
|
12
|
+
id: string;
|
|
13
|
+
dependsOn?: string[] | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
tool: string;
|
|
16
|
+
args: Record<string, unknown>;
|
|
17
|
+
id: string;
|
|
18
|
+
dependsOn?: string[] | undefined;
|
|
19
|
+
}>, "many">;
|
|
20
|
+
description: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
steps: {
|
|
23
|
+
tool: string;
|
|
24
|
+
args: Record<string, unknown>;
|
|
25
|
+
id: string;
|
|
26
|
+
dependsOn?: string[] | undefined;
|
|
27
|
+
}[];
|
|
28
|
+
description?: string | undefined;
|
|
29
|
+
}, {
|
|
30
|
+
steps: {
|
|
31
|
+
tool: string;
|
|
32
|
+
args: Record<string, unknown>;
|
|
33
|
+
id: string;
|
|
34
|
+
dependsOn?: string[] | undefined;
|
|
35
|
+
}[];
|
|
36
|
+
description?: string | undefined;
|
|
37
|
+
}>;
|
|
38
|
+
export declare const PipelineTool: Tool<typeof inputSchema>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PipelineExecutor, formatPipelineResults } from "../../services/PipelineExecutor.js";
|
|
3
|
+
const stepSchema = z.object({
|
|
4
|
+
id: z.string().describe("Unique step identifier"),
|
|
5
|
+
tool: z.string().describe("Tool name to execute (Glob, Grep, Read, Bash, etc.)"),
|
|
6
|
+
args: z.record(z.unknown()).describe("Tool arguments. Use $stepId to reference output of a prior step."),
|
|
7
|
+
dependsOn: z.array(z.string()).optional().describe("Step IDs that must complete before this step runs"),
|
|
8
|
+
});
|
|
9
|
+
const inputSchema = z.object({
|
|
10
|
+
steps: z.array(stepSchema).min(1).describe("Pipeline steps to execute in dependency order"),
|
|
11
|
+
description: z.string().optional().describe("What this pipeline does"),
|
|
12
|
+
});
|
|
13
|
+
export const PipelineTool = {
|
|
14
|
+
name: "Pipeline",
|
|
15
|
+
description: "Execute a declarative multi-step tool pipeline. Steps run in dependency order with variable substitution.",
|
|
16
|
+
inputSchema,
|
|
17
|
+
riskLevel: "medium",
|
|
18
|
+
isReadOnly(input) {
|
|
19
|
+
// Pipeline is read-only only if ALL steps use read-only tools
|
|
20
|
+
// Conservative: assume not read-only
|
|
21
|
+
return false;
|
|
22
|
+
},
|
|
23
|
+
isConcurrencySafe() {
|
|
24
|
+
return false;
|
|
25
|
+
},
|
|
26
|
+
async call(input, context) {
|
|
27
|
+
if (!context.tools) {
|
|
28
|
+
return { output: "Pipeline unavailable: no tools in context.", isError: true };
|
|
29
|
+
}
|
|
30
|
+
const executor = new PipelineExecutor(context.tools, context);
|
|
31
|
+
const results = await executor.execute(input.steps);
|
|
32
|
+
const summary = formatPipelineResults(results);
|
|
33
|
+
const hasErrors = results.some(r => r.isError);
|
|
34
|
+
return { output: summary, isError: hasErrors };
|
|
35
|
+
},
|
|
36
|
+
prompt() {
|
|
37
|
+
return `Execute a declarative multi-step tool pipeline. Each step specifies a tool and its arguments, with optional dependencies on prior steps. Use $stepId in args to reference the output of a completed step.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
{
|
|
41
|
+
"steps": [
|
|
42
|
+
{ "id": "find", "tool": "Glob", "args": { "pattern": "src/**/*.ts" } },
|
|
43
|
+
{ "id": "search", "tool": "Grep", "args": { "pattern": "TODO", "path": "$find" }, "dependsOn": ["find"] }
|
|
44
|
+
],
|
|
45
|
+
"description": "Find all TODO comments in TypeScript files"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Parameters:
|
|
49
|
+
- steps (array, required): Pipeline steps with id, tool, args, and optional dependsOn
|
|
50
|
+
- description (string, optional): What this pipeline does`;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -95,8 +95,8 @@ export const WebFetchTool = {
|
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
prompt() {
|
|
98
|
-
return `Fetch a URL and return its text content. Parameters:
|
|
99
|
-
- url (string, required): The URL to fetch (http/https only).
|
|
98
|
+
return `Fetch a URL and return its text content. Parameters:
|
|
99
|
+
- url (string, required): The URL to fetch (http/https only).
|
|
100
100
|
HTML tags are stripped. Output is truncated at 50K characters. Private/internal hosts are blocked for security.`;
|
|
101
101
|
},
|
|
102
102
|
};
|
package/dist/tools.js
CHANGED
|
@@ -41,6 +41,7 @@ import { ExitWorktreeTool } from "./tools/ExitWorktreeTool/index.js";
|
|
|
41
41
|
import { KillProcessTool } from "./tools/KillProcessTool/index.js";
|
|
42
42
|
import { RemoteTriggerTool } from "./tools/RemoteTriggerTool/index.js";
|
|
43
43
|
import { MultiEditTool } from "./tools/MultiEditTool/index.js";
|
|
44
|
+
import { PipelineTool } from "./tools/PipelineTool/index.js";
|
|
44
45
|
/**
|
|
45
46
|
* Returns all registered tools.
|
|
46
47
|
*
|
|
@@ -71,6 +72,8 @@ export function getAllTools() {
|
|
|
71
72
|
ExitPlanModeTool,
|
|
72
73
|
// Tool Discovery
|
|
73
74
|
ToolSearchTool,
|
|
75
|
+
// Pipelines
|
|
76
|
+
PipelineTool,
|
|
74
77
|
// Memory management
|
|
75
78
|
MemoryTool,
|
|
76
79
|
];
|
package/package.json
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@zhijiewang/openharness",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Open-source terminal coding agent. Works with any LLM.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"openharness": "./dist/main.js",
|
|
8
|
-
"oh": "./dist/main.js"
|
|
9
|
-
},
|
|
10
|
-
"main": "./dist/main.js",
|
|
11
|
-
"files": [
|
|
12
|
-
"dist/**/*.js",
|
|
13
|
-
"dist/**/*.d.ts",
|
|
14
|
-
"!dist/**/*.test.*",
|
|
15
|
-
"!dist/**/test-helpers.*",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"dev": "tsx src/main.tsx",
|
|
21
|
-
"build": "tsc",
|
|
22
|
-
"prepare": "tsc",
|
|
23
|
-
"prepublishOnly": "npm run build",
|
|
24
|
-
"test": "node scripts/test.mjs",
|
|
25
|
-
"test:coverage": "node scripts/coverage.mjs",
|
|
26
|
-
"typecheck": "tsc --noEmit",
|
|
27
|
-
"start": "node dist/main.js"
|
|
28
|
-
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"@types/marked": "^5.0.2",
|
|
31
|
-
"chalk": "^5.4.1",
|
|
32
|
-
"commander": "^13.0.0",
|
|
33
|
-
"ink": "^5.2.0",
|
|
34
|
-
"ink-spinner": "^5.0.0",
|
|
35
|
-
"ink-text-input": "^6.0.0",
|
|
36
|
-
"marked": "^17.0.5",
|
|
37
|
-
"react": "^18.3.1",
|
|
38
|
-
"yaml": "^2.7.0",
|
|
39
|
-
"zod": "^3.24.0"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/node": "^22.0.0",
|
|
43
|
-
"@types/react": "^18.3.0",
|
|
44
|
-
"c8": "^11.0.0",
|
|
45
|
-
"sharp": "^0.34.5",
|
|
46
|
-
"tsx": "^4.19.0",
|
|
47
|
-
"typescript": "^5.8.0"
|
|
48
|
-
},
|
|
49
|
-
"engines": {
|
|
50
|
-
"node": ">=18.0.0"
|
|
51
|
-
},
|
|
52
|
-
"keywords": [
|
|
53
|
-
"ai",
|
|
54
|
-
"agent",
|
|
55
|
-
"llm",
|
|
56
|
-
"cli",
|
|
57
|
-
"coding-agent",
|
|
58
|
-
"terminal",
|
|
59
|
-
"coding-assistant",
|
|
60
|
-
"ollama",
|
|
61
|
-
"openai",
|
|
62
|
-
"anthropic"
|
|
63
|
-
],
|
|
64
|
-
"license": "MIT",
|
|
65
|
-
"repository": {
|
|
66
|
-
"type": "git",
|
|
67
|
-
"url": "https://github.com/zhijiewong/openharness"
|
|
68
|
-
},
|
|
69
|
-
"bugs": {
|
|
70
|
-
"url": "https://github.com/zhijiewong/openharness/issues"
|
|
71
|
-
},
|
|
72
|
-
"homepage": "https://github.com/zhijiewong/openharness#readme"
|
|
73
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhijiewang/openharness",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Open-source terminal coding agent. Works with any LLM.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"openharness": "./dist/main.js",
|
|
8
|
+
"oh": "./dist/main.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/main.js",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/**/*.js",
|
|
13
|
+
"dist/**/*.d.ts",
|
|
14
|
+
"!dist/**/*.test.*",
|
|
15
|
+
"!dist/**/test-helpers.*",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "tsx src/main.tsx",
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepare": "tsc",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"test": "node scripts/test.mjs",
|
|
25
|
+
"test:coverage": "node scripts/coverage.mjs",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"start": "node dist/main.js"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@types/marked": "^5.0.2",
|
|
31
|
+
"chalk": "^5.4.1",
|
|
32
|
+
"commander": "^13.0.0",
|
|
33
|
+
"ink": "^5.2.0",
|
|
34
|
+
"ink-spinner": "^5.0.0",
|
|
35
|
+
"ink-text-input": "^6.0.0",
|
|
36
|
+
"marked": "^17.0.5",
|
|
37
|
+
"react": "^18.3.1",
|
|
38
|
+
"yaml": "^2.7.0",
|
|
39
|
+
"zod": "^3.24.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"@types/react": "^18.3.0",
|
|
44
|
+
"c8": "^11.0.0",
|
|
45
|
+
"sharp": "^0.34.5",
|
|
46
|
+
"tsx": "^4.19.0",
|
|
47
|
+
"typescript": "^5.8.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"ai",
|
|
54
|
+
"agent",
|
|
55
|
+
"llm",
|
|
56
|
+
"cli",
|
|
57
|
+
"coding-agent",
|
|
58
|
+
"terminal",
|
|
59
|
+
"coding-assistant",
|
|
60
|
+
"ollama",
|
|
61
|
+
"openai",
|
|
62
|
+
"anthropic"
|
|
63
|
+
],
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "https://github.com/zhijiewong/openharness"
|
|
68
|
+
},
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/zhijiewong/openharness/issues"
|
|
71
|
+
},
|
|
72
|
+
"homepage": "https://github.com/zhijiewong/openharness#readme"
|
|
73
|
+
}
|