@zhijiewang/openharness 2.5.0 → 2.8.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 +1 -1
- package/dist/commands/ai.d.ts +6 -0
- package/dist/commands/ai.js +244 -0
- package/dist/commands/git.d.ts +6 -0
- package/dist/commands/git.js +167 -0
- package/dist/commands/index.d.ts +10 -31
- package/dist/commands/index.js +22 -1096
- package/dist/commands/info.d.ts +8 -0
- package/dist/commands/info.js +671 -0
- package/dist/commands/session.d.ts +6 -0
- package/dist/commands/session.js +214 -0
- package/dist/commands/settings.d.ts +6 -0
- package/dist/commands/settings.js +187 -0
- package/dist/commands/skills.d.ts +6 -0
- package/dist/commands/skills.js +117 -0
- package/dist/commands/types.d.ts +36 -0
- package/dist/commands/types.js +5 -0
- package/dist/components/InitWizard.js +60 -62
- package/dist/harness/hooks.js +9 -6
- package/dist/providers/anthropic.js +7 -8
- package/dist/providers/openai.js +3 -2
- package/dist/renderer/layout-sections.d.ts +56 -0
- package/dist/renderer/layout-sections.js +462 -0
- package/dist/renderer/layout.d.ts +4 -2
- package/dist/renderer/layout.js +25 -500
- package/dist/tools/TodoWriteTool/index.d.ts +37 -0
- package/dist/tools/TodoWriteTool/index.js +78 -0
- package/dist/tools.js +2 -0
- package/package.json +1 -1
|
@@ -92,6 +92,65 @@ export default function InitWizard({ onDone }) {
|
|
|
92
92
|
const [selectedMcp, setSelectedMcp] = useState(new Set());
|
|
93
93
|
const [mcpIdx, setMcpIdx] = useState(0);
|
|
94
94
|
const provider = PROVIDERS[providerIdx];
|
|
95
|
+
// ── Connection test ──
|
|
96
|
+
const runTest = useCallback(async (prov, key, url) => {
|
|
97
|
+
setTestStatus("testing");
|
|
98
|
+
try {
|
|
99
|
+
const { createProviderInstance } = await import("../providers/index.js");
|
|
100
|
+
const p = createProviderInstance(prov.key, {
|
|
101
|
+
name: prov.key,
|
|
102
|
+
apiKey: key || process.env[`${prov.key.toUpperCase()}_API_KEY`],
|
|
103
|
+
baseUrl: url || prov.defaultBaseUrl,
|
|
104
|
+
defaultModel: prov.defaultModel,
|
|
105
|
+
});
|
|
106
|
+
const fetched = "fetchModels" in p && typeof p.fetchModels === "function"
|
|
107
|
+
? await p.fetchModels()
|
|
108
|
+
: p.listModels();
|
|
109
|
+
const modelNames = fetched.map((m) => m.id);
|
|
110
|
+
setAvailableModels(modelNames.length > 0 ? modelNames : [prov.defaultModel]);
|
|
111
|
+
setTestStatus("ok");
|
|
112
|
+
setTimeout(() => setStep("model"), 600);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
setTestStatus("fail");
|
|
116
|
+
setTestError(err instanceof Error ? err.message : String(err));
|
|
117
|
+
setAvailableModels([prov.defaultModel]);
|
|
118
|
+
setTimeout(() => setStep("model"), 800);
|
|
119
|
+
}
|
|
120
|
+
}, []);
|
|
121
|
+
// ── Write final config ──
|
|
122
|
+
const writeFinal = useCallback(() => {
|
|
123
|
+
const selectedModel = availableModels.length > 0 ? (availableModels[modelIdx] ?? model) : model;
|
|
124
|
+
// Build MCP server configs from selected registry entries
|
|
125
|
+
let mcpServers;
|
|
126
|
+
if (selectedMcp.size > 0) {
|
|
127
|
+
try {
|
|
128
|
+
const { MCP_REGISTRY } = require("../mcp/registry.js");
|
|
129
|
+
mcpServers = [...selectedMcp]
|
|
130
|
+
.map((name) => MCP_REGISTRY.find((e) => e.name === name))
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.map((e) => ({
|
|
133
|
+
name: e.name,
|
|
134
|
+
command: "npx",
|
|
135
|
+
args: ["-y", e.package, ...(e.args ?? [])],
|
|
136
|
+
...(e.envVars?.length ? { env: Object.fromEntries(e.envVars.map((v) => [v, `YOUR_${v}`])) } : {}),
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
/* ignore */
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
writeOhConfig({
|
|
144
|
+
provider: provider.key,
|
|
145
|
+
model: selectedModel || provider.defaultModel,
|
|
146
|
+
permissionMode: PERMISSION_MODES[permIdx].key,
|
|
147
|
+
...(apiKey ? { apiKey } : {}),
|
|
148
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
149
|
+
...(mcpServers?.length ? { mcpServers } : {}),
|
|
150
|
+
});
|
|
151
|
+
setStep("done");
|
|
152
|
+
setTimeout(() => onDone?.(), 1500);
|
|
153
|
+
}, [provider, model, availableModels, modelIdx, permIdx, apiKey, baseUrl, selectedMcp, onDone]);
|
|
95
154
|
// ── Keyboard navigation ──
|
|
96
155
|
useInput(useCallback((input, key) => {
|
|
97
156
|
if (step === "provider") {
|
|
@@ -175,68 +234,7 @@ export default function InitWizard({ onDone }) {
|
|
|
175
234
|
if (input === "n" || input === "N")
|
|
176
235
|
writeFinal();
|
|
177
236
|
}
|
|
178
|
-
},
|
|
179
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: runTest/writeFinal declared below, providerIdx intentionally omitted
|
|
180
|
-
[step, providerIdx, provider, modelIdx, availableModels, model, suggestedMcp, mcpIdx]));
|
|
181
|
-
// ── Connection test ──
|
|
182
|
-
const runTest = async (prov, key, url) => {
|
|
183
|
-
setTestStatus("testing");
|
|
184
|
-
try {
|
|
185
|
-
const { createProviderInstance } = await import("../providers/index.js");
|
|
186
|
-
const p = createProviderInstance(prov.key, {
|
|
187
|
-
name: prov.key,
|
|
188
|
-
apiKey: key || process.env[`${prov.key.toUpperCase()}_API_KEY`],
|
|
189
|
-
baseUrl: url || prov.defaultBaseUrl,
|
|
190
|
-
defaultModel: prov.defaultModel,
|
|
191
|
-
});
|
|
192
|
-
const fetched = "fetchModels" in p && typeof p.fetchModels === "function"
|
|
193
|
-
? await p.fetchModels()
|
|
194
|
-
: p.listModels();
|
|
195
|
-
const modelNames = fetched.map((m) => m.id);
|
|
196
|
-
setAvailableModels(modelNames.length > 0 ? modelNames : [prov.defaultModel]);
|
|
197
|
-
setTestStatus("ok");
|
|
198
|
-
setTimeout(() => setStep("model"), 600);
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
setTestStatus("fail");
|
|
202
|
-
setTestError(err instanceof Error ? err.message : String(err));
|
|
203
|
-
setAvailableModels([prov.defaultModel]);
|
|
204
|
-
setTimeout(() => setStep("model"), 800);
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
// ── Write final config ──
|
|
208
|
-
const writeFinal = useCallback(() => {
|
|
209
|
-
const selectedModel = availableModels.length > 0 ? (availableModels[modelIdx] ?? model) : model;
|
|
210
|
-
// Build MCP server configs from selected registry entries
|
|
211
|
-
let mcpServers;
|
|
212
|
-
if (selectedMcp.size > 0) {
|
|
213
|
-
try {
|
|
214
|
-
const { MCP_REGISTRY } = require("../mcp/registry.js");
|
|
215
|
-
mcpServers = [...selectedMcp]
|
|
216
|
-
.map((name) => MCP_REGISTRY.find((e) => e.name === name))
|
|
217
|
-
.filter(Boolean)
|
|
218
|
-
.map((e) => ({
|
|
219
|
-
name: e.name,
|
|
220
|
-
command: "npx",
|
|
221
|
-
args: ["-y", e.package, ...(e.args ?? [])],
|
|
222
|
-
...(e.envVars?.length ? { env: Object.fromEntries(e.envVars.map((v) => [v, `YOUR_${v}`])) } : {}),
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
/* ignore */
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
writeOhConfig({
|
|
230
|
-
provider: provider.key,
|
|
231
|
-
model: selectedModel || provider.defaultModel,
|
|
232
|
-
permissionMode: PERMISSION_MODES[permIdx].key,
|
|
233
|
-
...(apiKey ? { apiKey } : {}),
|
|
234
|
-
...(baseUrl ? { baseUrl } : {}),
|
|
235
|
-
...(mcpServers?.length ? { mcpServers } : {}),
|
|
236
|
-
});
|
|
237
|
-
setStep("done");
|
|
238
|
-
setTimeout(() => onDone?.(), 1500);
|
|
239
|
-
}, [provider, model, availableModels, modelIdx, permIdx, apiKey, baseUrl, selectedMcp, onDone]);
|
|
237
|
+
}, [step, provider, modelIdx, availableModels, model, suggestedMcp, mcpIdx, runTest, writeFinal]));
|
|
240
238
|
// ── Render ──
|
|
241
239
|
if (showSetup) {
|
|
242
240
|
return (_jsx(CybergotchiSetup, { onComplete: () => {
|
package/dist/harness/hooks.js
CHANGED
|
@@ -117,13 +117,16 @@ async function runHttpHook(url, event, ctx, timeoutMs = 10_000) {
|
|
|
117
117
|
return false;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Run a prompt hook. Uses LLM to make a yes/no decision.
|
|
122
|
+
*
|
|
123
|
+
* Currently a stub — prompt hooks always allow because the hook system
|
|
124
|
+
* runs outside the query loop and has no access to a Provider instance.
|
|
125
|
+
* Full implementation requires passing a Provider via HookContext so the
|
|
126
|
+
* hook can call provider.complete() with the prompt text.
|
|
127
|
+
*/
|
|
121
128
|
async function runPromptHook(_promptText, _ctx) {
|
|
122
|
-
|
|
123
|
-
// This is a lightweight check; full LLM call would need provider injection
|
|
124
|
-
// For now, prompt hooks evaluate the prompt text as a simple template
|
|
125
|
-
// TODO: inject provider for full LLM-based prompt hooks
|
|
126
|
-
return true; // Default allow if no LLM available
|
|
129
|
+
return true;
|
|
127
130
|
}
|
|
128
131
|
// ── Hook Execution ──
|
|
129
132
|
/** Execute a single hook definition. Returns true if allowed. */
|
|
@@ -87,15 +87,18 @@ export class AnthropicProvider {
|
|
|
87
87
|
// Prompt caching: send system prompt as content blocks with cache_control.
|
|
88
88
|
// Anthropic caches matching prefixes — 90% cost reduction on repeat turns.
|
|
89
89
|
const systemBlocks = [{ type: "text", text: systemPrompt, cache_control: { type: "ephemeral" } }];
|
|
90
|
+
// Scale max_tokens and thinking budget based on model
|
|
91
|
+
const isOpus = m.includes("opus");
|
|
92
|
+
const maxTokens = isOpus ? 16384 : 8192;
|
|
93
|
+
const thinkingBudget = isOpus ? 32000 : 10000;
|
|
90
94
|
const body = {
|
|
91
95
|
model: m,
|
|
92
|
-
max_tokens:
|
|
96
|
+
max_tokens: maxTokens,
|
|
93
97
|
system: systemBlocks,
|
|
94
98
|
messages: this.convertMessages(messages),
|
|
95
99
|
stream: true,
|
|
100
|
+
thinking: { type: "enabled", budget_tokens: thinkingBudget },
|
|
96
101
|
};
|
|
97
|
-
// Enable extended thinking for Claude models
|
|
98
|
-
body.thinking = { type: "enabled", budget_tokens: 10000 };
|
|
99
102
|
const anthropicTools = this.convertTools(tools);
|
|
100
103
|
if (anthropicTools) {
|
|
101
104
|
// Mark last tool definition as cacheable (cache covers all tools before it)
|
|
@@ -131,7 +134,6 @@ export class AnthropicProvider {
|
|
|
131
134
|
let currentToolId = "";
|
|
132
135
|
let currentToolName = "";
|
|
133
136
|
let currentToolArgs = "";
|
|
134
|
-
let _inThinkingBlock = false;
|
|
135
137
|
while (true) {
|
|
136
138
|
const { done, value } = await reader.read();
|
|
137
139
|
if (done)
|
|
@@ -169,9 +171,7 @@ export class AnthropicProvider {
|
|
|
169
171
|
callId: block.id,
|
|
170
172
|
};
|
|
171
173
|
}
|
|
172
|
-
|
|
173
|
-
_inThinkingBlock = true;
|
|
174
|
-
}
|
|
174
|
+
// thinking blocks are handled via thinking_delta in content_block_delta
|
|
175
175
|
break;
|
|
176
176
|
}
|
|
177
177
|
case "content_block_delta": {
|
|
@@ -188,7 +188,6 @@ export class AnthropicProvider {
|
|
|
188
188
|
break;
|
|
189
189
|
}
|
|
190
190
|
case "content_block_stop": {
|
|
191
|
-
_inThinkingBlock = false;
|
|
192
191
|
if (currentToolId) {
|
|
193
192
|
let parsedArgs = {};
|
|
194
193
|
if (currentToolArgs) {
|
package/dist/providers/openai.js
CHANGED
|
@@ -75,8 +75,9 @@ export class OpenAIProvider {
|
|
|
75
75
|
if (tools?.length)
|
|
76
76
|
body.tools = tools;
|
|
77
77
|
// Enable reasoning for o-series models
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
const isReasoning = m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4");
|
|
79
|
+
if (isReasoning) {
|
|
80
|
+
body.reasoning_effort = m.includes("mini") ? "medium" : "high";
|
|
80
81
|
}
|
|
81
82
|
let res;
|
|
82
83
|
try {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout section renderers — individual UI widgets rasterized into a CellGrid.
|
|
3
|
+
* Each function takes (state, grid, row, limit, ...options) and returns next row.
|
|
4
|
+
*/
|
|
5
|
+
import type { CellGrid, Style } from "./cells.js";
|
|
6
|
+
import type { LayoutState } from "./layout.js";
|
|
7
|
+
export declare const S_TEXT: Style;
|
|
8
|
+
export declare const S_DIM: Style;
|
|
9
|
+
export declare const S_BORDER: Style;
|
|
10
|
+
export declare const S_BANNER: Style;
|
|
11
|
+
export declare const S_BANNER_DIM: Style;
|
|
12
|
+
export declare let S_USER: Style;
|
|
13
|
+
export declare let S_ASSISTANT: Style;
|
|
14
|
+
export declare let S_ERROR: Style;
|
|
15
|
+
export declare let S_YELLOW: Style;
|
|
16
|
+
export declare let S_GREEN: Style;
|
|
17
|
+
/** Reset style cache — call after theme change */
|
|
18
|
+
export declare function resetStyleCache(): void;
|
|
19
|
+
export declare function ensureStyles(): void;
|
|
20
|
+
export declare const SPINNER_CHARS: string[];
|
|
21
|
+
export declare function renderBannerSection(state: LayoutState, grid: CellGrid, r: number, limit: number, opts: {
|
|
22
|
+
compact: boolean;
|
|
23
|
+
}): number;
|
|
24
|
+
export declare function renderThinkingSection(state: LayoutState, grid: CellGrid, r: number, limit: number): number;
|
|
25
|
+
export declare function renderThinkingSummarySection(state: LayoutState, grid: CellGrid, r: number, limit: number): number;
|
|
26
|
+
export declare function renderSpinnerSection(state: LayoutState, grid: CellGrid, r: number, limit: number): number;
|
|
27
|
+
export declare function renderErrorSection(state: LayoutState, grid: CellGrid, r: number, limit: number): number;
|
|
28
|
+
export declare function renderToolCallsSection(state: LayoutState, grid: CellGrid, r: number, limit: number, opts: {
|
|
29
|
+
maxLiveLines: number;
|
|
30
|
+
showOverflow: boolean;
|
|
31
|
+
}): number;
|
|
32
|
+
export declare function renderContextWarningSection(state: LayoutState, grid: CellGrid, r: number, limit: number): number;
|
|
33
|
+
export declare function renderPermissionBoxSection(state: LayoutState, grid: CellGrid, nextRow: number, h: number, opts: {
|
|
34
|
+
boxed: boolean;
|
|
35
|
+
maxDiffHeight: number;
|
|
36
|
+
}): number;
|
|
37
|
+
export declare function renderQuestionPromptSection(state: LayoutState, grid: CellGrid, nextRow: number, h: number, opts: {
|
|
38
|
+
boxed: boolean;
|
|
39
|
+
}): {
|
|
40
|
+
nextRow: number;
|
|
41
|
+
questionInputRow: number;
|
|
42
|
+
};
|
|
43
|
+
export declare function renderStatusLineSection(state: LayoutState, grid: CellGrid, nextRow: number, limit: number): number;
|
|
44
|
+
export declare function renderAutocompleteSection(state: LayoutState, grid: CellGrid, nextRow: number, limit: number, promptWidth: number): number;
|
|
45
|
+
export declare function renderNotificationsSection(state: LayoutState, grid: CellGrid, nextRow: number, limit: number): number;
|
|
46
|
+
export declare function renderInputSection(state: LayoutState, grid: CellGrid, inputRow: number, limit: number, promptText: string, promptWidth: number): number;
|
|
47
|
+
export declare function renderCompanionSection(state: LayoutState, grid: CellGrid, anchorRow: number, limit: number, promptWidth: number): void;
|
|
48
|
+
export declare function computeCursorPosition(state: LayoutState, inputRow: number, inputStart: number, questionInputRow: number): {
|
|
49
|
+
cursorRow: number;
|
|
50
|
+
cursorCol: number;
|
|
51
|
+
};
|
|
52
|
+
export declare function getPromptText(state: LayoutState): {
|
|
53
|
+
promptText: string;
|
|
54
|
+
promptWidth: number;
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=layout-sections.d.ts.map
|