@hizliemre/horse-code 0.1.0 → 0.1.2
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 +22 -1
- package/dist/{app-SB2L34JW.js → app-LAJN3TWC.js} +9 -9
- package/dist/{chunk-F2IALVBU.js → chunk-3UYA3KUG.js} +3 -3
- package/dist/chunk-4EWK7HWQ.js +177 -0
- package/dist/{chunk-TOPZL5SU.js → chunk-DRZSUQ7Q.js} +24 -3
- package/dist/{chunk-HBSC2HT2.js → chunk-EQX7BQYN.js} +1 -1
- package/dist/{chunk-2DGO2BUB.js → chunk-K2VERI5Q.js} +87 -20
- package/dist/{chunk-JWAEW7AJ.js → chunk-KOWMHL23.js} +1 -1
- package/dist/{chunk-3XVZXTB6.js → chunk-RPVAIS3P.js} +54 -38
- package/dist/{chunk-YILDXPSI.js → chunk-VPAWRRHL.js} +18 -4
- package/dist/{chunk-7TBYMFMG.js → chunk-WYQBRCKY.js} +2 -2
- package/dist/cli.js +57 -135
- package/dist/{fix-HBBOTUWM.js → fix-CMARU6JR.js} +4 -4
- package/dist/{ongoing-OV5XROTU.js → ongoing-XP6WXNI7.js} +2 -2
- package/dist/{save-skills-OHYGVTQ4.js → save-skills-NSLBU33X.js} +1 -1
- package/dist/{triage-2J3T5PVQ.js → triage-ES5OHZOS.js} +3 -3
- package/dist/{verify-WQ3GHION.js → verify-3R7DGUSI.js} +7 -7
- package/package.json +3 -2
- package/skills/README.md +123 -0
- package/skills/brainstorming/SKILL.md +159 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +55 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/test-driven-development/SKILL.md +371 -0
- package/skills/ui-ux-pro-max/LICENSE.txt +21 -0
- package/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/skills/writing-plans/SKILL.md +174 -0
- package/dist/chunk-DKVIN43T.js +0 -54
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Implementation tasks run in parallel, each in its own worktree, and escalate thr
|
|
|
33
33
|
|
|
34
34
|
Three constraints drove most of the design:
|
|
35
35
|
|
|
36
|
-
**One session, one worktree.** A run never writes to the checkout you are working in. Every file an agent writes is committed as a `wip(…)` checkpoint, so a bad change is recoverable and the review always sees the whole diff — an unstaged file is a hole in the evidence.
|
|
36
|
+
**One session, one worktree.** A run never writes to the checkout you are working in — enforced, not intended: `merge`, `commit`, `reset`, `checkout` and the rest are refused outright at the project root, so a finished run hands you a branch and the command to bring it in, and the decision stays yours. Every file an agent writes is committed as a `wip(…)` checkpoint, so a bad change is recoverable and the review always sees the whole diff — an unstaged file is a hole in the evidence.
|
|
37
37
|
|
|
38
38
|
**Every message an agent reads is a decision point.** A tool that answers "unknown tool: `view_file`" costs a full model turn to say nothing. So error messages name what exists, suggest the near miss, and say what to do next. Much of this repository is that: the difference between a syscall name and an answer.
|
|
39
39
|
|
|
@@ -108,6 +108,27 @@ Facts an agent learns — a command that only works from a subdirectory, a file
|
|
|
108
108
|
|
|
109
109
|
Each role is asked, at the close of its turn, whether anything cost it more than one attempt. That question is the difference between a run that learns and one that rediscovers.
|
|
110
110
|
|
|
111
|
+
## Skills
|
|
112
|
+
|
|
113
|
+
Six skills ship with the package. Each is a verbatim copy of its upstream, under `skills/`, in the same
|
|
114
|
+
format a project uses for its own — the only difference is who supplies them, and a project may replace any
|
|
115
|
+
of them by defining a skill of the same name in `.horsecode/skills/`.
|
|
116
|
+
|
|
117
|
+
| | |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `brainstorming` | intent and requirements before implementation — bound to the brainstormer |
|
|
120
|
+
| `writing-plans` | what makes an individual task executable — bound to the task list |
|
|
121
|
+
| `test-driven-development` | inlined into every role that writes code |
|
|
122
|
+
| `frontend-design` | design direction, inlined into the design roles |
|
|
123
|
+
| `systematic-debugging` | fetched on demand, when something is stuck |
|
|
124
|
+
| `ui-ux-pro-max` | 161 palettes, 57 font pairings, 25 chart types across 10 stacks — fetched on demand |
|
|
125
|
+
|
|
126
|
+
A skill can also be **referenced** rather than copied, for the ones that are large, script-driven, or
|
|
127
|
+
maintained upstream. [`impeccable`](https://github.com/pbakaus/impeccable) ships that way: declared by
|
|
128
|
+
default, installed by `/skills update` into `~/.horsecode/skills/`, and updatable from its source. Startup
|
|
129
|
+
never waits on the network — installing is an explicit act. An explicitly stated `skillSources` list, an
|
|
130
|
+
empty one included, replaces the shipped default entirely.
|
|
131
|
+
|
|
111
132
|
## Tools an agent has
|
|
112
133
|
|
|
113
134
|
`read_file` `write_file` `edit_file` `grep` `glob` `shell` `git` (read-only) `git_write` `web_fetch` `ask_user` `remember_fact` `propose_memory` `skill` `find_tool` `find_unfinished`, plus the graph tools (`graph_overview`, `graph_find`, `graph_context`, `graph_impact`, `graph_trace`) and every tool exposed by a connected MCP server.
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
restoreTerminal,
|
|
12
12
|
runJob,
|
|
13
13
|
sttySane
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-K2VERI5Q.js";
|
|
15
|
+
import "./chunk-3UYA3KUG.js";
|
|
16
16
|
import {
|
|
17
17
|
CODE_TEAM,
|
|
18
18
|
DEFAULT_COUNCIL,
|
|
@@ -24,13 +24,13 @@ import {
|
|
|
24
24
|
describeUnfinished,
|
|
25
25
|
toSlug,
|
|
26
26
|
unfinishedSessions
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-RPVAIS3P.js";
|
|
28
28
|
import "./chunk-QF4MP6BS.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-EQX7BQYN.js";
|
|
30
30
|
import "./chunk-FGVJFMK5.js";
|
|
31
31
|
import {
|
|
32
32
|
saveRoleSkills
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-4EWK7HWQ.js";
|
|
34
34
|
import {
|
|
35
35
|
objectField,
|
|
36
36
|
patchConfig
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
memoryNote,
|
|
42
42
|
memoryState,
|
|
43
43
|
walkFiles
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-DRZSUQ7Q.js";
|
|
45
45
|
import "./chunk-2SVAHH5N.js";
|
|
46
46
|
import {
|
|
47
47
|
estimateFreezeSeconds,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
relTime,
|
|
52
52
|
stripThinking,
|
|
53
53
|
writeHeapSnapshot
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-VPAWRRHL.js";
|
|
55
55
|
import "./chunk-DTWKSZXY.js";
|
|
56
56
|
import {
|
|
57
57
|
traceRootRel
|
|
@@ -5570,7 +5570,7 @@ Question: ${question}` }
|
|
|
5570
5570
|
};
|
|
5571
5571
|
const addMcp = async (input) => {
|
|
5572
5572
|
const { parseCommand, parseConfigBlock, extractFromPage, verify } = await import("./install-O34KMWJB.js");
|
|
5573
|
-
const { saveMcpServer } = await import("./save-skills-
|
|
5573
|
+
const { saveMcpServer } = await import("./save-skills-NSLBU33X.js");
|
|
5574
5574
|
let cand = parseCommand(input) ?? parseConfigBlock(input);
|
|
5575
5575
|
if (!cand) {
|
|
5576
5576
|
const url = input.trim();
|
|
@@ -5904,7 +5904,7 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5904
5904
|
const cwd = process.cwd();
|
|
5905
5905
|
const main = await recordedMainBranch(cwd) ?? await detectMainBranch(cwd, defaultGitRunner);
|
|
5906
5906
|
if (!main) return;
|
|
5907
|
-
const { ongoingWork, chooseOngoing } = await import("./ongoing-
|
|
5907
|
+
const { ongoingWork, chooseOngoing } = await import("./ongoing-XP6WXNI7.js");
|
|
5908
5908
|
const open = await ongoingWork(defaultGitRunner, cwd, main);
|
|
5909
5909
|
if (!open.length) return;
|
|
5910
5910
|
const picked = await chooseOngoing(deps, read, open[0]?.language, open);
|
|
@@ -2,16 +2,16 @@ import {
|
|
|
2
2
|
buildSkillTool,
|
|
3
3
|
editFileTool,
|
|
4
4
|
writeFileTool
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RPVAIS3P.js";
|
|
6
6
|
import {
|
|
7
7
|
globTool,
|
|
8
8
|
grepTool,
|
|
9
9
|
readFileTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DRZSUQ7Q.js";
|
|
11
11
|
import {
|
|
12
12
|
ToolRegistry,
|
|
13
13
|
runStructuredRole
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VPAWRRHL.js";
|
|
15
15
|
|
|
16
16
|
// src/engine/language.ts
|
|
17
17
|
function respondIn(language) {
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayField,
|
|
3
|
+
objectField,
|
|
4
|
+
patchConfig
|
|
5
|
+
} from "./chunk-H2FDGPVW.js";
|
|
6
|
+
|
|
7
|
+
// src/config/config.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var UNSET_MODEL = "default";
|
|
10
|
+
var DEFAULT_CONFIG = {
|
|
11
|
+
baseUrl: "http://localhost:20128",
|
|
12
|
+
model: UNSET_MODEL,
|
|
13
|
+
// acceptEdits: auto-approve file writes/edits (the pipeline builds in an isolated worktree → reviewed as a
|
|
14
|
+
// PR), still prompt for shell/exec. Keeps the automated build flowing without an approval per file.
|
|
15
|
+
mode: "acceptEdits",
|
|
16
|
+
allowlist: [],
|
|
17
|
+
roles: {},
|
|
18
|
+
specKit: { version: "v0.13.2" },
|
|
19
|
+
mcp: {},
|
|
20
|
+
modelSources: [],
|
|
21
|
+
traceDir: "",
|
|
22
|
+
/**
|
|
23
|
+
* Shipped as a REFERENCE, not a copy, for the reasons skills/README.md gives for exactly this shape: it is
|
|
24
|
+
* 3.3 MB across 154 files, it carries its own scripts, and it is maintained upstream. Vendoring it would
|
|
25
|
+
* freeze it at the commit it was taken on and turn every upstream fix into a manual merge — and it would
|
|
26
|
+
* multiply the published package by ten for a skill most runs never open.
|
|
27
|
+
*
|
|
28
|
+
* A default source is not an install. Startup stays offline; `/skills update` is the explicit act that
|
|
29
|
+
* fetches it, and a user who removes this entry from their own config is not overruled — the two configs
|
|
30
|
+
* merge by name.
|
|
31
|
+
*/
|
|
32
|
+
skillSources: [{ name: "impeccable", repo: "pbakaus/impeccable", path: ".agents/skills/impeccable" }],
|
|
33
|
+
maxParallel: 8,
|
|
34
|
+
telemetry: true
|
|
35
|
+
};
|
|
36
|
+
var reviewerSchema = z.object({ name: z.string(), perspective: z.string(), models: z.array(z.string()) });
|
|
37
|
+
var fileSchema = z.object({
|
|
38
|
+
apiKey: z.string().optional(),
|
|
39
|
+
baseUrl: z.string().optional(),
|
|
40
|
+
model: z.string().optional(),
|
|
41
|
+
mode: z.enum(["ask", "acceptEdits", "auto"]).optional(),
|
|
42
|
+
allowlist: z.array(z.string()).optional(),
|
|
43
|
+
roles: z.record(
|
|
44
|
+
z.string(),
|
|
45
|
+
z.object({
|
|
46
|
+
models: z.array(z.string()),
|
|
47
|
+
systemPrompt: z.string().optional(),
|
|
48
|
+
skills: z.array(z.string()).optional(),
|
|
49
|
+
// Anthropic models only — see RoleConfig.effort. Unknown keys are stripped, so without this line a
|
|
50
|
+
// level written into the config would be silently discarded on the way in.
|
|
51
|
+
effort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional()
|
|
52
|
+
})
|
|
53
|
+
).optional(),
|
|
54
|
+
// The review team's finder lenses, one set per stage (any omitted set falls back to the built-in default).
|
|
55
|
+
team: z.object({
|
|
56
|
+
spec: z.array(reviewerSchema).optional(),
|
|
57
|
+
plan: z.array(reviewerSchema).optional(),
|
|
58
|
+
code: z.array(reviewerSchema).optional()
|
|
59
|
+
}).optional(),
|
|
60
|
+
council: z.object({ members: z.array(reviewerSchema) }).optional(),
|
|
61
|
+
specKit: z.object({ version: z.string() }).optional(),
|
|
62
|
+
modelSources: z.array(z.string()).optional(),
|
|
63
|
+
traceDir: z.string().optional(),
|
|
64
|
+
// where /graph trace writes; empty = .horsecode/traces
|
|
65
|
+
mainBranch: z.string().optional(),
|
|
66
|
+
// the branch a resumed session syncs from; asked once, then remembered
|
|
67
|
+
// Bounded: below 1 nothing runs; above 32 the git merge lock, not the models, becomes the limit.
|
|
68
|
+
maxParallel: z.number().int().min(1).max(32).optional(),
|
|
69
|
+
telemetry: z.boolean().optional(),
|
|
70
|
+
skillSources: z.array(z.object({
|
|
71
|
+
name: z.string(),
|
|
72
|
+
repo: z.string(),
|
|
73
|
+
path: z.string().optional(),
|
|
74
|
+
ref: z.string().optional()
|
|
75
|
+
})).optional(),
|
|
76
|
+
mcp: z.record(
|
|
77
|
+
z.string(),
|
|
78
|
+
z.union([
|
|
79
|
+
z.object({ command: z.array(z.string()).min(1), env: z.record(z.string(), z.string()).optional(), readOnly: z.boolean().optional() }),
|
|
80
|
+
z.object({ url: z.string(), headers: z.record(z.string(), z.string()).optional(), readOnly: z.boolean().optional() })
|
|
81
|
+
])
|
|
82
|
+
).optional()
|
|
83
|
+
}).partial();
|
|
84
|
+
function parseFile(raw) {
|
|
85
|
+
if (!raw) return {};
|
|
86
|
+
try {
|
|
87
|
+
const parsed = fileSchema.safeParse(JSON.parse(raw));
|
|
88
|
+
return parsed.success ? parsed.data : {};
|
|
89
|
+
} catch {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function loadConfig(opts) {
|
|
94
|
+
const global = parseFile(opts.readFile(`${opts.home}/.horsecode/config.json`));
|
|
95
|
+
const project = parseFile(opts.readFile(`${opts.cwd}/.horsecode/config.json`));
|
|
96
|
+
const { apiKey: _leak, ...projectSafe } = project;
|
|
97
|
+
const merged = {
|
|
98
|
+
...DEFAULT_CONFIG,
|
|
99
|
+
...global,
|
|
100
|
+
...projectSafe
|
|
101
|
+
};
|
|
102
|
+
merged.allowlist = projectSafe.allowlist ?? global.allowlist ?? [];
|
|
103
|
+
merged.roles = { ...global.roles ?? {}, ...projectSafe.roles ?? {} };
|
|
104
|
+
merged.mcp = { ...global.mcp ?? {}, ...projectSafe.mcp ?? {} };
|
|
105
|
+
merged.modelSources = projectSafe.modelSources ?? global.modelSources ?? [];
|
|
106
|
+
merged.maxParallel = projectSafe.maxParallel ?? global.maxParallel ?? DEFAULT_CONFIG.maxParallel;
|
|
107
|
+
merged.telemetry = projectSafe.telemetry ?? global.telemetry ?? DEFAULT_CONFIG.telemetry;
|
|
108
|
+
const spoken = global.skillSources !== void 0 || projectSafe.skillSources !== void 0;
|
|
109
|
+
const byName = new Map((spoken ? [] : DEFAULT_CONFIG.skillSources).map((s) => [s.name, s]));
|
|
110
|
+
for (const s of global.skillSources ?? []) byName.set(s.name, s);
|
|
111
|
+
for (const s of projectSafe.skillSources ?? []) byName.set(s.name, s);
|
|
112
|
+
merged.skillSources = [...byName.values()];
|
|
113
|
+
merged.specKit = projectSafe.specKit ?? global.specKit ?? DEFAULT_CONFIG.specKit;
|
|
114
|
+
const team = {
|
|
115
|
+
spec: projectSafe.team?.spec ?? global.team?.spec,
|
|
116
|
+
plan: projectSafe.team?.plan ?? global.team?.plan,
|
|
117
|
+
code: projectSafe.team?.code ?? global.team?.code
|
|
118
|
+
};
|
|
119
|
+
merged.team = team.spec || team.plan || team.code ? team : void 0;
|
|
120
|
+
const councilMembers = projectSafe.council?.members ?? global.council?.members;
|
|
121
|
+
merged.council = councilMembers ? { members: councilMembers } : void 0;
|
|
122
|
+
if (opts.env.OMNIROUTE_API_KEY) merged.apiKey = opts.env.OMNIROUTE_API_KEY;
|
|
123
|
+
if (opts.env.OMNIROUTE_BASE_URL) merged.baseUrl = opts.env.OMNIROUTE_BASE_URL;
|
|
124
|
+
return merged;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/config/save-skills.ts
|
|
128
|
+
async function saveSkillSource(home, src) {
|
|
129
|
+
return patchConfig(home, (current) => {
|
|
130
|
+
if (current["skillSources"] === void 0) current = { ...current, skillSources: [...DEFAULT_CONFIG.skillSources] };
|
|
131
|
+
const sources = arrayField(current, "skillSources").filter(
|
|
132
|
+
(s) => !(typeof s === "object" && s !== null && s.name === src.name)
|
|
133
|
+
);
|
|
134
|
+
return { ...current, skillSources: [...sources, src] };
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function removeSkillSource(home, name) {
|
|
138
|
+
let found = false;
|
|
139
|
+
const ok = await patchConfig(home, (current) => {
|
|
140
|
+
const sources = arrayField(current, "skillSources");
|
|
141
|
+
const kept = sources.filter((s) => {
|
|
142
|
+
const match = typeof s === "object" && s !== null && s.name === name;
|
|
143
|
+
if (match) found = true;
|
|
144
|
+
return !match;
|
|
145
|
+
});
|
|
146
|
+
return found ? { ...current, skillSources: kept } : void 0;
|
|
147
|
+
});
|
|
148
|
+
return ok && found;
|
|
149
|
+
}
|
|
150
|
+
async function saveRoleSkills(home, assignments) {
|
|
151
|
+
const names = Object.keys(assignments);
|
|
152
|
+
if (!names.length) return 0;
|
|
153
|
+
const ok = await patchConfig(home, (current) => {
|
|
154
|
+
const roles = objectField(current, "roles");
|
|
155
|
+
for (const role of names) {
|
|
156
|
+
const prev = typeof roles[role] === "object" && roles[role] !== null ? roles[role] : {};
|
|
157
|
+
roles[role] = { ...prev, skills: [...assignments[role]] };
|
|
158
|
+
}
|
|
159
|
+
return { ...current, roles };
|
|
160
|
+
});
|
|
161
|
+
return ok ? names.length : 0;
|
|
162
|
+
}
|
|
163
|
+
async function saveMcpServer(home, name, spec) {
|
|
164
|
+
return patchConfig(home, (current) => ({
|
|
165
|
+
...current,
|
|
166
|
+
mcp: { ...objectField(current, "mcp"), [name]: spec }
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export {
|
|
171
|
+
UNSET_MODEL,
|
|
172
|
+
loadConfig,
|
|
173
|
+
saveSkillSource,
|
|
174
|
+
removeSkillSource,
|
|
175
|
+
saveRoleSkills,
|
|
176
|
+
saveMcpServer
|
|
177
|
+
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-2SVAHH5N.js";
|
|
6
6
|
import {
|
|
7
7
|
telemetry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VPAWRRHL.js";
|
|
9
9
|
import {
|
|
10
10
|
readBriefSync
|
|
11
11
|
} from "./chunk-DTWKSZXY.js";
|
|
@@ -83,6 +83,23 @@ async function sameNameElsewhere(cwd, asked) {
|
|
|
83
83
|
if (!hits.length) return "";
|
|
84
84
|
return hits.length === 1 ? ` There is one file named \`${base}\` in this project, at \`${hits[0]}\` \u2014 read that if it is what you meant.` : ` Files named \`${base}\` in this project: ${hits.map((h) => `\`${h}\``).join(", ")}.`;
|
|
85
85
|
}
|
|
86
|
+
var MAX_SIBLINGS = 12;
|
|
87
|
+
async function whatIsInThatDirectory(cwd, asked) {
|
|
88
|
+
const slash = asked.lastIndexOf("/");
|
|
89
|
+
if (slash <= 0) return "";
|
|
90
|
+
const dir = asked.slice(0, slash);
|
|
91
|
+
try {
|
|
92
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
93
|
+
const entries = await readdir2(resolve(cwd, dir), { withFileTypes: true });
|
|
94
|
+
const names = entries.filter((e) => !e.name.startsWith(".")).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
95
|
+
if (!names.length) return ` \`${dir}/\` exists but is empty.`;
|
|
96
|
+
const shown = names.slice(0, MAX_SIBLINGS).map((n) => `\`${n}\``).join(", ");
|
|
97
|
+
const rest = names.length - Math.min(names.length, MAX_SIBLINGS);
|
|
98
|
+
return ` \`${dir}/\` does exist, and holds: ${shown}${rest > 0 ? `, and ${rest} more` : ""}.`;
|
|
99
|
+
} catch {
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
86
103
|
function numbered(lines, startLine) {
|
|
87
104
|
const width = String(startLine + lines.length - 1).length;
|
|
88
105
|
return lines.map((l, i) => `${String(startLine + i).padStart(width, " ")} ${l}`).join("\n");
|
|
@@ -132,8 +149,12 @@ var readFileTool = {
|
|
|
132
149
|
isError: true
|
|
133
150
|
};
|
|
134
151
|
}
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
let hint = "";
|
|
153
|
+
if (said.includes("ENOENT")) {
|
|
154
|
+
hint = await sameNameElsewhere(ctx.cwd, args.path);
|
|
155
|
+
if (!hint) hint = await whatIsInThatDirectory(ctx.cwd, args.path);
|
|
156
|
+
}
|
|
157
|
+
return { content: `read_file error: ${said}${hint}`, isError: true };
|
|
137
158
|
}
|
|
138
159
|
const all = raw.split("\n");
|
|
139
160
|
if (args.offset === void 0 && args.limit === void 0 && raw.length <= MAX_READ_CHARS) {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
normalizeQuestion,
|
|
5
5
|
respondIn,
|
|
6
6
|
writerRegistry
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3UYA3KUG.js";
|
|
8
8
|
import {
|
|
9
9
|
Board,
|
|
10
10
|
LONG_CALL_MS,
|
|
@@ -36,14 +36,14 @@ import {
|
|
|
36
36
|
squashTask,
|
|
37
37
|
worktreeState,
|
|
38
38
|
writeFileTool
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-RPVAIS3P.js";
|
|
40
40
|
import {
|
|
41
41
|
resolveMainBranch
|
|
42
42
|
} from "./chunk-QF4MP6BS.js";
|
|
43
43
|
import {
|
|
44
44
|
askInUserLanguage,
|
|
45
45
|
inUserLanguage
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-EQX7BQYN.js";
|
|
47
47
|
import {
|
|
48
48
|
clearCheckpoint,
|
|
49
49
|
isContinuePrompt,
|
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
relationStrength,
|
|
72
72
|
supersedes,
|
|
73
73
|
verifyAnchors
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-DRZSUQ7Q.js";
|
|
75
75
|
import {
|
|
76
76
|
ToolRegistry,
|
|
77
77
|
handedOver,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
sanitizeForJson,
|
|
81
81
|
stripThinking,
|
|
82
82
|
telemetry
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-VPAWRRHL.js";
|
|
84
84
|
import {
|
|
85
85
|
TRACE_INDEX,
|
|
86
86
|
mergeTraceIndexes,
|
|
@@ -372,7 +372,52 @@ var AnthropicDecoder = class {
|
|
|
372
372
|
}
|
|
373
373
|
};
|
|
374
374
|
|
|
375
|
+
// src/providers/transport.ts
|
|
376
|
+
var SAYS = {
|
|
377
|
+
ECONNREFUSED: (w) => `nothing is listening at ${w} \u2014 connection refused`,
|
|
378
|
+
ENOTFOUND: (w) => `${w} could not be resolved \u2014 no such host`,
|
|
379
|
+
EAI_AGAIN: (w) => `${w} could not be resolved right now \u2014 DNS is not answering`,
|
|
380
|
+
ECONNRESET: (w) => `${w} closed the connection`,
|
|
381
|
+
EPIPE: (w) => `${w} closed the connection while it was being written to`,
|
|
382
|
+
ETIMEDOUT: (w) => `${w} did not accept a connection in time`,
|
|
383
|
+
EHOSTUNREACH: (w) => `${w} is unreachable from this machine`,
|
|
384
|
+
ENETUNREACH: (w) => `${w} is unreachable \u2014 no route`,
|
|
385
|
+
CERT_HAS_EXPIRED: (w) => `${w} presented an expired TLS certificate`,
|
|
386
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: (w) => `${w} presented a self-signed TLS certificate`,
|
|
387
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: (w) => `${w} presented a certificate that could not be verified`
|
|
388
|
+
};
|
|
389
|
+
function causeCode(e) {
|
|
390
|
+
const queue = [e];
|
|
391
|
+
for (let i = 0; i < queue.length && i < 32; i++) {
|
|
392
|
+
const cur = queue[i];
|
|
393
|
+
if (typeof cur !== "object" || cur === null) continue;
|
|
394
|
+
const code = cur.code;
|
|
395
|
+
if (typeof code === "string" && code) return code;
|
|
396
|
+
const errors = cur.errors;
|
|
397
|
+
if (Array.isArray(errors)) queue.push(...errors);
|
|
398
|
+
const cause = cur.cause;
|
|
399
|
+
if (cause !== void 0) queue.push(cause);
|
|
400
|
+
}
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
function origin(url) {
|
|
404
|
+
try {
|
|
405
|
+
return new URL(url).origin;
|
|
406
|
+
} catch {
|
|
407
|
+
return url;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function transportMessage(e, url) {
|
|
411
|
+
const said = e instanceof Error ? e.message : String(e);
|
|
412
|
+
const code = causeCode(e);
|
|
413
|
+
if (code === void 0) return said;
|
|
414
|
+
const where = origin(url);
|
|
415
|
+
const say = SAYS[code];
|
|
416
|
+
return say ? `${say(where)} (${code})` : `${where} could not be reached (${code})`;
|
|
417
|
+
}
|
|
418
|
+
|
|
375
419
|
// src/providers/omniroute.ts
|
|
420
|
+
var UNPRODUCTIVE_BUDGET = 3;
|
|
376
421
|
async function* withIdleTimeout(source, idleMs, onIdle) {
|
|
377
422
|
const it = source[Symbol.asyncIterator]();
|
|
378
423
|
for (; ; ) {
|
|
@@ -457,6 +502,11 @@ var OmniRouteProvider = class {
|
|
|
457
502
|
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
458
503
|
const idleAc = new AbortController();
|
|
459
504
|
const combined = AbortSignal.any([signal, idleAc.signal]);
|
|
505
|
+
let silent = false;
|
|
506
|
+
const firstByte = setTimeout(() => {
|
|
507
|
+
silent = true;
|
|
508
|
+
idleAc.abort();
|
|
509
|
+
}, this.idleMs);
|
|
460
510
|
let res;
|
|
461
511
|
try {
|
|
462
512
|
res = await this.fetchFn(`${this.baseUrl}${native ? "/v1/messages" : "/api/v1/chat/completions"}`, {
|
|
@@ -467,7 +517,17 @@ var OmniRouteProvider = class {
|
|
|
467
517
|
body: JSON.stringify(sanitizeForJson(native ? toAnthropicBody(req) : toOpenAIBody(req))),
|
|
468
518
|
signal: combined
|
|
469
519
|
});
|
|
520
|
+
clearTimeout(firstByte);
|
|
470
521
|
} catch (e) {
|
|
522
|
+
clearTimeout(firstByte);
|
|
523
|
+
if (silent && !isCallerAbort(signal)) {
|
|
524
|
+
yield {
|
|
525
|
+
type: "error",
|
|
526
|
+
retryable: true,
|
|
527
|
+
message: `the model sent nothing at all for ${Math.round(this.idleMs / 1e3)}s \u2014 not even a response header`
|
|
528
|
+
};
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
471
531
|
if (isCallerAbort(signal)) {
|
|
472
532
|
yield { type: "error", message: "cancelled", retryable: false };
|
|
473
533
|
return;
|
|
@@ -476,7 +536,7 @@ var OmniRouteProvider = class {
|
|
|
476
536
|
yield { type: "error", message: "the model did not answer within its deadline", retryable: true };
|
|
477
537
|
return;
|
|
478
538
|
}
|
|
479
|
-
yield { type: "error", message: e
|
|
539
|
+
yield { type: "error", message: transportMessage(e, this.baseUrl), retryable: true };
|
|
480
540
|
return;
|
|
481
541
|
}
|
|
482
542
|
if (!res.ok) {
|
|
@@ -504,9 +564,17 @@ var OmniRouteProvider = class {
|
|
|
504
564
|
let sawText = false;
|
|
505
565
|
let usage;
|
|
506
566
|
const billed = {};
|
|
567
|
+
let producedAt = Date.now();
|
|
568
|
+
const budget = this.idleMs * UNPRODUCTIVE_BUDGET;
|
|
569
|
+
const noProduction = () => Date.now() - producedAt > budget;
|
|
507
570
|
try {
|
|
508
571
|
for await (const line of withIdleTimeout(parseSSE(stream), this.idleMs, () => idleAc.abort())) {
|
|
572
|
+
if (noProduction()) {
|
|
573
|
+
idleAc.abort();
|
|
574
|
+
throw new Error(`omniroute: the stream stayed open for ${Math.round(budget / 1e3)}s without the model producing anything \u2014 aborted`);
|
|
575
|
+
}
|
|
509
576
|
if (line.kind === "comment") {
|
|
577
|
+
producedAt = Date.now();
|
|
510
578
|
const m = line.value.match(/^x-omniroute-tokens-(in|out)\s*=\s*(\d+)/i);
|
|
511
579
|
if (m) billed[m[1] === "in" ? "in" : "out"] = Number(m[2]);
|
|
512
580
|
continue;
|
|
@@ -517,7 +585,13 @@ var OmniRouteProvider = class {
|
|
|
517
585
|
} catch {
|
|
518
586
|
continue;
|
|
519
587
|
}
|
|
588
|
+
const c = chunk;
|
|
589
|
+
if (c.usage !== void 0 || (c.choices ?? []).some((ch) => ch.finish_reason || Object.keys(ch.delta ?? {}).length > 0)) {
|
|
590
|
+
producedAt = Date.now();
|
|
591
|
+
}
|
|
520
592
|
if (decoder) {
|
|
593
|
+
const type = chunk.type;
|
|
594
|
+
if (type !== void 0 && type !== "ping") producedAt = Date.now();
|
|
521
595
|
for (const ev of decoder.push(chunk)) yield ev;
|
|
522
596
|
continue;
|
|
523
597
|
}
|
|
@@ -560,7 +634,7 @@ var OmniRouteProvider = class {
|
|
|
560
634
|
yield { type: "error", message: "the model did not answer within its deadline", retryable: true };
|
|
561
635
|
return;
|
|
562
636
|
}
|
|
563
|
-
yield { type: "error", message: e
|
|
637
|
+
yield { type: "error", message: transportMessage(e, this.baseUrl), retryable: true };
|
|
564
638
|
return;
|
|
565
639
|
}
|
|
566
640
|
const cut = sawText ? void 0 : [...toolCalls.values()].find((a) => !argumentsComplete(a.arguments));
|
|
@@ -3500,7 +3574,7 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
|
|
|
3500
3574
|
emitPhase("verify");
|
|
3501
3575
|
const cwd = await documentWorkdir(process.cwd(), prompt, ensureWorktree, r.title);
|
|
3502
3576
|
laneCheckpoint(cwd, "verify", resume, prompt, r);
|
|
3503
|
-
const { runVerify, describeVerify, currentBranchOf } = await import("./verify-
|
|
3577
|
+
const { runVerify, describeVerify, currentBranchOf } = await import("./verify-3R7DGUSI.js");
|
|
3504
3578
|
const branch = await currentBranchOf(cwd);
|
|
3505
3579
|
const res = await runVerify({
|
|
3506
3580
|
deps,
|
|
@@ -3543,11 +3617,11 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
|
|
|
3543
3617
|
if (!resume && !hasPreservedWork && routeIntent(r.intent) === "pipeline") {
|
|
3544
3618
|
const cwd = workingIn?.() ?? process.cwd();
|
|
3545
3619
|
emitPhase("sizing");
|
|
3546
|
-
const { sizeRequest } = await import("./triage-
|
|
3620
|
+
const { sizeRequest } = await import("./triage-ES5OHZOS.js");
|
|
3547
3621
|
const size = await sizeRequest(deps, cwd, r.refinedPrompt);
|
|
3548
3622
|
let small = size.verdict === "small";
|
|
3549
3623
|
if (size.verdict === "unsure") {
|
|
3550
|
-
const { describeSizeDoubt } = await import("./triage-
|
|
3624
|
+
const { describeSizeDoubt } = await import("./triage-ES5OHZOS.js");
|
|
3551
3625
|
const answer = await askInUserLanguage(
|
|
3552
3626
|
deps,
|
|
3553
3627
|
askUser,
|
|
@@ -3565,8 +3639,8 @@ Which is it?`,
|
|
|
3565
3639
|
if (small) {
|
|
3566
3640
|
emitPhase("small change");
|
|
3567
3641
|
emit({ kind: "note", text: `\u26A1 Small change \u2014 ${size.reason}. No branch, no spec, no plan.` });
|
|
3568
|
-
const { runSmallChange, describeSmallChange } = await import("./fix-
|
|
3569
|
-
const { currentBranchOf } = await import("./verify-
|
|
3642
|
+
const { runSmallChange, describeSmallChange } = await import("./fix-CMARU6JR.js");
|
|
3643
|
+
const { currentBranchOf } = await import("./verify-3R7DGUSI.js");
|
|
3570
3644
|
const res = await runSmallChange(deps, cwd, r.title, r.refinedPrompt, size);
|
|
3571
3645
|
return {
|
|
3572
3646
|
intent: r.intent,
|
|
@@ -4370,14 +4444,7 @@ async function runJob(deps, opts) {
|
|
|
4370
4444
|
}
|
|
4371
4445
|
}
|
|
4372
4446
|
if (!wave.pr && reviewable) {
|
|
4373
|
-
|
|
4374
|
-
if (landed.ok) {
|
|
4375
|
-
wave.delivery.mergedInto = opts.fromBranch;
|
|
4376
|
-
emit({ kind: "note", text: `\u{1F4E6} Merged into \`${opts.fromBranch}\` \u2014 the files are in your working copy.` });
|
|
4377
|
-
} else {
|
|
4378
|
-
wave.delivery.notMerged = landed.why;
|
|
4379
|
-
emit({ kind: "note", text: `\u{1F4E6} Not merged (${landed.why}) \u2014 the work is on \`${wave.delivery.branch}\`.` });
|
|
4380
|
-
}
|
|
4447
|
+
emit({ kind: "note", text: `\u{1F4E6} The work is on \`${wave.delivery.branch}\` \u2014 merge it when you are ready.` });
|
|
4381
4448
|
}
|
|
4382
4449
|
await curate(deps, up.refinedPrompt ?? opts.prompt, board.list(), deferredAll, session.baseWorktree);
|
|
4383
4450
|
emit({ kind: "phase", phase: "report" });
|