@hizliemre/horse-code 0.3.1 → 0.4.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.
@@ -2,7 +2,7 @@ import {
2
2
  Board,
3
3
  refreshAfterChange,
4
4
  runTaskCycle
5
- } from "./chunk-27F44PBD.js";
5
+ } from "./chunk-PIG54WFU.js";
6
6
  import {
7
7
  defaultGitRunner
8
8
  } from "./chunk-LPQU436C.js";
package/dist/cli.js CHANGED
@@ -15,26 +15,28 @@ import {
15
15
  restoreTerminal,
16
16
  runJob,
17
17
  sttySane
18
- } from "./chunk-2WXG35EM.js";
18
+ } from "./chunk-KXBYRU4W.js";
19
19
  import "./chunk-M2RKCIGV.js";
20
+ import "./chunk-UTHLEW5V.js";
20
21
  import {
21
22
  CliProvider,
22
23
  RoleFitness,
23
24
  SHORT_CALL_MS,
24
- WorktreeManager,
25
25
  buildCouncilRegistry,
26
- buildTeamRegistry,
26
+ buildTeamRegistry
27
+ } from "./chunk-PIG54WFU.js";
28
+ import {
29
+ WorktreeManager,
27
30
  mainWorktreeRoot,
28
31
  toSlug
29
- } from "./chunk-27F44PBD.js";
32
+ } from "./chunk-NBTH2VVI.js";
33
+ import "./chunk-ZSQ24YDJ.js";
30
34
  import "./chunk-QF4MP6BS.js";
31
35
  import {
32
36
  MAX_CHUNK_CHARS,
33
37
  discover,
34
38
  readFinding
35
39
  } from "./chunk-QJYVZPLG.js";
36
- import "./chunk-UTHLEW5V.js";
37
- import "./chunk-ZSQ24YDJ.js";
38
40
  import {
39
41
  parseFrontmatter
40
42
  } from "./chunk-BY4DP7IE.js";
@@ -45,14 +47,7 @@ import {
45
47
  } from "./chunk-JR2JLRE3.js";
46
48
  import "./chunk-H2FDGPVW.js";
47
49
  import {
48
- CODE_TEAM,
49
- DEFAULT_COUNCIL,
50
- DEFAULT_PROMPTS,
51
- DEFAULT_ROLE_SKILLS,
52
- PLAN_TEAM,
53
- REQUIRED_ROLES,
54
50
  RoleRegistry,
55
- SPEC_TEAM,
56
51
  ZAI_MODELS,
57
52
  buildBrief,
58
53
  cliCatalog,
@@ -61,7 +56,16 @@ import {
61
56
  planFor,
62
57
  runTraces,
63
58
  traceableFiles
64
- } from "./chunk-YULQ4URQ.js";
59
+ } from "./chunk-ACTVFJRW.js";
60
+ import {
61
+ CODE_TEAM,
62
+ DEFAULT_COUNCIL,
63
+ DEFAULT_PROMPTS,
64
+ DEFAULT_ROLE_SKILLS,
65
+ PLAN_TEAM,
66
+ REQUIRED_ROLES,
67
+ SPEC_TEAM
68
+ } from "./chunk-MZM24M5M.js";
65
69
  import {
66
70
  CLI_KINDS,
67
71
  checkProfile,
@@ -74,10 +78,10 @@ import {
74
78
  InjectionLog,
75
79
  memoryNote
76
80
  } from "./chunk-BFIZMM4G.js";
77
- import "./chunk-63E73TGI.js";
78
81
  import {
79
82
  ToolRegistry
80
83
  } from "./chunk-CYLPQWIF.js";
84
+ import "./chunk-63E73TGI.js";
81
85
  import {
82
86
  briefStatus
83
87
  } from "./chunk-6S4WWQMN.js";
@@ -1641,6 +1645,7 @@ function renderResult(res) {
1641
1645
  if (res.kind === "rejected") return `Not approved (stopped at the ${res.stage} stage).`;
1642
1646
  if (res.kind === "undone") return res.report;
1643
1647
  if (res.kind === "verified") return res.report;
1648
+ if (res.kind === "researched") return res.report;
1644
1649
  if (res.kind === "tweaked") return res.report;
1645
1650
  if (res.kind === "governed") {
1646
1651
  return res.written ? `**Constitution written** \u2014 \`${res.path}\`
@@ -1929,7 +1934,7 @@ async function main(argv) {
1929
1934
  const useTui = shouldUseTui(!!process.stdin.isTTY, !!process.stdout.isTTY, !!args.noTui);
1930
1935
  if (!args.prompt) {
1931
1936
  if (useTui) {
1932
- const { runTuiRepl } = await import("./app-4WN37LZ3.js");
1937
+ const { runTuiRepl } = await import("./app-KR7TAHUD.js");
1933
1938
  const { fetchCatalog, makeProbe, discoverSources } = await import("./discover-G2Z6XC3O.js");
1934
1939
  const { loadSourceCache, saveSourceCache } = await import("./source-cache-XEK5WN7I.js");
1935
1940
  const manualSources = config.modelSources.length > 0;
@@ -2126,7 +2131,7 @@ _Committed with the repo, so every clone starts with them. Agents read one with
2126
2131
  ...args.revisionRounds !== void 0 && { revisionRounds: args.revisionRounds }
2127
2132
  };
2128
2133
  if (useTui) {
2129
- const { runTui } = await import("./app-4WN37LZ3.js");
2134
+ const { runTui } = await import("./app-KR7TAHUD.js");
2130
2135
  const res = await runTui({ buildDeps, job });
2131
2136
  console.log(renderResult(res));
2132
2137
  return;
@@ -8,15 +8,17 @@ import {
8
8
  dirtyPaths,
9
9
  runFix,
10
10
  runSmallChange
11
- } from "./chunk-3ACDNDCG.js";
12
- import "./chunk-27F44PBD.js";
11
+ } from "./chunk-UANNVVIU.js";
12
+ import "./chunk-PIG54WFU.js";
13
+ import "./chunk-NBTH2VVI.js";
13
14
  import "./chunk-ZSQ24YDJ.js";
14
- import "./chunk-YULQ4URQ.js";
15
+ import "./chunk-ACTVFJRW.js";
16
+ import "./chunk-MZM24M5M.js";
15
17
  import "./chunk-G45RWL7S.js";
16
18
  import "./chunk-LPQU436C.js";
17
19
  import "./chunk-BFIZMM4G.js";
18
- import "./chunk-63E73TGI.js";
19
20
  import "./chunk-CYLPQWIF.js";
21
+ import "./chunk-63E73TGI.js";
20
22
  import "./chunk-6S4WWQMN.js";
21
23
  import "./chunk-JLWQCA7B.js";
22
24
  import "./chunk-ZPJP2VH5.js";
@@ -0,0 +1,111 @@
1
+ import {
2
+ createWebFetchTool,
3
+ readOnlyRegistry
4
+ } from "./chunk-NBTH2VVI.js";
5
+ import "./chunk-ZSQ24YDJ.js";
6
+ import "./chunk-MZM24M5M.js";
7
+ import {
8
+ defaultGitRunner
9
+ } from "./chunk-LPQU436C.js";
10
+ import "./chunk-BFIZMM4G.js";
11
+ import "./chunk-CYLPQWIF.js";
12
+ import "./chunk-63E73TGI.js";
13
+ import "./chunk-6S4WWQMN.js";
14
+ import {
15
+ runToCompletion
16
+ } from "./chunk-JLWQCA7B.js";
17
+ import "./chunk-ZPJP2VH5.js";
18
+ import "./chunk-4M6LXNG2.js";
19
+ import "./chunk-6W4UH2BQ.js";
20
+
21
+ // src/engine/research.ts
22
+ import { mkdir, writeFile } from "fs/promises";
23
+ import { dirname, join, relative } from "path";
24
+ import { existsSync } from "fs";
25
+ var RESEARCH_MAX_TURNS = 120;
26
+ function researchDir(trackedFiles2) {
27
+ const known = [/^docs\/research\//i, /^docs\/adr\//i, /^docs\/decisions\//i, /^research\//i, /^\.planning\//i];
28
+ for (const re of known) {
29
+ const hit = trackedFiles2.find((f) => re.test(f.replace(/\\/g, "/")));
30
+ if (hit) return dirname(hit.replace(/\\/g, "/"));
31
+ }
32
+ return "docs/research";
33
+ }
34
+ function reportName(title, now = /* @__PURE__ */ new Date()) {
35
+ const slug = (title || "research").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
36
+ const day = now.toISOString().slice(0, 10);
37
+ return `${day}-${slug || "research"}.md`;
38
+ }
39
+ var researchRule = "You are researching a question and writing ONE document. You are not changing this project: you have no tool that writes a file, and the report is your final answer \u2014 horse-code writes it down.\n\nGround the answer in THIS repository before anything else. Read the code that the question is about; a recommendation that ignores what is already here is a recommendation for a different project.\n\nThe document must contain, in this order:\n1. The question, restated in one or two sentences as you understood it.\n2. What this project does today in the area concerned, with file paths \u2014 the starting position.\n3. The options, one section each. For every option: how it would work here, what it costs, and what it rules out. An option nobody would pick is still worth a paragraph saying why not.\n4. A comparison table across the dimensions that actually decide this one. Choose the dimensions from the question rather than from habit.\n5. A recommendation, with the condition that would change it. A recommendation that nothing could overturn is a preference wearing a suit.\n6. What you could not establish, and what it would take to settle. Say it plainly \u2014 an unexamined assumption presented as a finding is the one failure this document cannot recover from.\n\nWrite markdown. Cite file paths for claims about this project and URLs for claims about the world. Where you are reasoning from your own knowledge rather than from something you read, say so.";
40
+ async function runResearch(opts) {
41
+ const { deps, workdir } = opts;
42
+ const dir = researchDir(opts.trackedFiles ?? await trackedFiles(workdir));
43
+ const rel = join(dir, reportName(opts.title));
44
+ const tools = readOnlyRegistry(deps, { remember: true });
45
+ tools.register(createWebFetchTool());
46
+ const language = opts.language ? `
47
+
48
+ Write the document in ${opts.language}. Keep file paths, identifiers and URLs exactly as they are.` : "";
49
+ const rules = deps.roleRegistry.ruleSuffix();
50
+ const last = await runToCompletion({
51
+ ...deps.roleRegistry.fallbackOpts("analyst"),
52
+ systemPrompt: researchRule + language + rules,
53
+ messages: [{ role: "user", content: opts.prompt }],
54
+ tools,
55
+ maxTurns: RESEARCH_MAX_TURNS,
56
+ provider: deps.provider,
57
+ permission: deps.permission,
58
+ approve: deps.approve,
59
+ cwd: workdir,
60
+ signal: deps.signal,
61
+ onActivity: deps.onActivity,
62
+ onLiveActivity: deps.onLiveActivity,
63
+ inbox: deps.inbox,
64
+ ...opts.note ? { onSay: opts.note } : {}
65
+ });
66
+ const body = (last.content ?? "").trim();
67
+ if (!body) return { reportPath: rel, written: false, committed: false };
68
+ const abs = join(workdir, rel);
69
+ await mkdir(dirname(abs), { recursive: true });
70
+ await writeFile(abs, `${body}
71
+ `, "utf8");
72
+ return { reportPath: rel, written: true, committed: await commitReport(workdir, rel, opts.title) };
73
+ }
74
+ async function trackedFiles(cwd) {
75
+ try {
76
+ const r = await defaultGitRunner(["ls-files"], cwd);
77
+ return r.code === 0 ? r.stdout.split("\n").filter(Boolean) : [];
78
+ } catch {
79
+ return [];
80
+ }
81
+ }
82
+ async function commitReport(workdir, rel, title) {
83
+ if (!existsSync(join(workdir, ".git")) && !existsSync(join(workdir, rel))) return false;
84
+ const add = await defaultGitRunner(["add", "--", rel], workdir);
85
+ if (add.code !== 0) return false;
86
+ const staged = await defaultGitRunner(["diff", "--cached", "--quiet", "--", rel], workdir);
87
+ if (staged.code === 0) return false;
88
+ const r = await defaultGitRunner(["commit", "-m", `docs(research): ${title || "findings"}`, "--", rel], workdir);
89
+ return r.code === 0;
90
+ }
91
+ function describeResearch(r, branch) {
92
+ if (!r.written) return "The research produced no document \u2014 nothing was written.";
93
+ const where = r.committed ? `Committed on \`${branch}\`.` : "Written, but not committed \u2014 it is in the working tree.";
94
+ return `\u{1F4C4} **Research report:** \`${r.reportPath}\`
95
+ ${where}
96
+
97
+ _No code was changed: the lane runs without a write tool at all._`;
98
+ }
99
+ function plannedPath(title, trackedFiles2) {
100
+ return relative(".", join(researchDir(trackedFiles2), reportName(title))).replace(/\\/g, "/");
101
+ }
102
+ export {
103
+ RESEARCH_MAX_TURNS,
104
+ commitReport,
105
+ describeResearch,
106
+ plannedPath,
107
+ reportName,
108
+ researchDir,
109
+ researchRule,
110
+ runResearch
111
+ };
@@ -7,7 +7,8 @@ import {
7
7
  planFor,
8
8
  runTraces,
9
9
  traceableFiles
10
- } from "./chunk-YULQ4URQ.js";
10
+ } from "./chunk-ACTVFJRW.js";
11
+ import "./chunk-MZM24M5M.js";
11
12
  import "./chunk-G45RWL7S.js";
12
13
  import "./chunk-LPQU436C.js";
13
14
  import "./chunk-6S4WWQMN.js";
@@ -10,8 +10,8 @@ import {
10
10
  triageFinding
11
11
  } from "./chunk-372X5HHU.js";
12
12
  import "./chunk-BFIZMM4G.js";
13
- import "./chunk-63E73TGI.js";
14
13
  import "./chunk-CYLPQWIF.js";
14
+ import "./chunk-63E73TGI.js";
15
15
  import "./chunk-6S4WWQMN.js";
16
16
  import "./chunk-JLWQCA7B.js";
17
17
  import "./chunk-ZPJP2VH5.js";
@@ -3,33 +3,36 @@ import {
3
3
  describeFix,
4
4
  dirtyPaths,
5
5
  runFix
6
- } from "./chunk-3ACDNDCG.js";
6
+ } from "./chunk-UANNVVIU.js";
7
7
  import {
8
8
  respondIn
9
9
  } from "./chunk-M2RKCIGV.js";
10
+ import {
11
+ askInUserLanguage,
12
+ inUserLanguage
13
+ } from "./chunk-UTHLEW5V.js";
10
14
  import {
11
15
  buildAskUserTool,
16
+ editFileTool,
17
+ refreshAfterChange,
18
+ shellTool,
19
+ writeFileTool
20
+ } from "./chunk-PIG54WFU.js";
21
+ import {
12
22
  buildRememberTool,
13
23
  constitutionNote,
14
24
  createWebFetchTool,
15
25
  deferMcp,
16
- editFileTool,
17
26
  featureSlugFor,
18
27
  gitTool,
19
- refreshAfterChange,
20
- shellTool,
21
28
  specsDir,
22
- verifyPaths,
23
- writeFileTool
24
- } from "./chunk-27F44PBD.js";
25
- import {
26
- askInUserLanguage,
27
- inUserLanguage
28
- } from "./chunk-UTHLEW5V.js";
29
+ verifyPaths
30
+ } from "./chunk-NBTH2VVI.js";
29
31
  import "./chunk-ZSQ24YDJ.js";
32
+ import "./chunk-ACTVFJRW.js";
30
33
  import {
31
34
  buildSkillTool
32
- } from "./chunk-YULQ4URQ.js";
35
+ } from "./chunk-MZM24M5M.js";
33
36
  import "./chunk-G45RWL7S.js";
34
37
  import {
35
38
  defaultGitRunner
@@ -48,10 +51,10 @@ import {
48
51
  readFileTool,
49
52
  reinforceUsed
50
53
  } from "./chunk-BFIZMM4G.js";
51
- import "./chunk-63E73TGI.js";
52
54
  import {
53
55
  ToolRegistry
54
56
  } from "./chunk-CYLPQWIF.js";
57
+ import "./chunk-63E73TGI.js";
55
58
  import "./chunk-6S4WWQMN.js";
56
59
  import {
57
60
  handedOver,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hizliemre/horse-code",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Terminal coding agent: one sentence to reviewed, committed code — in its own git worktree",
5
5
  "license": "MIT",
6
6
  "author": "Emre Hızlı <hizliemre@gmail.com>",