@hizliemre/horse-code 0.3.1 → 0.5.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.
@@ -10,28 +10,29 @@ import {
10
10
  restoreTerminal,
11
11
  runJob,
12
12
  sttySane
13
- } from "./chunk-2WXG35EM.js";
13
+ } from "./chunk-2JDMCRRD.js";
14
14
  import "./chunk-M2RKCIGV.js";
15
+ import "./chunk-DCK2QQ2S.js";
15
16
  import {
16
17
  RoleFitness,
17
- asChoice,
18
+ asChoice
19
+ } from "./chunk-M2RP2AGC.js";
20
+ import {
18
21
  describeUnfinished,
19
22
  toSlug,
20
23
  unfinishedSessions
21
- } from "./chunk-27F44PBD.js";
22
- import "./chunk-QF4MP6BS.js";
23
- import "./chunk-UTHLEW5V.js";
24
+ } from "./chunk-C3AOCIS2.js";
24
25
  import "./chunk-ZSQ24YDJ.js";
26
+ import "./chunk-QF4MP6BS.js";
25
27
  import {
26
28
  saveRoleSkills
27
- } from "./chunk-JR2JLRE3.js";
29
+ } from "./chunk-KFB5H4HX.js";
28
30
  import {
29
31
  objectField,
30
32
  patchConfig
31
33
  } from "./chunk-H2FDGPVW.js";
32
34
  import {
33
35
  DURABLE_ROLES,
34
- REQUIRED_ROLES,
35
36
  ROLE_PROFILES,
36
37
  adjustRoleModels,
37
38
  capabilityScore,
@@ -39,21 +40,25 @@ import {
39
40
  effortFor,
40
41
  filterModelsForRole,
41
42
  isKnownModel,
43
+ isPeer,
42
44
  modelBand,
43
45
  mostCapable,
44
46
  newestPrimary,
45
47
  sourceOf,
46
48
  strongestPrimary
47
- } from "./chunk-YULQ4URQ.js";
48
- import "./chunk-G45RWL7S.js";
49
+ } from "./chunk-UKIIQD6Q.js";
50
+ import {
51
+ REQUIRED_ROLES
52
+ } from "./chunk-MZM24M5M.js";
53
+ import "./chunk-PEAZQPEZ.js";
49
54
  import "./chunk-LPQU436C.js";
50
55
  import {
51
56
  memoryNote,
52
57
  memoryState,
53
58
  walkFiles
54
- } from "./chunk-BFIZMM4G.js";
59
+ } from "./chunk-LLJWUHI6.js";
60
+ import "./chunk-PTOFSTLL.js";
55
61
  import "./chunk-63E73TGI.js";
56
- import "./chunk-CYLPQWIF.js";
57
62
  import "./chunk-6S4WWQMN.js";
58
63
  import {
59
64
  estimateFreezeSeconds,
@@ -64,7 +69,7 @@ import {
64
69
  stripThinking,
65
70
  telemetry,
66
71
  writeHeapSnapshot
67
- } from "./chunk-JLWQCA7B.js";
72
+ } from "./chunk-FNUGG2FG.js";
68
73
  import {
69
74
  traceRootRel
70
75
  } from "./chunk-ZPJP2VH5.js";
@@ -154,17 +159,26 @@ function validateChains(assignments, roles, models, heuristic) {
154
159
  chain.push(m);
155
160
  }
156
161
  };
162
+ const primaryOf = (byRole.get(role) ?? [])[0];
163
+ const admissible = (m) => {
164
+ const head = chain[0] ?? primaryOf;
165
+ if (!head || m === head) return true;
166
+ return sourceOf(m) !== sourceOf(head) && isPeer(head, m) && !chain.some((c) => sourceOf(c) === sourceOf(m));
167
+ };
168
+ const addIf = (m) => {
169
+ if (admissible(m)) add(m);
170
+ };
157
171
  for (const m of byRole.get(role) ?? []) {
158
172
  if (chain.length >= 3) break;
159
- add(m);
173
+ addIf(m);
160
174
  }
161
175
  for (const m of heuMap.get(role) ?? []) {
162
176
  if (chain.length >= 3) break;
163
- add(m);
177
+ addIf(m);
164
178
  }
165
179
  for (const m of models) {
166
180
  if (chain.length >= 3) break;
167
- add(m);
181
+ addIf(m);
168
182
  }
169
183
  const tuned = DURABLE_ROLES.includes(role) ? strongestPrimary(chain, models) : chain;
170
184
  return { role, models: newestPrimary(tuned, models) };
@@ -5594,7 +5608,7 @@ Question: ${question}` }
5594
5608
  };
5595
5609
  const addMcp = async (input) => {
5596
5610
  const { parseCommand, parseConfigBlock, extractFromPage, verify } = await import("./install-O34KMWJB.js");
5597
- const { saveMcpServer } = await import("./save-skills-NPKTYNAF.js");
5611
+ const { saveMcpServer } = await import("./save-skills-7M3XUKMK.js");
5598
5612
  let cand = parseCommand(input) ?? parseConfigBlock(input);
5599
5613
  if (!cand) {
5600
5614
  const url = input.trim();
@@ -5934,7 +5948,7 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
5934
5948
  const cwd = process.cwd();
5935
5949
  const main = await recordedMainBranch(cwd) ?? await detectMainBranch(cwd, defaultGitRunner);
5936
5950
  if (!main) return;
5937
- const { ongoingWork, chooseOngoing } = await import("./ongoing-6NUSPSCV.js");
5951
+ const { ongoingWork, chooseOngoing } = await import("./ongoing-J2UQL7FG.js");
5938
5952
  const open = await ongoingWork(defaultGitRunner, cwd, main);
5939
5953
  if (!open.length) return;
5940
5954
  const picked = await chooseOngoing(deps, read, open[0]?.language, open);
@@ -5986,7 +6000,7 @@ _${saved ? `Saved to your config \u2014 future sessions start with these. ` : ""
5986
6000
  }
5987
6001
  startupExtra.traces = Object.keys(index.traces).length;
5988
6002
  paintSummary();
5989
- const { traceableFiles } = await import("./trace-run-CZWEZ4R6.js");
6003
+ const { traceableFiles } = await import("./trace-run-YJNCJPU4.js");
5990
6004
  const { traceCoverage } = await import("./trace-UVMZZRA5.js");
5991
6005
  startupExtra.coverage = await traceCoverage(process.cwd(), await traceableFiles(process.cwd()), index);
5992
6006
  paintSummary();
@@ -1,58 +1,61 @@
1
1
  import {
2
2
  respondIn
3
3
  } from "./chunk-M2RKCIGV.js";
4
+ import {
5
+ askInUserLanguage,
6
+ inUserLanguage
7
+ } from "./chunk-DCK2QQ2S.js";
4
8
  import {
5
9
  Board,
10
+ FactBus,
6
11
  LONG_CALL_MS,
7
12
  WHAT_IT_COST,
8
13
  buildAskUserTool,
9
- buildRememberTool,
10
14
  changedByMerge,
11
15
  commitFile,
12
16
  commitStep,
13
- constitutionNote,
14
- constitutionPath,
15
17
  createDefaultRegistry,
16
- describeInherited,
17
- describeTopUp,
18
18
  editFileTool,
19
19
  extractChoicesFrom,
20
20
  hasWorkAgainst,
21
- nextFeatureSlug,
22
21
  normalizeQuestion,
23
- readOnlyRegistry,
24
22
  refreshAfterChange,
25
- routeSkills,
26
23
  routeTask,
27
24
  runCycleWithRole,
28
25
  runImplementer,
29
26
  runReviewLoop,
30
- runReviewer,
31
- scaffoldFeature,
32
27
  squashTask,
33
28
  subjectOf,
34
29
  worktreeState,
35
30
  writeFileTool,
36
31
  writerRegistry
37
- } from "./chunk-27F44PBD.js";
32
+ } from "./chunk-M2RP2AGC.js";
38
33
  import {
39
- resolveMainBranch
40
- } from "./chunk-QF4MP6BS.js";
41
- import {
42
- askInUserLanguage,
43
- inUserLanguage
44
- } from "./chunk-UTHLEW5V.js";
34
+ buildRememberTool,
35
+ constitutionNote,
36
+ constitutionPath,
37
+ describeInherited,
38
+ describeTopUp,
39
+ nextFeatureSlug,
40
+ readOnlyRegistry,
41
+ routeSkills,
42
+ runReviewer,
43
+ scaffoldFeature
44
+ } from "./chunk-C3AOCIS2.js";
45
45
  import {
46
46
  clearCheckpoint,
47
47
  isContinuePrompt,
48
48
  readCheckpoint,
49
49
  writeCheckpoint
50
50
  } from "./chunk-ZSQ24YDJ.js";
51
+ import {
52
+ resolveMainBranch
53
+ } from "./chunk-QF4MP6BS.js";
51
54
  import {
52
55
  applySkills,
53
56
  buildSkillTool,
54
57
  placedSkills
55
- } from "./chunk-YULQ4URQ.js";
58
+ } from "./chunk-MZM24M5M.js";
56
59
  import {
57
60
  defaultGitRunner
58
61
  } from "./chunk-LPQU436C.js";
@@ -74,17 +77,17 @@ import {
74
77
  relationStrength,
75
78
  supersedes,
76
79
  verifyAnchors
77
- } from "./chunk-BFIZMM4G.js";
80
+ } from "./chunk-LLJWUHI6.js";
78
81
  import {
79
82
  ToolRegistry,
80
83
  runStructuredRole
81
- } from "./chunk-CYLPQWIF.js";
84
+ } from "./chunk-PTOFSTLL.js";
82
85
  import {
83
86
  handedOver,
84
87
  runToCompletion,
85
88
  stripThinking,
86
89
  telemetry
87
- } from "./chunk-JLWQCA7B.js";
90
+ } from "./chunk-FNUGG2FG.js";
88
91
  import {
89
92
  TRACE_INDEX,
90
93
  mergeTraceIndexes,
@@ -2737,7 +2740,7 @@ var RefinerSchema = z6.object({
2737
2740
  * needs no specification. The prompt's own words for `verify` describe that request exactly; they were just
2738
2741
  * nowhere near the field being filled in.
2739
2742
  */
2740
- intent: z6.enum(["chat", "feature", "bugfix", "govern", "undo", "verify"]).describe(
2743
+ intent: z6.enum(["chat", "feature", "bugfix", "govern", "undo", "verify", "research"]).describe(
2741
2744
  "What the request PRODUCES, not what it mentions. `verify`: a record of what EXISTING software DID \u2014 running scenarios, querying the database or logs for evidence, writing or extending a test report. Anything whose output is findings rather than changed behaviour is verify, including a follow-up that only adds more evidence to a report already being written. `feature`: new or changed behaviour in the product. `bugfix`: existing behaviour is wrong and must be corrected. `govern`: the output is a governing document (constitution, conventions), no source changes. `undo`: reverse what the previous turn did. `chat`: a question or conversation, nothing to build. A verify request never needs a specification or a plan: if the answer is 'run it and write down what happened', it is verify."
2742
2745
  ),
2743
2746
  // The natural language the user wrote in (English name, e.g. "Turkish") → the coach replies in it.
@@ -2765,6 +2768,7 @@ function routeIntent(intent) {
2765
2768
  if (intent === "chat") return "chat";
2766
2769
  if (intent === "undo") return "undo";
2767
2770
  if (intent === "verify") return "verify";
2771
+ if (intent === "research") return "research";
2768
2772
  return intent === "govern" ? "govern" : "pipeline";
2769
2773
  }
2770
2774
 
@@ -3221,7 +3225,7 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
3221
3225
  emitPhase("verify");
3222
3226
  const cwd = await documentWorkdir(process.cwd(), prompt, ensureWorktree, r.title);
3223
3227
  laneCheckpoint(cwd, "verify", resume, prompt, r);
3224
- const { runVerify, describeVerify, currentBranchOf } = await import("./verify-HWZBTK5X.js");
3228
+ const { runVerify, describeVerify, currentBranchOf } = await import("./verify-5CSN24AE.js");
3225
3229
  const branch = await currentBranchOf(cwd);
3226
3230
  const res = await runVerify({
3227
3231
  deps,
@@ -3242,6 +3246,31 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
3242
3246
  written: res.reportWritten
3243
3247
  };
3244
3248
  }
3249
+ if (laneFor(r, prompt, resume) === "research") {
3250
+ emitPhase("research");
3251
+ const cwd = await documentWorkdir(process.cwd(), prompt, ensureWorktree, r.title);
3252
+ laneCheckpoint(cwd, "research", resume, prompt, r);
3253
+ const { runResearch, describeResearch } = await import("./research-WQYDF53R.js");
3254
+ const { currentBranchOf } = await import("./verify-5CSN24AE.js");
3255
+ const branch = await currentBranchOf(cwd);
3256
+ const res = await runResearch({
3257
+ deps,
3258
+ workdir: cwd,
3259
+ prompt: r.refinedPrompt,
3260
+ title: r.title,
3261
+ // The report is read by a person, so it is written in theirs — see src/engine/language.ts.
3262
+ ...r.language ? { language: r.language } : {},
3263
+ note: (text) => emit({ kind: "note", text })
3264
+ });
3265
+ return {
3266
+ intent: r.intent,
3267
+ refinedPrompt: r.refinedPrompt,
3268
+ kind: "researched",
3269
+ report: describeResearch(res, branch),
3270
+ reportPath: res.reportPath,
3271
+ written: res.written
3272
+ };
3273
+ }
3245
3274
  if (laneFor(r, prompt, resume) === "govern") {
3246
3275
  emitPhase("constitution");
3247
3276
  const templates2 = await deps.specKit();
@@ -3264,11 +3293,11 @@ async function runUpstream(deps, ensureWorktree, prompt, askUser, maxRounds, his
3264
3293
  if (!resume && !hasPreservedWork && routeIntent(r.intent) === "pipeline") {
3265
3294
  const cwd = workingIn?.() ?? process.cwd();
3266
3295
  emitPhase("sizing");
3267
- const { sizeRequest } = await import("./triage-IFCVL5MA.js");
3296
+ const { sizeRequest } = await import("./triage-ELDGP3JC.js");
3268
3297
  const size = await sizeRequest(deps, cwd, r.refinedPrompt);
3269
3298
  let small = size.verdict === "small";
3270
3299
  if (size.verdict === "unsure") {
3271
- const { describeSizeDoubt } = await import("./triage-IFCVL5MA.js");
3300
+ const { describeSizeDoubt } = await import("./triage-ELDGP3JC.js");
3272
3301
  const answer = await askInUserLanguage(
3273
3302
  deps,
3274
3303
  askUser,
@@ -3286,8 +3315,8 @@ Which is it?`,
3286
3315
  if (small) {
3287
3316
  emitPhase("small change");
3288
3317
  emit({ kind: "note", text: `\u26A1 Small change \u2014 ${size.reason}. No branch, no spec, no plan.` });
3289
- const { runSmallChange, describeSmallChange } = await import("./fix-QCL5AITT.js");
3290
- const { currentBranchOf } = await import("./verify-HWZBTK5X.js");
3318
+ const { runSmallChange, describeSmallChange } = await import("./fix-UGGMUNZI.js");
3319
+ const { currentBranchOf } = await import("./verify-5CSN24AE.js");
3291
3320
  const res = await runSmallChange(deps, cwd, r.title, r.refinedPrompt, size);
3292
3321
  return {
3293
3322
  intent: r.intent,
@@ -4048,6 +4077,9 @@ async function runJob(deps, opts) {
4048
4077
  if (up.kind === "tweaked") {
4049
4078
  return { kind: "tweaked", report: up.report, done: up.done, refinedPrompt: up.refinedPrompt };
4050
4079
  }
4080
+ if (up.kind === "researched") {
4081
+ return { kind: "researched", report: up.report, reportPath: up.reportPath, written: up.written, refinedPrompt: up.refinedPrompt };
4082
+ }
4051
4083
  if (up.kind === "verified") {
4052
4084
  return { kind: "verified", report: up.report, reportPath: up.reportPath, written: up.written, refinedPrompt: up.refinedPrompt };
4053
4085
  }
@@ -4089,13 +4121,24 @@ async function runJob(deps, opts) {
4089
4121
  };
4090
4122
  board.onMove = (card, _from, to, actor) => emit({ kind: "note", text: moveNote(card, to, actor) });
4091
4123
  emit({ kind: "phase", phase: "waves" });
4124
+ const facts = new FactBus();
4092
4125
  const wave = await runWaves(
4093
- { ...deps, askUser: opts.askUser },
4126
+ { ...deps, askUser: opts.askUser, facts },
4094
4127
  session,
4095
4128
  board,
4096
4129
  { base: opts.fromBranch, prTitle: opts.prTitle, request: opts.prompt }
4097
4130
  );
4098
4131
  emit({ kind: "phase", phase: "waves-done", detail: wave.status });
4132
+ const factStats = facts.stats();
4133
+ telemetry().event("decision.shared_facts", {
4134
+ "hc.decision": "shared_facts",
4135
+ "hc.facts.published": factStats.published,
4136
+ "hc.facts.refused": factStats.refused,
4137
+ "hc.facts.delivered": factStats.delivered,
4138
+ "hc.facts.dropped": factStats.dropped,
4139
+ "hc.facts.readers": factStats.readers,
4140
+ "hc.facts.cards": board.list().length
4141
+ });
4099
4142
  let revision;
4100
4143
  let deferredAll = [];
4101
4144
  const reviewable = board.list().some((c) => c.column === "MERGED");