@hizliemre/horse-code 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-LAJN3TWC.js → app-5FXHE7GX.js} +354 -350
- package/dist/{chunk-2SVAHH5N.js → chunk-63E73TGI.js} +2 -4
- package/dist/{chunk-DTWKSZXY.js → chunk-6OSEQOYY.js} +2 -2
- package/dist/{chunk-SSDLHWSF.js → chunk-6W4UH2BQ.js} +33 -1
- package/dist/{chunk-VPAWRRHL.js → chunk-AE36LLL2.js} +31 -21
- package/dist/{chunk-4EWK7HWQ.js → chunk-EAF22QIG.js} +32 -0
- package/dist/chunk-G45RWL7S.js +289 -0
- package/dist/{chunk-FFYBY2NA.js → chunk-KAGKX2YT.js} +2 -4
- package/dist/{chunk-DRZSUQ7Q.js → chunk-LLL7QWXB.js} +18 -7
- package/dist/{chunk-WYQBRCKY.js → chunk-LNW557IO.js} +2 -2
- package/dist/{chunk-IW2KBAVZ.js → chunk-LPQU436C.js} +12 -1
- package/dist/chunk-M2RKCIGV.js +11 -0
- package/dist/{chunk-NNTIACT4.js → chunk-MRZVA5JB.js} +4 -4
- package/dist/{chunk-RPVAIS3P.js → chunk-UEWVVN5L.js} +1511 -305
- package/dist/{chunk-EQX7BQYN.js → chunk-UGESK765.js} +1 -1
- package/dist/{chunk-PGOYDOI4.js → chunk-XEGQT5EN.js} +3 -5
- package/dist/{chunk-K2VERI5Q.js → chunk-XYZVZPAY.js} +365 -666
- package/dist/{chunk-KOWMHL23.js → chunk-YPZP7LYL.js} +2 -2
- package/dist/{chunk-FGVJFMK5.js → chunk-ZSQ24YDJ.js} +1 -1
- package/dist/cli.js +675 -95
- package/dist/{discover-5URG7C4J.js → discover-G2Z6XC3O.js} +9 -3
- package/dist/fix-ONLA45HD.js +34 -0
- package/dist/git-QZTDZSJY.js +8 -0
- package/dist/{ongoing-XP6WXNI7.js → ongoing-WHYXPW24.js} +4 -4
- package/dist/{project-graph-IOPCSZUA.js → project-graph-5HNPRFQG.js} +2 -3
- package/dist/{run-LQOZ5I7Z.js → run-P6ZYL5JL.js} +2 -3
- package/dist/{save-skills-NSLBU33X.js → save-skills-ZW5GY6KV.js} +2 -1
- package/dist/{trace-ZMB7LT7W.js → trace-X6TU3AG6.js} +2 -3
- package/dist/{trace-adopt-C6TUWFJL.js → trace-adopt-URECQWJV.js} +2 -3
- package/dist/{trace-run-F23MFTY4.js → trace-run-7U4WJZ3V.js} +6 -7
- package/dist/{triage-ES5OHZOS.js → triage-FCYHD2AQ.js} +8 -9
- package/dist/{verify-3R7DGUSI.js → verify-LC57A6H2.js} +19 -19
- package/package.json +1 -1
- package/dist/chunk-3UYA3KUG.js +0 -212
- package/dist/chunk-B67BK5GQ.js +0 -34
- package/dist/chunk-O74BDQKS.js +0 -28
- package/dist/fix-CMARU6JR.js +0 -34
- package/dist/git-VTSZALSR.js +0 -6
|
@@ -4,45 +4,54 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
MAX_PARALLEL_TASKS,
|
|
6
6
|
MemoryStore,
|
|
7
|
-
isAnthropicModel,
|
|
8
7
|
isUnknownModelError,
|
|
9
8
|
makeAskUser,
|
|
10
9
|
restoreOnExit,
|
|
11
10
|
restoreTerminal,
|
|
12
11
|
runJob,
|
|
13
12
|
sttySane
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-XYZVZPAY.js";
|
|
14
|
+
import "./chunk-M2RKCIGV.js";
|
|
16
15
|
import {
|
|
17
|
-
|
|
18
|
-
DEFAULT_COUNCIL,
|
|
19
|
-
PLAN_TEAM,
|
|
16
|
+
DURABLE_ROLES,
|
|
20
17
|
REQUIRED_ROLES,
|
|
18
|
+
ROLE_PROFILES,
|
|
21
19
|
RoleFitness,
|
|
22
|
-
|
|
20
|
+
adjustRoleModels,
|
|
23
21
|
asChoice,
|
|
22
|
+
capabilityScore,
|
|
23
|
+
cliFor,
|
|
24
24
|
describeUnfinished,
|
|
25
|
+
effortFor,
|
|
26
|
+
filterModelsForRole,
|
|
27
|
+
isKnownModel,
|
|
28
|
+
modelBand,
|
|
29
|
+
mostCapable,
|
|
30
|
+
newestPrimary,
|
|
31
|
+
sourceOf,
|
|
32
|
+
strongestPrimary,
|
|
25
33
|
toSlug,
|
|
26
34
|
unfinishedSessions
|
|
27
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-UEWVVN5L.js";
|
|
28
36
|
import "./chunk-QF4MP6BS.js";
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
37
|
+
import "./chunk-UGESK765.js";
|
|
38
|
+
import "./chunk-ZSQ24YDJ.js";
|
|
31
39
|
import {
|
|
32
40
|
saveRoleSkills
|
|
33
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-EAF22QIG.js";
|
|
42
|
+
import "./chunk-G45RWL7S.js";
|
|
34
43
|
import {
|
|
35
44
|
objectField,
|
|
36
45
|
patchConfig
|
|
37
46
|
} from "./chunk-H2FDGPVW.js";
|
|
38
|
-
import "./chunk-
|
|
39
|
-
import "./chunk-
|
|
47
|
+
import "./chunk-MRZVA5JB.js";
|
|
48
|
+
import "./chunk-LPQU436C.js";
|
|
40
49
|
import {
|
|
41
50
|
memoryNote,
|
|
42
51
|
memoryState,
|
|
43
52
|
walkFiles
|
|
44
|
-
} from "./chunk-
|
|
45
|
-
import "./chunk-
|
|
53
|
+
} from "./chunk-LLL7QWXB.js";
|
|
54
|
+
import "./chunk-63E73TGI.js";
|
|
46
55
|
import {
|
|
47
56
|
estimateFreezeSeconds,
|
|
48
57
|
fmtDuration,
|
|
@@ -50,319 +59,24 @@ import {
|
|
|
50
59
|
redactSecrets,
|
|
51
60
|
relTime,
|
|
52
61
|
stripThinking,
|
|
62
|
+
telemetry,
|
|
53
63
|
writeHeapSnapshot
|
|
54
|
-
} from "./chunk-
|
|
55
|
-
import "./chunk-
|
|
64
|
+
} from "./chunk-AE36LLL2.js";
|
|
65
|
+
import "./chunk-6OSEQOYY.js";
|
|
56
66
|
import {
|
|
57
67
|
traceRootRel
|
|
58
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-KAGKX2YT.js";
|
|
59
69
|
import {
|
|
60
70
|
buildProjectGraph,
|
|
61
71
|
graphStatus
|
|
62
|
-
} from "./chunk-
|
|
63
|
-
import "./chunk-SSDLHWSF.js";
|
|
72
|
+
} from "./chunk-XEGQT5EN.js";
|
|
64
73
|
import {
|
|
65
74
|
writeAtomic
|
|
66
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-6W4UH2BQ.js";
|
|
67
76
|
|
|
68
77
|
// src/tui/app.tsx
|
|
69
78
|
import { render } from "ink";
|
|
70
79
|
|
|
71
|
-
// src/tui/role-models.ts
|
|
72
|
-
var WEAK_RE = /\b(flash|mini|nano|haiku|lite|small|turbo|fast|\d{1,2}b)\b/i;
|
|
73
|
-
var FLAGSHIP_ROLES = ["judge", "principal-coder"];
|
|
74
|
-
var COUNCIL_ROLES = DEFAULT_COUNCIL.map((c) => c.name);
|
|
75
|
-
var SPEC_LENS_ROLES = SPEC_TEAM.map((c) => c.name);
|
|
76
|
-
var PLAN_LENS_ROLES = PLAN_TEAM.map((c) => c.name);
|
|
77
|
-
var CODE_LENS_ROLES = CODE_TEAM.map((c) => c.name);
|
|
78
|
-
var STRONG_ROLES = [
|
|
79
|
-
"brainstormer",
|
|
80
|
-
"analyst",
|
|
81
|
-
"planner",
|
|
82
|
-
"architect",
|
|
83
|
-
"senior-coder",
|
|
84
|
-
"senior-designer",
|
|
85
|
-
...COUNCIL_ROLES,
|
|
86
|
-
...PLAN_LENS_ROLES,
|
|
87
|
-
...CODE_LENS_ROLES
|
|
88
|
-
];
|
|
89
|
-
var MID_ROLES = ["coach", "coder", "designer", "code-reviewer", "operational", "memory-keeper", "task-auditor", ...SPEC_LENS_ROLES];
|
|
90
|
-
var FAST_ROLES = ["refiner", "router", "project-manager", "team-lead"];
|
|
91
|
-
var CAPABLE_ROLES = /* @__PURE__ */ new Set([...FLAGSHIP_ROLES, ...STRONG_ROLES, ...MID_ROLES]);
|
|
92
|
-
var ROLE_PROFILES = {
|
|
93
|
-
tracer: "Writes the per-file reference note every other agent reads before changing unfamiliar code \u2014 high volume, but its output is a COMMITTED FILE, not a turn in a conversation: a shallow note is believed by every agent that opens that file, forever, and nothing later corrects it. Give it the MOST capable non-[flagship] model in the catalogue, not merely one that qualifies as [strong]. Volume is not a reason to go cheaper here.",
|
|
94
|
-
refiner: "Classifies intent and rewrites the prompt every turn \u2014 highest call volume, trivial task \u2192 a fast, cheap model.",
|
|
95
|
-
router: "Picks coder-vs-designer for a task \u2014 tiny and frequent \u2192 fast, cheap.",
|
|
96
|
-
"project-manager": "Turns a task list into board items \u2014 light and structured \u2192 fast, cheap.",
|
|
97
|
-
"task-auditor": "The only check on the task breakdown before hours of implementation are spent executing it \u2014 reads the plan against the task list and finds what was dropped. Low volume, and everything downstream depends on it \u2192 a capable model, never the cheapest.",
|
|
98
|
-
"team-lead": "Coordinates implementation waves \u2014 light orchestration \u2192 fast, cheap.",
|
|
99
|
-
coach: "Your main interactive assistant, used constantly all session (highest interaction volume) \u2192 a capable but EFFICIENT model, never the costly flagship.",
|
|
100
|
-
brainstormer: "Turns a raw request into a decided design before the spec: explores the repo, weighs 2-3 approaches, gets the user to choose. Low volume, sets the direction for everything downstream \u2192 a strong reasoning model.",
|
|
101
|
-
analyst: "Authors the spec and constitution \u2192 a strong reasoning model (Opus-tier).",
|
|
102
|
-
planner: "Designs the implementation plan \u2192 a strong reasoning model (Opus-tier).",
|
|
103
|
-
architect: "Diagnoses stuck tasks and produces recovery plans \u2014 serious design work \u2192 a strong model.",
|
|
104
|
-
judge: "Critiques specs/plans and makes the final review call \u2014 low volume, high stakes \u2192 the most capable flagship model.",
|
|
105
|
-
coder: "Writes the bulk of the implementation \u2014 very high work volume \u2192 a good high-throughput coding model (Sonnet-tier), NOT the flagship (wasteful at this volume).",
|
|
106
|
-
"senior-coder": "Reviews and revises above the coder \u2014 must be MORE capable than the coder (Opus-tier).",
|
|
107
|
-
"principal-coder": "Final code decision-maker \u2014 low volume, high stakes \u2192 the flagship is appropriate.",
|
|
108
|
-
designer: "Builds UI \u2014 high volume \u2192 a capable coding/design model, not the flagship.",
|
|
109
|
-
"senior-designer": "Senior UI reviewer \u2014 more capable than the designer.",
|
|
110
|
-
"code-reviewer": "Reviews diffs \u2014 moderate volume \u2192 a solid capable model.",
|
|
111
|
-
"memory-keeper": "Decides what a finished job taught the project and writes it to durable memory \u2014 low volume, but a bad memory poisons every later run \u2192 a capable, efficient model, never the cheapest.",
|
|
112
|
-
operational: "Handles version control: writes conventional commit messages and (later) drives merges/conflicts \u2014 high volume \u2192 a capable, efficient model."
|
|
113
|
-
};
|
|
114
|
-
for (const [stage, lenses, heft] of [
|
|
115
|
-
["spec", SPEC_TEAM, "a capable, efficient model (a spec is a short business-level doc)"],
|
|
116
|
-
["plan", PLAN_TEAM, "a strong model (technical design judgment)"],
|
|
117
|
-
["code", CODE_TEAM, "a strong model (reads real implementations)"]
|
|
118
|
-
]) {
|
|
119
|
-
for (const l of lenses) ROLE_PROFILES[l.name] = `${stage.toUpperCase()}-review lens \u2014 ${l.perspective}. Low volume, quality-critical \u2192 ${heft}.`;
|
|
120
|
-
}
|
|
121
|
-
for (const c of DEFAULT_COUNCIL) {
|
|
122
|
-
ROLE_PROFILES[c.name] = `Review COUNCIL decider \u2014 ${c.perspective} Casts the binding pass/revise vote on contested work \u2192 a strong model.`;
|
|
123
|
-
}
|
|
124
|
-
var ROLE_ADVICE = ROLE_PROFILES;
|
|
125
|
-
function filterModelsForRole(role, all, exclude = []) {
|
|
126
|
-
const advice = ROLE_ADVICE[role];
|
|
127
|
-
const excluded = new Set(exclude);
|
|
128
|
-
const avail = all.filter((m) => !excluded.has(m));
|
|
129
|
-
if (CAPABLE_ROLES.has(role)) {
|
|
130
|
-
const strong = avail.filter((m) => !WEAK_RE.test(m));
|
|
131
|
-
if (strong.length === 0) return { models: avail.length ? avail : all, note: advice ? `${advice} (No strong models detected \u2014 showing all.)` : void 0 };
|
|
132
|
-
return { models: strong, note: `${advice ?? ""} Showing ${strong.length} of ${avail.length} models (fast/weak models hidden for this role).`.trim() };
|
|
133
|
-
}
|
|
134
|
-
if (FAST_ROLES.includes(role)) {
|
|
135
|
-
const fast = avail.filter((m) => WEAK_RE.test(m));
|
|
136
|
-
if (fast.length === 0) return { models: avail.length ? avail : all, note: advice };
|
|
137
|
-
return { models: fast, note: `${advice ?? ""} Showing ${fast.length} of ${avail.length} fast/cheap models.`.trim() };
|
|
138
|
-
}
|
|
139
|
-
return { models: avail.length ? avail : all };
|
|
140
|
-
}
|
|
141
|
-
function effortFor(role, model) {
|
|
142
|
-
if (!isAnthropicModel(model)) return void 0;
|
|
143
|
-
if (FLAGSHIP_ROLES.includes(role)) return "max";
|
|
144
|
-
if (STRONG_ROLES.includes(role)) return "xhigh";
|
|
145
|
-
if (FAST_ROLES.includes(role)) return "low";
|
|
146
|
-
if (MID_ROLES.includes(role)) return "high";
|
|
147
|
-
return void 0;
|
|
148
|
-
}
|
|
149
|
-
var effortBump = (s) => /-(ultra|max|xhigh)/.test(s) ? 4 : /-high/.test(s) ? 3 : /-medium/.test(s) ? 2 : /-low/.test(s) ? 1 : 0;
|
|
150
|
-
var versionBump = (s, family) => {
|
|
151
|
-
if (family) {
|
|
152
|
-
const m = s.match(new RegExp(`${family}[-_. ]?(\\d+)(?:[-.](\\d+))?`));
|
|
153
|
-
if (m) {
|
|
154
|
-
const major = Number(m[1]);
|
|
155
|
-
const minor = m[2] === void 0 ? 0 : Number(m[2]);
|
|
156
|
-
if (major < 100) return major + (minor < 10 ? minor / 10 : minor / 100);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const g = s.match(/(\d)[-.](\d)\b/);
|
|
160
|
-
return g ? Number(g[1]) + Number(g[2]) / 10 : 0;
|
|
161
|
-
};
|
|
162
|
-
var KNOWN_FAMILY_RE = /(fable|mythos|opus|sonnet|haiku|claude|codex|gpt-|\bo\d\b|gemini|deepseek|llama|qwen|kimi|glm|mistral|grok|nova|command-r|phi-\d)/i;
|
|
163
|
-
var NON_TEXT_RE = /\b(image|imagen|vision|video|veo|tts|audio|speech|voice|embed|embedding|rerank|ocr|computer-use|realtime|moderation)\b/i;
|
|
164
|
-
function isKnownModel(model) {
|
|
165
|
-
return KNOWN_FAMILY_RE.test(model) && !NON_TEXT_RE.test(model);
|
|
166
|
-
}
|
|
167
|
-
var UNRANKED_SCORE = 50;
|
|
168
|
-
function capabilityScore(model) {
|
|
169
|
-
const s = model.toLowerCase();
|
|
170
|
-
if (WEAK_RE.test(s)) return 20 + effortBump(s);
|
|
171
|
-
if (/fable|mythos/.test(s)) return 100;
|
|
172
|
-
if (/opus/.test(s)) return 88 + versionBump(s, "opus");
|
|
173
|
-
if (/codex|gpt-5|\bo3\b/.test(s)) return 82 + effortBump(s) + versionBump(s, "gpt") / 100;
|
|
174
|
-
if (/sonnet/.test(s)) return 78 + versionBump(s, "sonnet");
|
|
175
|
-
if (/gemini/.test(s) && /pro/.test(s)) return 76 + versionBump(s, "gemini") + effortBump(s);
|
|
176
|
-
if (/gpt-4/.test(s)) return 65;
|
|
177
|
-
if (/deepseek/.test(s)) return 55;
|
|
178
|
-
return UNRANKED_SCORE;
|
|
179
|
-
}
|
|
180
|
-
function mostCapable(models) {
|
|
181
|
-
return [...models].sort((a, b) => capabilityScore(b) - capabilityScore(a))[0] ?? "";
|
|
182
|
-
}
|
|
183
|
-
function modelBand(model) {
|
|
184
|
-
if (WEAK_RE.test(model)) return "fast";
|
|
185
|
-
const s = capabilityScore(model);
|
|
186
|
-
if (s >= 95) return "flagship";
|
|
187
|
-
if (s >= 84) return "strong";
|
|
188
|
-
if (s <= UNRANKED_SCORE) return "fast";
|
|
189
|
-
return "mid";
|
|
190
|
-
}
|
|
191
|
-
function baseModel(model) {
|
|
192
|
-
const segs = model.toLowerCase().split("/");
|
|
193
|
-
let s = segs[segs.length - 1];
|
|
194
|
-
s = s.replace(/-(ultra|max|xhigh|high|medium|low|free|thinking|preview)\b/g, "");
|
|
195
|
-
s = s.replace(/-\d{6,8}\b/g, "");
|
|
196
|
-
return s.replace(/-+$/, "");
|
|
197
|
-
}
|
|
198
|
-
function modelFamily(model) {
|
|
199
|
-
return baseModel(model).replace(/[-.]v?\d+(?:[-.]\d+)*(?=[-.]|$)/g, "").replace(/[-.]{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
200
|
-
}
|
|
201
|
-
function latestFirst(models) {
|
|
202
|
-
const best = /* @__PURE__ */ new Map();
|
|
203
|
-
for (const m of models) {
|
|
204
|
-
const key = modelFamily(m);
|
|
205
|
-
const cur = best.get(key);
|
|
206
|
-
if (!cur || capabilityScore(m) > capabilityScore(cur)) best.set(key, m);
|
|
207
|
-
}
|
|
208
|
-
const isLatest = (m) => best.get(modelFamily(m)) === m;
|
|
209
|
-
return [...models.filter(isLatest), ...models.filter((m) => !isLatest(m))];
|
|
210
|
-
}
|
|
211
|
-
function versionlessId(model) {
|
|
212
|
-
const cut = model.lastIndexOf("/");
|
|
213
|
-
const prefix = cut >= 0 ? model.slice(0, cut + 1) : "";
|
|
214
|
-
const name = model.slice(cut + 1).toLowerCase().replace(/-\d{6,8}\b/g, "").replace(/[-.]v?\d+(?:[-.]\d+)*(?=[-.]|$)/g, "").replace(/[-.]{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
215
|
-
return prefix + name;
|
|
216
|
-
}
|
|
217
|
-
var DURABLE_ROLES = ["tracer"];
|
|
218
|
-
function strongestPrimary(chain, pool) {
|
|
219
|
-
const head = chain[0];
|
|
220
|
-
if (!head) return chain;
|
|
221
|
-
let best = head;
|
|
222
|
-
for (const m of pool) {
|
|
223
|
-
if (modelBand(m) === "flagship" || !isKnownModel(m)) continue;
|
|
224
|
-
if (capabilityScore(m) > capabilityScore(best)) best = m;
|
|
225
|
-
}
|
|
226
|
-
if (best === head) return chain;
|
|
227
|
-
const at = chain.indexOf(best);
|
|
228
|
-
if (at > 0) {
|
|
229
|
-
const next = [...chain];
|
|
230
|
-
next[at] = head;
|
|
231
|
-
next[0] = best;
|
|
232
|
-
return next;
|
|
233
|
-
}
|
|
234
|
-
return [best, ...chain.slice(1)];
|
|
235
|
-
}
|
|
236
|
-
function newestPrimary(chain, pool) {
|
|
237
|
-
const head = chain[0];
|
|
238
|
-
if (!head) return chain;
|
|
239
|
-
const key = versionlessId(head);
|
|
240
|
-
let best = head;
|
|
241
|
-
for (const m of pool) {
|
|
242
|
-
if (versionlessId(m) !== key) continue;
|
|
243
|
-
if (capabilityScore(m) > capabilityScore(best)) best = m;
|
|
244
|
-
}
|
|
245
|
-
if (best === head) return chain;
|
|
246
|
-
const at = chain.indexOf(best);
|
|
247
|
-
if (at > 0) {
|
|
248
|
-
const next = [...chain];
|
|
249
|
-
next[at] = head;
|
|
250
|
-
next[0] = best;
|
|
251
|
-
return next;
|
|
252
|
-
}
|
|
253
|
-
return [best, ...chain.slice(1)];
|
|
254
|
-
}
|
|
255
|
-
function dedupBest(models) {
|
|
256
|
-
const best = /* @__PURE__ */ new Map();
|
|
257
|
-
for (const m of models) {
|
|
258
|
-
const key = baseModel(m);
|
|
259
|
-
const cur = best.get(key);
|
|
260
|
-
if (!cur || capabilityScore(m) > capabilityScore(cur)) best.set(key, m);
|
|
261
|
-
}
|
|
262
|
-
return [...best.values()].sort((a, b) => capabilityScore(b) - capabilityScore(a));
|
|
263
|
-
}
|
|
264
|
-
function sourceOf(model) {
|
|
265
|
-
const s = model.toLowerCase().replace(/^no-think\//, "");
|
|
266
|
-
const p = s.split("/")[0];
|
|
267
|
-
return p === "cc" ? "claude" : p === "cx" ? "codex" : p;
|
|
268
|
-
}
|
|
269
|
-
function interleaveBySource(pool) {
|
|
270
|
-
const bySource = /* @__PURE__ */ new Map();
|
|
271
|
-
for (const m of pool) {
|
|
272
|
-
const s = sourceOf(m);
|
|
273
|
-
const q = bySource.get(s);
|
|
274
|
-
if (q) q.push(m);
|
|
275
|
-
else bySource.set(s, [m]);
|
|
276
|
-
}
|
|
277
|
-
const queues = [...bySource.values()];
|
|
278
|
-
const out = [];
|
|
279
|
-
for (let more = true; more; ) {
|
|
280
|
-
more = false;
|
|
281
|
-
for (const q of queues) {
|
|
282
|
-
const m = q.shift();
|
|
283
|
-
if (m !== void 0) {
|
|
284
|
-
out.push(m);
|
|
285
|
-
more = true;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return out;
|
|
290
|
-
}
|
|
291
|
-
var BAND_ORDER = { fast: 0, mid: 1, strong: 2, flagship: 3 };
|
|
292
|
-
function bandDistance(primary, candidate) {
|
|
293
|
-
const p = BAND_ORDER[modelBand(primary)];
|
|
294
|
-
const c = BAND_ORDER[modelBand(candidate)];
|
|
295
|
-
return Math.abs(c - p) * 2 + (c < p ? 1 : 0);
|
|
296
|
-
}
|
|
297
|
-
function pickFallbacks(primary, pool, n) {
|
|
298
|
-
const chosen = [];
|
|
299
|
-
const usedModels = /* @__PURE__ */ new Set([baseModel(primary)]);
|
|
300
|
-
const usedSources = /* @__PURE__ */ new Set([sourceOf(primary)]);
|
|
301
|
-
const byHeft = pool.map((m, i) => ({ m, i })).sort((a, b) => bandDistance(primary, a.m) - bandDistance(primary, b.m) || a.i - b.i).map((x) => x.m);
|
|
302
|
-
for (const m of byHeft) {
|
|
303
|
-
if (chosen.length >= n) break;
|
|
304
|
-
if (usedModels.has(baseModel(m)) || usedSources.has(sourceOf(m))) continue;
|
|
305
|
-
chosen.push(m);
|
|
306
|
-
usedModels.add(baseModel(m));
|
|
307
|
-
usedSources.add(sourceOf(m));
|
|
308
|
-
}
|
|
309
|
-
for (const m of byHeft) {
|
|
310
|
-
if (chosen.length >= n) break;
|
|
311
|
-
if (usedModels.has(baseModel(m))) continue;
|
|
312
|
-
chosen.push(m);
|
|
313
|
-
usedModels.add(baseModel(m));
|
|
314
|
-
}
|
|
315
|
-
return chosen;
|
|
316
|
-
}
|
|
317
|
-
var FALLBACK_COUNT = 2;
|
|
318
|
-
function adjustRoleModels(roles, models, unfit) {
|
|
319
|
-
if (models.length === 0) return [];
|
|
320
|
-
const recognised = models.filter(isKnownModel);
|
|
321
|
-
const pick = recognised.length ? recognised : models;
|
|
322
|
-
const capable = dedupBest(pick.filter((m) => !WEAK_RE.test(m)));
|
|
323
|
-
const fast = dedupBest(pick.filter((m) => WEAK_RE.test(m)));
|
|
324
|
-
const capablePool = capable.length ? capable : fast;
|
|
325
|
-
const fastPool = fast.length ? fast : capable;
|
|
326
|
-
const primaryPool = latestFirst(capablePool);
|
|
327
|
-
const primaryFast = latestFirst(fastPool);
|
|
328
|
-
const nonFlagship = primaryPool.filter((m) => modelBand(m) !== "flagship");
|
|
329
|
-
const strongPool = primaryPool.filter((m) => modelBand(m) === "strong");
|
|
330
|
-
const midPool = primaryPool.filter((m) => modelBand(m) === "mid");
|
|
331
|
-
const wanted = new Set(roles);
|
|
332
|
-
const forRole = (role, pool) => {
|
|
333
|
-
if (!unfit) return pool;
|
|
334
|
-
const fit = pool.filter((m) => !unfit(role, m));
|
|
335
|
-
return fit.length ? fit : pool;
|
|
336
|
-
};
|
|
337
|
-
const known = /* @__PURE__ */ new Set([...FLAGSHIP_ROLES, ...STRONG_ROLES, ...MID_ROLES, ...FAST_ROLES]);
|
|
338
|
-
const primary = /* @__PURE__ */ new Map();
|
|
339
|
-
const flagSrc = primaryPool;
|
|
340
|
-
FLAGSHIP_ROLES.filter((r) => wanted.has(r)).forEach((r, i) => {
|
|
341
|
-
const src = forRole(r, flagSrc);
|
|
342
|
-
primary.set(r, src[i % src.length]);
|
|
343
|
-
});
|
|
344
|
-
const strongSrc = interleaveBySource(strongPool.length ? strongPool : nonFlagship.length ? nonFlagship : primaryPool);
|
|
345
|
-
STRONG_ROLES.filter((r) => wanted.has(r)).concat(roles.filter((r) => !known.has(r))).forEach((r, i) => {
|
|
346
|
-
const src = forRole(r, strongSrc);
|
|
347
|
-
primary.set(r, src[i % src.length]);
|
|
348
|
-
});
|
|
349
|
-
const midSrc = interleaveBySource(midPool.length ? midPool : nonFlagship.length ? nonFlagship : primaryPool);
|
|
350
|
-
MID_ROLES.filter((r) => wanted.has(r)).forEach((r, i) => {
|
|
351
|
-
const src = forRole(r, midSrc);
|
|
352
|
-
primary.set(r, src[i % src.length]);
|
|
353
|
-
});
|
|
354
|
-
FAST_ROLES.filter((r) => wanted.has(r)).forEach((r, i) => {
|
|
355
|
-
const src = forRole(r, primaryFast);
|
|
356
|
-
primary.set(r, src[i % src.length]);
|
|
357
|
-
});
|
|
358
|
-
return roles.map((role) => {
|
|
359
|
-
const head = primary.get(role) ?? primaryPool[0];
|
|
360
|
-
const capForFb = MID_ROLES.includes(role) ? capablePool.filter((m) => modelBand(m) !== "flagship") : capablePool;
|
|
361
|
-
const pool = FAST_ROLES.includes(role) ? [...fastPool, ...capForFb] : [...capForFb, ...fastPool];
|
|
362
|
-
return { role, models: newestPrimary([head, ...pickFallbacks(head, forRole(role, pool), FALLBACK_COUNT)], models) };
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
|
|
366
80
|
// src/engine/role-tuner.ts
|
|
367
81
|
function describeCatalog(models) {
|
|
368
82
|
const bySource = /* @__PURE__ */ new Map();
|
|
@@ -644,7 +358,7 @@ async function connectAllMcp(specs) {
|
|
|
644
358
|
|
|
645
359
|
// src/tui/app.tsx
|
|
646
360
|
import { homedir as homedir2 } from "os";
|
|
647
|
-
import { join as
|
|
361
|
+
import { join as join7 } from "path";
|
|
648
362
|
import { basename } from "path";
|
|
649
363
|
|
|
650
364
|
// src/tui/labels.ts
|
|
@@ -2025,6 +1739,7 @@ var COMMANDS = [
|
|
|
2025
1739
|
{ name: "/memories", desc: "List remembered facts (/forget N to remove)" },
|
|
2026
1740
|
{ name: "/forget", desc: "Forget a remembered fact (/forget N)" },
|
|
2027
1741
|
{ name: "/mcp", desc: "Connected MCP servers (/mcp add <url|command> installs one and verifies it)" },
|
|
1742
|
+
{ name: "/models", desc: "Models each connected subscription serves, and which ones a role is running" },
|
|
2028
1743
|
{ name: "/sources", desc: "Show your connected model sources (/sources refresh re-detects)" },
|
|
2029
1744
|
{ name: "/migrate", desc: "Bring a project from Claude Code / Codex / Cursor into horse-code (rules, memory, skills)" },
|
|
2030
1745
|
{ name: "/continue-from-claude", desc: "Continue work started in a Claude Code worktree (/continue-from-claude <name>) \u2014 its branch becomes the base" },
|
|
@@ -2036,6 +1751,7 @@ var COMMANDS = [
|
|
|
2036
1751
|
{ name: "/monitor", desc: "Where the run's time is going (/monitor enable shows the panel, disable hides it, log shows the file, heap writes a snapshot)" },
|
|
2037
1752
|
{ name: "/watch", desc: "Watch any command \u2014 each line it prints becomes an event (/watch <cmd>, /watch stop N)" },
|
|
2038
1753
|
{ name: "/paste", desc: "Put the clipboard's image into the input (same as Ctrl+V, for terminals that swallow it)" },
|
|
1754
|
+
{ name: "/start-smoke-test", desc: "Walk the finished feature's manual verification one step at a time, and record what you saw into its guide" },
|
|
2039
1755
|
{ name: "/help", desc: "List the available commands" },
|
|
2040
1756
|
{ name: "/clear", desc: "Clear the conversation" },
|
|
2041
1757
|
{ name: "/exit", desc: "Quit horse-code" }
|
|
@@ -2245,7 +1961,7 @@ function rankFiles(files, query, limit = 8) {
|
|
|
2245
1961
|
}
|
|
2246
1962
|
|
|
2247
1963
|
// src/tui/components.tsx
|
|
2248
|
-
import { join as
|
|
1964
|
+
import { join as join3 } from "path";
|
|
2249
1965
|
import { homedir } from "os";
|
|
2250
1966
|
|
|
2251
1967
|
// src/tui/paste.ts
|
|
@@ -2576,7 +2292,130 @@ function shortName(command) {
|
|
|
2576
2292
|
return first.split("/").pop()?.slice(0, 24) || "watch";
|
|
2577
2293
|
}
|
|
2578
2294
|
|
|
2295
|
+
// src/engine/smoke-test.ts
|
|
2296
|
+
import { existsSync, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
2297
|
+
import { join as join2 } from "path";
|
|
2298
|
+
function splitInstructions(paragraph) {
|
|
2299
|
+
return paragraph.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter((s) => s.length > 12);
|
|
2300
|
+
}
|
|
2301
|
+
function parseGuide(path, markdown) {
|
|
2302
|
+
const lines = markdown.split("\n");
|
|
2303
|
+
const title = lines.find((l) => l.startsWith("# "))?.slice(2).trim() ?? "Manual verification";
|
|
2304
|
+
const preconditions = [];
|
|
2305
|
+
const scenarios = [];
|
|
2306
|
+
const steps = [];
|
|
2307
|
+
let section = "other";
|
|
2308
|
+
let scenario = "";
|
|
2309
|
+
let buffer = [];
|
|
2310
|
+
const flush = () => {
|
|
2311
|
+
if (!scenario) {
|
|
2312
|
+
buffer = [];
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
const text = buffer.join(" ").trim();
|
|
2316
|
+
for (const instruction of splitInstructions(text)) {
|
|
2317
|
+
steps.push({ n: steps.length + 1, scenario, scenarioIndex: Math.max(0, scenarios.length - 1), instruction });
|
|
2318
|
+
}
|
|
2319
|
+
buffer = [];
|
|
2320
|
+
};
|
|
2321
|
+
for (const line of lines) {
|
|
2322
|
+
const h2 = /^##\s+(?!#)(.*)$/.exec(line);
|
|
2323
|
+
const h3 = /^###\s+(.*)$/.exec(line);
|
|
2324
|
+
if (h2) {
|
|
2325
|
+
flush();
|
|
2326
|
+
scenario = "";
|
|
2327
|
+
section = /ön koşul|precondition/i.test(h2[1]) ? "pre" : /senaryo|scenario/i.test(h2[1]) ? "scenario" : "other";
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
if (h3) {
|
|
2331
|
+
flush();
|
|
2332
|
+
if (section === "scenario") {
|
|
2333
|
+
scenario = h3[1].trim();
|
|
2334
|
+
scenarios.push(scenario);
|
|
2335
|
+
}
|
|
2336
|
+
continue;
|
|
2337
|
+
}
|
|
2338
|
+
const text = line.trim();
|
|
2339
|
+
if (!text) {
|
|
2340
|
+
flush();
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
if (section === "pre") {
|
|
2344
|
+
const item = /^\d+\.\s+(.*)$/.exec(text)?.[1] ?? /^[-*]\s+(.*)$/.exec(text)?.[1];
|
|
2345
|
+
if (item) preconditions.push(item);
|
|
2346
|
+
continue;
|
|
2347
|
+
}
|
|
2348
|
+
if (section === "scenario" && scenario) buffer.push(text);
|
|
2349
|
+
}
|
|
2350
|
+
flush();
|
|
2351
|
+
return { path, title, preconditions, steps, scenarios };
|
|
2352
|
+
}
|
|
2353
|
+
function renderStep(step, total) {
|
|
2354
|
+
return `**Ad\u0131m ${step.n}/${total}** \u2014 _${step.scenario}_
|
|
2355
|
+
|
|
2356
|
+
${step.instruction}`;
|
|
2357
|
+
}
|
|
2358
|
+
function recordOutcome(markdown, scenarioIndex, status, evidence) {
|
|
2359
|
+
const want = scenarioIndex + 1;
|
|
2360
|
+
return markdown.split("\n").map((line) => {
|
|
2361
|
+
if (!line.startsWith("|")) return line;
|
|
2362
|
+
const cells = line.split("|");
|
|
2363
|
+
if (cells.length < 4) return line;
|
|
2364
|
+
if (Number(/^\s*(\d+)/.exec(cells[1] ?? "")?.[1]) !== want) return line;
|
|
2365
|
+
cells[2] = ` ${status} `;
|
|
2366
|
+
cells[3] = ` ${evidence.replace(/\|/g, "\\|").replace(/\s+/g, " ").trim()} `;
|
|
2367
|
+
return cells.join("|");
|
|
2368
|
+
}).join("\n");
|
|
2369
|
+
}
|
|
2370
|
+
function scenarioOutcome(results) {
|
|
2371
|
+
const status = results.some((r) => r.status === "ba\u015Far\u0131s\u0131z") ? "ba\u015Far\u0131s\u0131z" : results.every((r) => r.status === "\xE7al\u0131\u015Ft\u0131r\u0131lmad\u0131") ? "\xE7al\u0131\u015Ft\u0131r\u0131lmad\u0131" : "ge\xE7ti";
|
|
2372
|
+
const evidence = results.filter((r) => r.evidence.trim()).map((r) => `${r.step.n}. ${r.evidence.trim()}`).join(" ");
|
|
2373
|
+
return { status, evidence: evidence || "g\xF6zlem kaydedilmedi" };
|
|
2374
|
+
}
|
|
2375
|
+
function findGuide(projectRoot) {
|
|
2376
|
+
const roots = [projectRoot, ...worktreeRoots(projectRoot)];
|
|
2377
|
+
let best;
|
|
2378
|
+
for (const root of roots) {
|
|
2379
|
+
let names = [];
|
|
2380
|
+
try {
|
|
2381
|
+
names = readdirSync(join2(root, "specs"));
|
|
2382
|
+
} catch {
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
for (const slug of names) {
|
|
2386
|
+
if (!/^\d{3}-/.test(slug)) continue;
|
|
2387
|
+
const path = join2(root, "specs", slug, "quickstart.md");
|
|
2388
|
+
if (!existsSync(path)) continue;
|
|
2389
|
+
if (!best || slug > best.slug) best = { root, slug, path };
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
return best;
|
|
2393
|
+
}
|
|
2394
|
+
function worktreeRoots(projectRoot) {
|
|
2395
|
+
const dir = join2(projectRoot, ".horsecode", "worktrees");
|
|
2396
|
+
try {
|
|
2397
|
+
return readdirSync(dir).map((s) => join2(dir, s, "base")).filter((p) => existsSync(p));
|
|
2398
|
+
} catch {
|
|
2399
|
+
return [];
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
function progressLine(done, total) {
|
|
2403
|
+
const width = 24;
|
|
2404
|
+
const filled = total ? Math.round(done / total * width) : 0;
|
|
2405
|
+
return `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)} ${done}/${total}`;
|
|
2406
|
+
}
|
|
2407
|
+
function loadGuide(projectRoot) {
|
|
2408
|
+
const found = findGuide(projectRoot);
|
|
2409
|
+
if (!found) {
|
|
2410
|
+
return { error: "No `specs/NNN-\u2026/quickstart.md` in this project or any of its session worktrees \u2014 a feature run writes one when it finishes." };
|
|
2411
|
+
}
|
|
2412
|
+
const guide = parseGuide(found.path, readFileSync2(found.path, "utf8"));
|
|
2413
|
+
if (!guide.steps.length) return { error: `${found.path} has no scenarios to walk \u2014 it may not be a verification guide.` };
|
|
2414
|
+
return { ...guide, slug: found.slug };
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2579
2417
|
// src/tui/components.tsx
|
|
2418
|
+
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
2580
2419
|
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2581
2420
|
function parsePending(raw) {
|
|
2582
2421
|
const t = raw.replace(/^\s+/, "");
|
|
@@ -3330,7 +3169,7 @@ function ViewportLines({ lines, height }) {
|
|
|
3330
3169
|
lines.map((line, i) => /* @__PURE__ */ jsx4(Text4, { children: line.length === 0 ? " " : line.map((s, j) => /* @__PURE__ */ jsx4(Text4, { color: s.color, backgroundColor: s.backgroundColor, bold: s.bold, italic: s.italic, dimColor: s.dim, children: s.text }, j)) }, i))
|
|
3331
3170
|
] });
|
|
3332
3171
|
}
|
|
3333
|
-
function App({ controller, fullscreen = false, model, coachModel, refinerModel, listModels, setModel, setRoleModel, listRoles, adjustRoles, listSessions, resumeSession, listPins, addPin, removePin, listMemories, addMemory, removeMemory, listMcp, sourcesInfo, refreshSources, listSkills, updateSkills, addSkill, graphStatus: graphStatus2, buildGraph, planTraces, runTraces, cleanWorktrees, migrate, continueFromClaude, addMcp, answerByTheWay, telemetryPath, parallel, setParallel, permMode, setPermMode, cancelJob, onExit }) {
|
|
3172
|
+
function App({ controller, fullscreen = false, model, coachModel, refinerModel, listModels, setModel, setRoleModel, listRoles, adjustRoles, modelsPanel, listSessions, resumeSession, listPins, addPin, removePin, listMemories, addMemory, removeMemory, listMcp, sourcesInfo, refreshSources, listSkills, updateSkills, addSkill, graphStatus: graphStatus2, buildGraph, planTraces, runTraces, cleanWorktrees, migrate, continueFromClaude, addMcp, answerByTheWay, telemetryPath, parallel, setParallel, permMode, setPermMode, cancelJob, onExit }) {
|
|
3334
3173
|
const [state, setState] = useState3(controller.getState());
|
|
3335
3174
|
useEffect3(() => controller.subscribe(() => setState(controller.getState())), [controller]);
|
|
3336
3175
|
const { stdout } = useStdout();
|
|
@@ -3543,10 +3382,10 @@ _Type \`/resume N\` to continue one._`);
|
|
|
3543
3382
|
}
|
|
3544
3383
|
try {
|
|
3545
3384
|
const { writeFile: writeFile3, mkdir: mkdir3 } = await import("fs/promises");
|
|
3546
|
-
const dir =
|
|
3385
|
+
const dir = join3(homedir(), ".horsecode", "pastes");
|
|
3547
3386
|
await mkdir3(dir, { recursive: true });
|
|
3548
3387
|
const id = ++imageIdRef.current;
|
|
3549
|
-
const file =
|
|
3388
|
+
const file = join3(dir, `paste-${process.pid}-${id}.png`);
|
|
3550
3389
|
await writeFile3(file, Buffer.from(uri.slice(uri.indexOf(",") + 1), "base64"));
|
|
3551
3390
|
imageMapRef.current.set(id, file);
|
|
3552
3391
|
const tok = imageToken(id);
|
|
@@ -3786,6 +3625,14 @@ ${rows.join("\n")}
|
|
|
3786
3625
|
|
|
3787
3626
|
_\`/mcp add <url|command>\` to install another._`);
|
|
3788
3627
|
};
|
|
3628
|
+
const doModels = () => {
|
|
3629
|
+
if (!modelsPanel) {
|
|
3630
|
+
controller.note("Models are not available.");
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
const inUse = [...new Set((listRoles?.() ?? []).flatMap((r) => r.models))];
|
|
3634
|
+
controller.note(modelsPanel(inUse));
|
|
3635
|
+
};
|
|
3789
3636
|
const doSources = (arg) => {
|
|
3790
3637
|
const info = sourcesInfo?.();
|
|
3791
3638
|
if (arg.trim().toLowerCase() === "refresh") {
|
|
@@ -3849,6 +3696,56 @@ _\`/sources refresh\` to re-probe your connected subscriptions._`);
|
|
|
3849
3696
|
}
|
|
3850
3697
|
);
|
|
3851
3698
|
};
|
|
3699
|
+
const doSmokeTest = async () => {
|
|
3700
|
+
const guide = loadGuide(process.cwd());
|
|
3701
|
+
if ("error" in guide) {
|
|
3702
|
+
controller.note(guide.error);
|
|
3703
|
+
return;
|
|
3704
|
+
}
|
|
3705
|
+
controller.note(
|
|
3706
|
+
`\u{1F9EA} **${guide.title}**
|
|
3707
|
+
|
|
3708
|
+
\`${guide.slug}\` \xB7 ${guide.steps.length} ad\u0131m \xB7 ${guide.scenarios.length} senaryo`
|
|
3709
|
+
);
|
|
3710
|
+
if (guide.preconditions.length) {
|
|
3711
|
+
controller.note(`**Ba\u015Flamadan \xF6nce**
|
|
3712
|
+
${guide.preconditions.map((p) => `- ${p}`).join("\n")}`);
|
|
3713
|
+
}
|
|
3714
|
+
controller.note(
|
|
3715
|
+
"Her ad\u0131mda ne g\xF6rd\xFC\u011F\xFCn\xFC yap\u0131\u015Ft\u0131r \u2014 API yan\u0131t\u0131, DB sat\u0131r\u0131, log kayd\u0131. horse-code veritaban\u0131na ve Loki'ye kendisi bakmaz; kan\u0131t senin g\xF6zlemin olarak kaydedilir."
|
|
3716
|
+
);
|
|
3717
|
+
if (await controller.ask("Ortam haz\u0131r m\u0131?", { options: ["Ba\u015Fla", "\u0130ptal"] }) !== "Ba\u015Fla") {
|
|
3718
|
+
controller.note("B\u0131rak\u0131ld\u0131 \u2014 rehberde hi\xE7bir \u015Fey de\u011Fi\u015Fmedi.");
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
let markdown = readFileSync3(guide.path, "utf8");
|
|
3722
|
+
let batch = [];
|
|
3723
|
+
const flush = (index) => {
|
|
3724
|
+
if (!batch.length) return;
|
|
3725
|
+
const { status, evidence } = scenarioOutcome(batch);
|
|
3726
|
+
markdown = recordOutcome(markdown, index, status, evidence);
|
|
3727
|
+
writeFileSync(guide.path, markdown);
|
|
3728
|
+
controller.note(`\u{1F4DD} _${guide.scenarios[index]}_ \u2192 **${status}**`);
|
|
3729
|
+
batch = [];
|
|
3730
|
+
};
|
|
3731
|
+
for (const [i, step] of guide.steps.entries()) {
|
|
3732
|
+
const previous = guide.steps[i - 1];
|
|
3733
|
+
if (previous && previous.scenarioIndex !== step.scenarioIndex) flush(previous.scenarioIndex);
|
|
3734
|
+
controller.note(renderStep(step, guide.steps.length));
|
|
3735
|
+
const answer = await controller.ask("Sonu\xE7?", { options: ["ge\xE7ti", "ba\u015Far\u0131s\u0131z", "atla", "durdur"] });
|
|
3736
|
+
if (answer === "durdur") {
|
|
3737
|
+
flush(step.scenarioIndex);
|
|
3738
|
+
controller.note(`Durduruldu \u2014 ${step.n - 1}/${guide.steps.length} ad\u0131m kaydedildi.`);
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
const status = answer === "ge\xE7ti" ? "ge\xE7ti" : answer === "ba\u015Far\u0131s\u0131z" ? "ba\u015Far\u0131s\u0131z" : "\xE7al\u0131\u015Ft\u0131r\u0131lmad\u0131";
|
|
3742
|
+
const evidence = status === "\xE7al\u0131\u015Ft\u0131r\u0131lmad\u0131" ? "" : await controller.ask("Ne g\xF6zlemledin? (API yan\u0131t\u0131 / DB sat\u0131r\u0131 / log)");
|
|
3743
|
+
batch.push({ step, status, evidence });
|
|
3744
|
+
controller.note(progressLine(step.n, guide.steps.length));
|
|
3745
|
+
}
|
|
3746
|
+
flush(guide.steps[guide.steps.length - 1].scenarioIndex);
|
|
3747
|
+
controller.note(`\u2705 Bitti \u2014 ${guide.path} g\xFCncellendi.`);
|
|
3748
|
+
};
|
|
3852
3749
|
const doGraph = (arg) => {
|
|
3853
3750
|
if (!graphStatus2 || !buildGraph) {
|
|
3854
3751
|
controller.note("The project graph is not available.");
|
|
@@ -4012,8 +3909,10 @@ _\`/skills add <github-url>\` to install one \xB7 \`/skills update\` re-installs
|
|
|
4012
3909
|
else if (c.name === "/remember") doRemember("");
|
|
4013
3910
|
else if (c.name === "/forget") doForget("");
|
|
4014
3911
|
else if (c.name === "/mcp") doMcp("");
|
|
3912
|
+
else if (c.name === "/models") doModels();
|
|
4015
3913
|
else if (c.name === "/sources") doSources("");
|
|
4016
3914
|
else if (c.name === "/skills") doSkills("");
|
|
3915
|
+
else if (c.name === "/start-smoke-test") void doSmokeTest();
|
|
4017
3916
|
else if (c.name === "/graph") doGraph("");
|
|
4018
3917
|
else if (c.name === "/clean-worktrees") doCleanWorktrees("");
|
|
4019
3918
|
else if (c.name === "/paste") pasteImage();
|
|
@@ -4303,9 +4202,9 @@ _\`/skills add <github-url>\` to install one \xB7 \`/skills update\` re-installs
|
|
|
4303
4202
|
const cw = Math.max(1, size.cols - 4);
|
|
4304
4203
|
const inputH = draft.split("\n").reduce((n, l) => n + Math.max(1, Math.ceil((l.length + 3) / cw)), 0);
|
|
4305
4204
|
const running = mode === "running";
|
|
4306
|
-
const
|
|
4205
|
+
const progressLine2 = running && !state.pending;
|
|
4307
4206
|
const doneLine = !!state.meta && !state.meta.running && !state.pending;
|
|
4308
|
-
const showStatus =
|
|
4207
|
+
const showStatus = progressLine2 || !!state.pending || doneLine;
|
|
4309
4208
|
const choiceOptionsForFit = state.pending?.options ?? [];
|
|
4310
4209
|
const pendingBodyMax = state.pending ? Math.max(
|
|
4311
4210
|
1,
|
|
@@ -4316,7 +4215,7 @@ _\`/skills add <github-url>\` to install one \xB7 \`/skills update\` re-installs
|
|
|
4316
4215
|
flattenMarkdown(parsePending(state.pending.question).body, pendingBodyWidth(size.cols)).length,
|
|
4317
4216
|
pendingBodyMax
|
|
4318
4217
|
) : 0;
|
|
4319
|
-
const statusH = (
|
|
4218
|
+
const statusH = (progressLine2 || doneLine ? 1 : 0) + pendingLines;
|
|
4320
4219
|
const inputMarginTop = showStatus ? 0 : 1;
|
|
4321
4220
|
const choiceOptions = state.pending?.options ?? [];
|
|
4322
4221
|
const choiceActive = choiceOptions.length > 0 && !choiceDismissed;
|
|
@@ -4363,7 +4262,7 @@ _\`/skills add <github-url>\` to install one \xB7 \`/skills update\` re-installs
|
|
|
4363
4262
|
/* @__PURE__ */ jsx4(Box4, { paddingLeft: CHAT_INDENT, children: /* @__PURE__ */ jsx4(ViewportLines, { lines: windowed, height: viewportH }) }),
|
|
4364
4263
|
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: clamped > 0 ? ` \u2193 ${clamped} more \xB7 \u2193/PgDn to jump to bottom` : " " }),
|
|
4365
4264
|
showStatus ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
4366
|
-
|
|
4265
|
+
progressLine2 ? /* @__PURE__ */ jsx4(Box4, { paddingLeft: 2, children: /* @__PURE__ */ jsx4(ProgressView, { phase: state.phase, detail: state.detail, refinerModel: refinerModel?.(), meta: state.meta, cols: size.cols, live: state.liveActivity }) }) : null,
|
|
4367
4266
|
doneLine ? /* @__PURE__ */ jsx4(Box4, { paddingLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: `${donePhrase(state.phase)} for ${fmtDuration(state.meta?.durationMs ?? 0)}${state.meta && state.meta.calls > 0 ? ` \xB7 \u2191${fmtTokens(state.meta.promptTokens)} \u2193${fmtTokens(state.meta.completionTokens)} \xB7 ${state.meta.calls} call${state.meta.calls === 1 ? "" : "s"}` : ""}` }) }) : null,
|
|
4368
4267
|
state.pending ? /* @__PURE__ */ jsx4(PendingQuestion, { text: state.pending.question, cols: size.cols, maxLines: pendingBodyMax }) : null
|
|
4369
4268
|
] }) : null,
|
|
@@ -4556,6 +4455,15 @@ _\`/skills add <github-url>\` to install one \xB7 \`/skills update\` re-installs
|
|
|
4556
4455
|
doMcp(trimmed.slice("/mcp".length).trim());
|
|
4557
4456
|
return;
|
|
4558
4457
|
}
|
|
4458
|
+
if (cmd === "/start-smoke-test") {
|
|
4459
|
+
setScroll(0);
|
|
4460
|
+
setDraft("");
|
|
4461
|
+
setDraftCursor(0);
|
|
4462
|
+
usedPastes();
|
|
4463
|
+
controller.echoCommand(trimmed);
|
|
4464
|
+
void doSmokeTest();
|
|
4465
|
+
return;
|
|
4466
|
+
}
|
|
4559
4467
|
if (cmd.startsWith("/graph ")) {
|
|
4560
4468
|
setScroll(0);
|
|
4561
4469
|
setDraft("");
|
|
@@ -4667,7 +4575,7 @@ async function saveMode(home, mode) {
|
|
|
4667
4575
|
// src/session/store.ts
|
|
4668
4576
|
import { mkdir, readFile as readFile2, readdir } from "fs/promises";
|
|
4669
4577
|
import { createHash } from "crypto";
|
|
4670
|
-
import { join as
|
|
4578
|
+
import { join as join4 } from "path";
|
|
4671
4579
|
var SessionStore = class {
|
|
4672
4580
|
activeId;
|
|
4673
4581
|
dir;
|
|
@@ -4675,7 +4583,7 @@ var SessionStore = class {
|
|
|
4675
4583
|
constructor(opts) {
|
|
4676
4584
|
this.now = opts.now ?? (() => Date.now());
|
|
4677
4585
|
const hash = createHash("sha256").update(opts.cwd).digest("hex").slice(0, 16);
|
|
4678
|
-
this.dir =
|
|
4586
|
+
this.dir = join4(opts.home, ".horsecode", "projects", hash, "sessions");
|
|
4679
4587
|
this.activeId = `s${this.now()}`;
|
|
4680
4588
|
}
|
|
4681
4589
|
/** The id this store currently writes to (a fresh one, or a resumed session's id after setActive). */
|
|
@@ -4687,7 +4595,7 @@ var SessionStore = class {
|
|
|
4687
4595
|
this.activeId = id;
|
|
4688
4596
|
}
|
|
4689
4597
|
file(id) {
|
|
4690
|
-
return
|
|
4598
|
+
return join4(this.dir, `${id}.json`);
|
|
4691
4599
|
}
|
|
4692
4600
|
/** Overwrite the active session with the current messages (no-op on an empty transcript). */
|
|
4693
4601
|
async save(messages) {
|
|
@@ -4714,7 +4622,7 @@ var SessionStore = class {
|
|
|
4714
4622
|
for (const n of names) {
|
|
4715
4623
|
if (!n.endsWith(".json")) continue;
|
|
4716
4624
|
try {
|
|
4717
|
-
const d = JSON.parse(await readFile2(
|
|
4625
|
+
const d = JSON.parse(await readFile2(join4(this.dir, n), "utf8"));
|
|
4718
4626
|
out.push({ id: d.id, title: d.title, updatedAt: d.updatedAt, count: d.count });
|
|
4719
4627
|
} catch {
|
|
4720
4628
|
}
|
|
@@ -4740,7 +4648,7 @@ function titleOf(messages) {
|
|
|
4740
4648
|
// src/session/pins.ts
|
|
4741
4649
|
import { mkdir as mkdir2, readFile as readFile3 } from "fs/promises";
|
|
4742
4650
|
import { createHash as createHash2 } from "crypto";
|
|
4743
|
-
import { join as
|
|
4651
|
+
import { join as join5, dirname } from "path";
|
|
4744
4652
|
var MAX_PINS = 20;
|
|
4745
4653
|
var MAX_PIN_CHARS = 500;
|
|
4746
4654
|
var PinStore = class {
|
|
@@ -4748,7 +4656,7 @@ var PinStore = class {
|
|
|
4748
4656
|
cache;
|
|
4749
4657
|
constructor(opts) {
|
|
4750
4658
|
const hash = createHash2("sha256").update(opts.cwd).digest("hex").slice(0, 16);
|
|
4751
|
-
this.file =
|
|
4659
|
+
this.file = join5(opts.home, ".horsecode", "projects", hash, "pins.json");
|
|
4752
4660
|
}
|
|
4753
4661
|
/** Load pins from disk (memoized). Missing/corrupt → empty. */
|
|
4754
4662
|
async load() {
|
|
@@ -4798,6 +4706,8 @@ var ModelHealth = class {
|
|
|
4798
4706
|
fitness;
|
|
4799
4707
|
listModels;
|
|
4800
4708
|
probe;
|
|
4709
|
+
/** Routability, not recovery — see ModelHealthOpts.routable. */
|
|
4710
|
+
routable;
|
|
4801
4711
|
note;
|
|
4802
4712
|
now;
|
|
4803
4713
|
/** Serializes healing: a whole review team failing at once must not trigger 14 concurrent re-assignments. */
|
|
@@ -4873,6 +4783,7 @@ var ModelHealth = class {
|
|
|
4873
4783
|
this.port = opts.port;
|
|
4874
4784
|
this.listModels = opts.listModels;
|
|
4875
4785
|
this.probe = opts.probe;
|
|
4786
|
+
this.routable = opts.routable;
|
|
4876
4787
|
this.note = opts.note ?? (() => {
|
|
4877
4788
|
});
|
|
4878
4789
|
this.now = opts.now ?? (() => Date.now());
|
|
@@ -4893,7 +4804,38 @@ var ModelHealth = class {
|
|
|
4893
4804
|
mark(model, reason) {
|
|
4894
4805
|
for (const r of this.port.registries()) r.markExhausted(model, reason, this.now());
|
|
4895
4806
|
}
|
|
4896
|
-
/**
|
|
4807
|
+
/**
|
|
4808
|
+
* Models that may be assigned right now: the catalog, minus everything quarantined, minus what no role has
|
|
4809
|
+
* ever been configured with.
|
|
4810
|
+
*
|
|
4811
|
+
* The catalog is what the gateway LISTS, which is not what it will route to. Measured across a whole run:
|
|
4812
|
+
* `opencode-go/muse-spark-1.2-contributor-xhigh` is in the catalog of 726 and answers every request with
|
|
4813
|
+
* "not available in the active live catalog". It was in nobody's chain — `/roles adjust` had already
|
|
4814
|
+
* dropped it — and re-assignment handed it out anyway, to twenty-three parallel slots at once. It took the
|
|
4815
|
+
* ladders of twenty-one tasks and, through their dependencies, ninety-seven more.
|
|
4816
|
+
*
|
|
4817
|
+
* The configured set is the curated one: every model in it was chosen for a role and has been reached at
|
|
4818
|
+
* least once. Preferring it costs nothing, because the wider catalog is still there when the curated pool
|
|
4819
|
+
* is spent — which is the only situation that ever needed the breadth.
|
|
4820
|
+
*
|
|
4821
|
+
* That fallback is the door the herd came back through, so nothing walks through it unproven. Measured on
|
|
4822
|
+
* the run after the preference was added: the curated pool emptied for about a minute, the catalog came
|
|
4823
|
+
* back whole, and `muse-spark-1.2-contributor-xhigh` — ranked top by name alone — was handed to every role
|
|
4824
|
+
* being re-assigned at the same instant. 204 calls left before the first answer could bench it. Its eight
|
|
4825
|
+
* siblings each cost exactly ONE call, which is the bench working; the difference is only that they were
|
|
4826
|
+
* not the head of the ranking.
|
|
4827
|
+
*
|
|
4828
|
+
* A probe is the cheapest thing that can tell "listed" from "routable", it runs only on this rare path, and
|
|
4829
|
+
* `poolWithConfigured` already proves the same pattern at startup. It must be the ROUTABILITY probe (see
|
|
4830
|
+
* `routable`): asking the recovery probe instead rejects everything the moment the fleet is busy, which is
|
|
4831
|
+
* exactly when this path runs.
|
|
4832
|
+
*
|
|
4833
|
+
* And a probe that clears nothing does not get to strand every role. Measured live: "probed 78 catalog
|
|
4834
|
+
* model(s) and dropped 78", then `No healthy model left to reassign code-data-integrity` — the lens kept
|
|
4835
|
+
* its spent chain and returned UNVERIFIED, blocking the review. Handing back the unproven catalog risks
|
|
4836
|
+
* one wasted call per model, which the bench ends and the ladder no longer charges for; handing back
|
|
4837
|
+
* nothing leaves a role with no way to run at all. The first is recoverable, so it wins.
|
|
4838
|
+
*/
|
|
4897
4839
|
async healthyModels() {
|
|
4898
4840
|
const dead = new Set(this.quarantined().map((q) => q.model));
|
|
4899
4841
|
let all;
|
|
@@ -4902,7 +4844,46 @@ var ModelHealth = class {
|
|
|
4902
4844
|
} catch {
|
|
4903
4845
|
return [];
|
|
4904
4846
|
}
|
|
4905
|
-
|
|
4847
|
+
const live = all.filter((m) => !dead.has(m));
|
|
4848
|
+
const configured = new Set(this.port.registries().flatMap((r) => r.knownModels()));
|
|
4849
|
+
const curated = live.filter((m) => configured.has(m) || cliFor(m) !== void 0);
|
|
4850
|
+
telemetry().event("decision.pool", {
|
|
4851
|
+
"hc.decision": "pool",
|
|
4852
|
+
"hc.pool.catalog": all.length,
|
|
4853
|
+
"hc.pool.live": live.length,
|
|
4854
|
+
"hc.pool.configured": configured.size,
|
|
4855
|
+
"hc.pool.curated": curated.length,
|
|
4856
|
+
"hc.pool.source": curated.length ? "curated" : "fallback"
|
|
4857
|
+
});
|
|
4858
|
+
if (curated.length) return curated;
|
|
4859
|
+
const routable = this.routable;
|
|
4860
|
+
if (!routable || !live.length) {
|
|
4861
|
+
telemetry().event("decision.pool", {
|
|
4862
|
+
"hc.decision": "pool",
|
|
4863
|
+
"hc.pool.source": "unscreened",
|
|
4864
|
+
"hc.pool.live": live.length,
|
|
4865
|
+
"hc.pool.reason": routable ? "no live models" : "no routability probe"
|
|
4866
|
+
});
|
|
4867
|
+
return live;
|
|
4868
|
+
}
|
|
4869
|
+
const checked = await Promise.all(live.map(async (m) => ({ m, ok: await routable(m).catch(() => false) })));
|
|
4870
|
+
const usable = checked.filter((c) => c.ok).map((c) => c.m);
|
|
4871
|
+
if (!usable.length) {
|
|
4872
|
+
this.note(`\u{1F50E} The configured models are all spent, and none of the ${checked.length} catalog model(s) answered a probe \u2014 offering them anyway rather than leaving roles with nothing.`);
|
|
4873
|
+
return live;
|
|
4874
|
+
}
|
|
4875
|
+
const refused = checked.length - usable.length;
|
|
4876
|
+
telemetry().event("decision.pool", {
|
|
4877
|
+
"hc.decision": "pool",
|
|
4878
|
+
"hc.pool.source": "probed",
|
|
4879
|
+
"hc.pool.probed": checked.length,
|
|
4880
|
+
"hc.pool.refused": refused,
|
|
4881
|
+
"hc.pool.usable": usable.length
|
|
4882
|
+
});
|
|
4883
|
+
if (refused) {
|
|
4884
|
+
this.note(`\u{1F50E} The configured models are all spent \u2014 probed ${checked.length} catalog model(s) and dropped ${refused} the gateway would not route to.`);
|
|
4885
|
+
}
|
|
4886
|
+
return usable;
|
|
4906
4887
|
}
|
|
4907
4888
|
/**
|
|
4908
4889
|
* Re-probes the quarantine and releases whatever answers again. A 429 is a temporary limit, not a permanent
|
|
@@ -4970,6 +4951,12 @@ var ModelHealth = class {
|
|
|
4970
4951
|
const reg = this.port.registryFor(role);
|
|
4971
4952
|
const before = [...reg.rawChain(role)];
|
|
4972
4953
|
reg.setRoleModel(role, chain);
|
|
4954
|
+
telemetry().event("decision.rechain", {
|
|
4955
|
+
"hc.decision": "rechain",
|
|
4956
|
+
"hc.role": role,
|
|
4957
|
+
"hc.chain": chain.join(","),
|
|
4958
|
+
"hc.chain.before": before.join(",")
|
|
4959
|
+
});
|
|
4973
4960
|
out.push({ role, chain, before });
|
|
4974
4961
|
}
|
|
4975
4962
|
return out;
|
|
@@ -5204,7 +5191,7 @@ First explain your key choices in a few short sentences, including which skills
|
|
|
5204
5191
|
// src/engine/scan-repo.ts
|
|
5205
5192
|
import { spawn as spawn2 } from "child_process";
|
|
5206
5193
|
import { readFile as readFile4 } from "fs/promises";
|
|
5207
|
-
import { join as
|
|
5194
|
+
import { join as join6 } from "path";
|
|
5208
5195
|
var MANIFESTS = ["package.json", "pyproject.toml", "requirements.txt", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle", "composer.json"];
|
|
5209
5196
|
var MAX_FILES = 2e4;
|
|
5210
5197
|
function gitFiles(cwd) {
|
|
@@ -5224,7 +5211,7 @@ async function scanRepo(cwd) {
|
|
|
5224
5211
|
for (const name of MANIFESTS) {
|
|
5225
5212
|
if (!files.includes(name)) continue;
|
|
5226
5213
|
try {
|
|
5227
|
-
manifests[name] = await readFile4(
|
|
5214
|
+
manifests[name] = await readFile4(join6(cwd, name), "utf8");
|
|
5228
5215
|
} catch {
|
|
5229
5216
|
}
|
|
5230
5217
|
}
|
|
@@ -5373,6 +5360,12 @@ function startupSummary(f) {
|
|
|
5373
5360
|
`- Rules: ${f.rules} active${f.rules ? " (`/memories` to read them)" : ""}`,
|
|
5374
5361
|
`- Memory: ${plural(m.total, "entry", "entries")}${kinds ? ` \u2014 ${kinds}` : ""}`,
|
|
5375
5362
|
`- Skills: ${plural(f.skills, "available", "available")}`,
|
|
5363
|
+
/**
|
|
5364
|
+
* The one line about what the work will be CHARGED to, so it sits with the rest of what the session has
|
|
5365
|
+
* to work with. It was previously printed with `console.log` before this panel rendered — which the TUI
|
|
5366
|
+
* then cleared, so a person with two working logins was shown nothing at all.
|
|
5367
|
+
*/
|
|
5368
|
+
...f.accounts ? [`- Accounts: ${f.accounts}`] : [],
|
|
5376
5369
|
/**
|
|
5377
5370
|
* "traces" is named precisely, because the word is taken.
|
|
5378
5371
|
*
|
|
@@ -5395,7 +5388,7 @@ function startupSummary(f) {
|
|
|
5395
5388
|
|
|
5396
5389
|
// src/tui/app.tsx
|
|
5397
5390
|
import { execFileSync } from "child_process";
|
|
5398
|
-
import { existsSync } from "fs";
|
|
5391
|
+
import { existsSync as existsSync2 } from "fs";
|
|
5399
5392
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
5400
5393
|
async function runTui(opts) {
|
|
5401
5394
|
const controller = new TuiController();
|
|
@@ -5445,7 +5438,7 @@ async function runTuiRepl(opts) {
|
|
|
5445
5438
|
const chain = regFor(r).chain(r);
|
|
5446
5439
|
return { name: r, model: chain[0] ?? "", models: chain, council: reviewNames.has(r), decider: councilNames.includes(r) };
|
|
5447
5440
|
});
|
|
5448
|
-
const fitness = deps0.fitness ?? new RoleFitness(
|
|
5441
|
+
const fitness = deps0.fitness ?? new RoleFitness(join7(homedir2(), ".horsecode", "model-fitness.json"));
|
|
5449
5442
|
for (const r of [deps0.roleRegistry, deps0.teamRegistries.spec, deps0.teamRegistries.plan, deps0.teamRegistries.code, deps0.councilRegistry]) {
|
|
5450
5443
|
r.setFitness(fitness);
|
|
5451
5444
|
}
|
|
@@ -5460,6 +5453,12 @@ async function runTuiRepl(opts) {
|
|
|
5460
5453
|
// STRICTER than source discovery, which counts 429 as "routed": a rate-limited model is exactly what was
|
|
5461
5454
|
// quarantined, so only a real answer may release it.
|
|
5462
5455
|
...opts.probeModel ? { probe: opts.probeModel } : {},
|
|
5456
|
+
/**
|
|
5457
|
+
* …and the 429-tolerant one for the other question. `healthyModels` asks whether the gateway will ROUTE
|
|
5458
|
+
* to a model, not whether a benched one has recovered, and it asks precisely when the fleet is busiest.
|
|
5459
|
+
* Given the strict probe it dropped all 78 catalog models at once — see ModelHealthOpts.routable.
|
|
5460
|
+
*/
|
|
5461
|
+
...opts.routableModel ? { routable: opts.routableModel } : {},
|
|
5463
5462
|
note: (m) => controller.note(m)
|
|
5464
5463
|
});
|
|
5465
5464
|
health.watch();
|
|
@@ -5570,7 +5569,7 @@ Question: ${question}` }
|
|
|
5570
5569
|
};
|
|
5571
5570
|
const addMcp = async (input) => {
|
|
5572
5571
|
const { parseCommand, parseConfigBlock, extractFromPage, verify } = await import("./install-O34KMWJB.js");
|
|
5573
|
-
const { saveMcpServer } = await import("./save-skills-
|
|
5572
|
+
const { saveMcpServer } = await import("./save-skills-ZW5GY6KV.js");
|
|
5574
5573
|
let cand = parseCommand(input) ?? parseConfigBlock(input);
|
|
5575
5574
|
if (!cand) {
|
|
5576
5575
|
const url = input.trim();
|
|
@@ -5619,7 +5618,7 @@ _Restart to connect it._`;
|
|
|
5619
5618
|
const baseRef = { current: opts.jobBase.fromBranch };
|
|
5620
5619
|
const continueFromClaude = async (arg) => {
|
|
5621
5620
|
const { adoptClaudeWorktree, describeAdoption, AdoptError, listClaudeWorktrees } = await import("./worktree-F7TWLWLN.js");
|
|
5622
|
-
const { defaultGitRunner } = await import("./git-
|
|
5621
|
+
const { defaultGitRunner } = await import("./git-QZTDZSJY.js");
|
|
5623
5622
|
const git = defaultGitRunner;
|
|
5624
5623
|
try {
|
|
5625
5624
|
const w = await adoptClaudeWorktree(git, process.cwd(), arg, baseRef.current);
|
|
@@ -5642,7 +5641,7 @@ _This project has no worktrees under \`.claude/worktrees\`._`}`);
|
|
|
5642
5641
|
const migrate = async () => {
|
|
5643
5642
|
if (!opts.memStore) return "Migration needs the memory store, which is not available in this session.";
|
|
5644
5643
|
const progressLines = /* @__PURE__ */ new Map();
|
|
5645
|
-
const { runMigration, describeResult } = await import("./run-
|
|
5644
|
+
const { runMigration, describeResult } = await import("./run-P6ZYL5JL.js");
|
|
5646
5645
|
const r = await runMigration({
|
|
5647
5646
|
cwd: process.cwd(),
|
|
5648
5647
|
home: homedir2(),
|
|
@@ -5868,9 +5867,13 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5868
5867
|
rules: rulesFromMemory().length,
|
|
5869
5868
|
memory: { total: all.length, rules: kindOf("rule"), lessons: kindOf("lesson"), facts: kindOf("fact") },
|
|
5870
5869
|
skills: opts.listSkills?.().length ?? 0,
|
|
5871
|
-
constitution:
|
|
5870
|
+
constitution: existsSync2(join7(process.cwd(), ".specify", "memory", "constitution.md")),
|
|
5872
5871
|
graph: { built: false, nodes: 0 },
|
|
5873
5872
|
traceRoot: traceRootRel(),
|
|
5873
|
+
...(() => {
|
|
5874
|
+
const a = opts.accountsNote?.();
|
|
5875
|
+
return a ? { accounts: a } : {};
|
|
5876
|
+
})(),
|
|
5874
5877
|
...unfinished.length ? { unfinished } : {},
|
|
5875
5878
|
...startupExtra
|
|
5876
5879
|
};
|
|
@@ -5899,12 +5902,12 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5899
5902
|
paintSummary();
|
|
5900
5903
|
void (async () => {
|
|
5901
5904
|
try {
|
|
5902
|
-
const { defaultGitRunner } = await import("./git-
|
|
5905
|
+
const { defaultGitRunner } = await import("./git-QZTDZSJY.js");
|
|
5903
5906
|
const { recordedMainBranch, detectMainBranch } = await import("./main-branch-KGWUINYQ.js");
|
|
5904
5907
|
const cwd = process.cwd();
|
|
5905
5908
|
const main = await recordedMainBranch(cwd) ?? await detectMainBranch(cwd, defaultGitRunner);
|
|
5906
5909
|
if (!main) return;
|
|
5907
|
-
const { ongoingWork, chooseOngoing } = await import("./ongoing-
|
|
5910
|
+
const { ongoingWork, chooseOngoing } = await import("./ongoing-WHYXPW24.js");
|
|
5908
5911
|
const open = await ongoingWork(defaultGitRunner, cwd, main);
|
|
5909
5912
|
if (!open.length) return;
|
|
5910
5913
|
const picked = await chooseOngoing(deps, read, open[0]?.language, open);
|
|
@@ -5923,11 +5926,11 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5923
5926
|
})();
|
|
5924
5927
|
void (async () => {
|
|
5925
5928
|
try {
|
|
5926
|
-
const { loadTraceIndex } = await import("./trace-
|
|
5929
|
+
const { loadTraceIndex } = await import("./trace-X6TU3AG6.js");
|
|
5927
5930
|
let index = await loadTraceIndex(process.cwd());
|
|
5928
5931
|
startupExtra.traces = Object.keys(index.traces).length;
|
|
5929
5932
|
paintSummary();
|
|
5930
|
-
const { graphStatus: graphStatus2 } = await import("./project-graph-
|
|
5933
|
+
const { graphStatus: graphStatus2 } = await import("./project-graph-5HNPRFQG.js");
|
|
5931
5934
|
const g = await graphStatus2(process.cwd());
|
|
5932
5935
|
startupExtra.graph = { built: g.built, nodes: g.nodes, stale: g.stale };
|
|
5933
5936
|
if (!g.built || g.stale) {
|
|
@@ -5936,18 +5939,18 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5936
5939
|
const after = await graphStatus2(process.cwd());
|
|
5937
5940
|
startupExtra.graph = { built: after.built, nodes: after.nodes, stale: after.stale };
|
|
5938
5941
|
}
|
|
5939
|
-
const g0 = await import("./project-graph-
|
|
5942
|
+
const g0 = await import("./project-graph-5HNPRFQG.js").then((m) => m.loadGraphSync(process.cwd()));
|
|
5940
5943
|
if (g0) {
|
|
5941
5944
|
const files = /* @__PURE__ */ new Set();
|
|
5942
5945
|
for (const n of g0.nodes) if (n.source_file) files.add(n.source_file);
|
|
5943
|
-
const { adoptDocs, indexAdoption, describeAdoption } = await import("./trace-adopt-
|
|
5946
|
+
const { adoptDocs, indexAdoption, describeAdoption } = await import("./trace-adopt-URECQWJV.js");
|
|
5944
5947
|
const adoption = await adoptDocs(process.cwd(), files);
|
|
5945
5948
|
const { readFile: readFile5 } = await import("fs/promises");
|
|
5946
5949
|
const r = await indexAdoption(
|
|
5947
5950
|
process.cwd(),
|
|
5948
5951
|
index,
|
|
5949
5952
|
adoption,
|
|
5950
|
-
async (f) => readFile5(
|
|
5953
|
+
async (f) => readFile5(join7(process.cwd(), f), "utf8").catch(() => void 0)
|
|
5951
5954
|
);
|
|
5952
5955
|
if (r.added) {
|
|
5953
5956
|
index = r.index;
|
|
@@ -5956,8 +5959,8 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
5956
5959
|
}
|
|
5957
5960
|
startupExtra.traces = Object.keys(index.traces).length;
|
|
5958
5961
|
paintSummary();
|
|
5959
|
-
const { traceableFiles } = await import("./trace-run-
|
|
5960
|
-
const { traceCoverage } = await import("./trace-
|
|
5962
|
+
const { traceableFiles } = await import("./trace-run-7U4WJZ3V.js");
|
|
5963
|
+
const { traceCoverage } = await import("./trace-X6TU3AG6.js");
|
|
5961
5964
|
startupExtra.coverage = await traceCoverage(process.cwd(), await traceableFiles(process.cwd()), index);
|
|
5962
5965
|
paintSummary();
|
|
5963
5966
|
} catch {
|
|
@@ -6073,6 +6076,7 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
|
|
|
6073
6076
|
setRoleModel: applyChainPersisted,
|
|
6074
6077
|
listRoles,
|
|
6075
6078
|
adjustRoles,
|
|
6079
|
+
modelsPanel: opts.modelsPanel,
|
|
6076
6080
|
listSkills: opts.listSkills,
|
|
6077
6081
|
updateSkills: opts.updateSkills,
|
|
6078
6082
|
addSkill: opts.addSkill,
|