@hizliemre/horse-code 0.3.0 → 0.3.1
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-5FXHE7GX.js → app-4WN37LZ3.js} +65 -37
- package/dist/{chunk-UEWVVN5L.js → chunk-27F44PBD.js} +196 -1126
- package/dist/{chunk-XYZVZPAY.js → chunk-2WXG35EM.js} +19 -15
- package/dist/{chunk-LNW557IO.js → chunk-372X5HHU.js} +2 -2
- package/dist/{chunk-YPZP7LYL.js → chunk-3ACDNDCG.js} +1 -1
- package/dist/{chunk-XEGQT5EN.js → chunk-4M6LXNG2.js} +1 -1
- package/dist/{chunk-6OSEQOYY.js → chunk-6S4WWQMN.js} +2 -2
- package/dist/{chunk-LLL7QWXB.js → chunk-BFIZMM4G.js} +6 -6
- package/dist/chunk-CYLPQWIF.js +214 -0
- package/dist/{chunk-AE36LLL2.js → chunk-JLWQCA7B.js} +2 -209
- package/dist/{run-P6ZYL5JL.js → chunk-QJYVZPLG.js} +133 -389
- package/dist/{chunk-UGESK765.js → chunk-UTHLEW5V.js} +1 -1
- package/dist/chunk-YULQ4URQ.js +1220 -0
- package/dist/{chunk-KAGKX2YT.js → chunk-ZPJP2VH5.js} +10 -1
- package/dist/cli.js +254 -66
- package/dist/{fix-ONLA45HD.js → fix-QCL5AITT.js} +9 -8
- package/dist/{ongoing-WHYXPW24.js → ongoing-6NUSPSCV.js} +3 -2
- package/dist/{project-graph-5HNPRFQG.js → project-graph-OGIM2B33.js} +1 -1
- package/dist/run-V5ZLZ3LS.js +274 -0
- package/dist/{save-skills-ZW5GY6KV.js → save-skills-NPKTYNAF.js} +2 -2
- package/dist/{trace-X6TU3AG6.js → trace-UVMZZRA5.js} +1 -1
- package/dist/{trace-adopt-URECQWJV.js → trace-adopt-7HWELJFE.js} +1 -1
- package/dist/{trace-run-7U4WJZ3V.js → trace-run-CZWEZ4R6.js} +8 -4
- package/dist/{triage-FCYHD2AQ.js → triage-IFCVL5MA.js} +7 -6
- package/dist/{verify-LC57A6H2.js → verify-HWZBTK5X.js} +15 -12
- package/package.json +1 -1
- package/dist/chunk-MRZVA5JB.js +0 -163
- package/dist/{chunk-EAF22QIG.js → chunk-JR2JLRE3.js} +3 -3
|
@@ -298,7 +298,16 @@ var LOCAL_ONLY = [
|
|
|
298
298
|
// The graph itself: rebuilt per checkout, inherited by copy, and unmergeable by construction — see above.
|
|
299
299
|
"graphify-out/graph.json",
|
|
300
300
|
// …and the commit it was built at, which describes that local copy and travels with it.
|
|
301
|
-
"graphify-out/.graph-commit.json"
|
|
301
|
+
"graphify-out/.graph-commit.json",
|
|
302
|
+
/**
|
|
303
|
+
* The AST cache — which this list's own heading has always described and never actually excluded.
|
|
304
|
+
*
|
|
305
|
+
* "an AST cache keyed by local mtimes" is the sentence written above these rules, and no rule matched it.
|
|
306
|
+
* Measured on a real project: 711 files and 7.8 MB sitting untracked and un-ignored, so `git add -A` after
|
|
307
|
+
* a `/graph build` swept the whole cache into the commit. Keyed by this checkout's mtimes, it is worthless
|
|
308
|
+
* to anyone else and rebuilt whenever it is missing.
|
|
309
|
+
*/
|
|
310
|
+
"graphify-out/cache/"
|
|
302
311
|
];
|
|
303
312
|
var NESTED_CHECKOUTS = [".claude/worktrees/", ".horsecode/worktrees/"];
|
|
304
313
|
function openDirectory(text, dir) {
|
package/dist/cli.js
CHANGED
|
@@ -15,31 +15,25 @@ import {
|
|
|
15
15
|
restoreTerminal,
|
|
16
16
|
runJob,
|
|
17
17
|
sttySane
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-2WXG35EM.js";
|
|
19
19
|
import "./chunk-M2RKCIGV.js";
|
|
20
20
|
import {
|
|
21
|
-
CODE_TEAM,
|
|
22
21
|
CliProvider,
|
|
23
|
-
DEFAULT_COUNCIL,
|
|
24
|
-
DEFAULT_PROMPTS,
|
|
25
|
-
DEFAULT_ROLE_SKILLS,
|
|
26
|
-
PLAN_TEAM,
|
|
27
|
-
REQUIRED_ROLES,
|
|
28
22
|
RoleFitness,
|
|
29
|
-
RoleRegistry,
|
|
30
23
|
SHORT_CALL_MS,
|
|
31
|
-
SPEC_TEAM,
|
|
32
24
|
WorktreeManager,
|
|
33
|
-
ZAI_MODELS,
|
|
34
25
|
buildCouncilRegistry,
|
|
35
26
|
buildTeamRegistry,
|
|
36
|
-
cliCatalog,
|
|
37
27
|
mainWorktreeRoot,
|
|
38
|
-
modelsFor,
|
|
39
28
|
toSlug
|
|
40
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-27F44PBD.js";
|
|
41
30
|
import "./chunk-QF4MP6BS.js";
|
|
42
|
-
import
|
|
31
|
+
import {
|
|
32
|
+
MAX_CHUNK_CHARS,
|
|
33
|
+
discover,
|
|
34
|
+
readFinding
|
|
35
|
+
} from "./chunk-QJYVZPLG.js";
|
|
36
|
+
import "./chunk-UTHLEW5V.js";
|
|
43
37
|
import "./chunk-ZSQ24YDJ.js";
|
|
44
38
|
import {
|
|
45
39
|
parseFrontmatter
|
|
@@ -48,58 +42,75 @@ import {
|
|
|
48
42
|
UNSET_MODEL,
|
|
49
43
|
loadConfig,
|
|
50
44
|
saveSkillSource
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import {
|
|
53
|
-
CLI_KINDS,
|
|
54
|
-
checkProfile,
|
|
55
|
-
runLogin
|
|
56
|
-
} from "./chunk-G45RWL7S.js";
|
|
45
|
+
} from "./chunk-JR2JLRE3.js";
|
|
57
46
|
import "./chunk-H2FDGPVW.js";
|
|
58
47
|
import {
|
|
48
|
+
CODE_TEAM,
|
|
49
|
+
DEFAULT_COUNCIL,
|
|
50
|
+
DEFAULT_PROMPTS,
|
|
51
|
+
DEFAULT_ROLE_SKILLS,
|
|
52
|
+
PLAN_TEAM,
|
|
53
|
+
REQUIRED_ROLES,
|
|
54
|
+
RoleRegistry,
|
|
55
|
+
SPEC_TEAM,
|
|
56
|
+
ZAI_MODELS,
|
|
59
57
|
buildBrief,
|
|
58
|
+
cliCatalog,
|
|
60
59
|
describePlan,
|
|
60
|
+
modelsFor,
|
|
61
61
|
planFor,
|
|
62
62
|
runTraces,
|
|
63
63
|
traceableFiles
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-YULQ4URQ.js";
|
|
65
|
+
import {
|
|
66
|
+
CLI_KINDS,
|
|
67
|
+
checkProfile,
|
|
68
|
+
runLogin
|
|
69
|
+
} from "./chunk-G45RWL7S.js";
|
|
65
70
|
import {
|
|
66
71
|
defaultGitRunner
|
|
67
72
|
} from "./chunk-LPQU436C.js";
|
|
68
73
|
import {
|
|
69
74
|
InjectionLog,
|
|
70
75
|
memoryNote
|
|
71
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-BFIZMM4G.js";
|
|
72
77
|
import "./chunk-63E73TGI.js";
|
|
78
|
+
import {
|
|
79
|
+
ToolRegistry
|
|
80
|
+
} from "./chunk-CYLPQWIF.js";
|
|
81
|
+
import {
|
|
82
|
+
briefStatus
|
|
83
|
+
} from "./chunk-6S4WWQMN.js";
|
|
73
84
|
import {
|
|
74
85
|
Telemetry,
|
|
75
|
-
ToolRegistry,
|
|
76
86
|
clearPerfMarks,
|
|
77
87
|
sampleMemory,
|
|
78
88
|
setTelemetry,
|
|
79
89
|
stripThinking,
|
|
80
90
|
telemetry,
|
|
81
91
|
writeHeapSnapshot
|
|
82
|
-
} from "./chunk-
|
|
83
|
-
import {
|
|
84
|
-
briefStatus
|
|
85
|
-
} from "./chunk-6OSEQOYY.js";
|
|
92
|
+
} from "./chunk-JLWQCA7B.js";
|
|
86
93
|
import {
|
|
87
94
|
discoverTraceRoot,
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
ensureGitignore,
|
|
96
|
+
localOnly,
|
|
97
|
+
setTraceRoot,
|
|
98
|
+
sharedDerived,
|
|
99
|
+
traceRootRel
|
|
100
|
+
} from "./chunk-ZPJP2VH5.js";
|
|
90
101
|
import {
|
|
91
102
|
buildProjectGraph,
|
|
92
103
|
graphStatus,
|
|
93
104
|
graphifyPython
|
|
94
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-4M6LXNG2.js";
|
|
95
106
|
import {
|
|
96
107
|
sessionBase
|
|
97
108
|
} from "./chunk-6W4UH2BQ.js";
|
|
98
109
|
|
|
99
110
|
// src/cli.ts
|
|
100
111
|
import { execFileSync } from "child_process";
|
|
101
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
102
|
-
import { join as
|
|
112
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, realpathSync, rmSync, statSync as statSync2 } from "fs";
|
|
113
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
103
114
|
import { pathToFileURL } from "url";
|
|
104
115
|
|
|
105
116
|
// src/agents/cli-accounts.ts
|
|
@@ -694,6 +705,137 @@ async function syncSkillSources(home, sources, deps = {}) {
|
|
|
694
705
|
return { ok, failed };
|
|
695
706
|
}
|
|
696
707
|
|
|
708
|
+
// src/engine/init-project.ts
|
|
709
|
+
import { existsSync as existsSync2, statSync, readdirSync } from "fs";
|
|
710
|
+
import { join as join7 } from "path";
|
|
711
|
+
|
|
712
|
+
// src/migrate/project-docs.ts
|
|
713
|
+
import { basename } from "path";
|
|
714
|
+
var DERIVED = [
|
|
715
|
+
{ re: /^\.horsecode\//, why: "horse-code's own state \u2014 traces describe the code, so rules mined from them would be the code talking back to itself" },
|
|
716
|
+
{ re: /^graphify-out\//, why: "generated by the graph build" },
|
|
717
|
+
{ re: /(^|\/)(node_modules|vendor|third_party|\.git)\//, why: "not this project's writing" },
|
|
718
|
+
{ re: /(^|\/)\.claude\/worktrees\//, why: "a nested checkout of this repository" }
|
|
719
|
+
];
|
|
720
|
+
var HISTORY = [
|
|
721
|
+
{ re: /(^|\/)archive(d)?\//i, why: "an archive of finished work" },
|
|
722
|
+
{ re: /\d{4}-\d{2}-\d{2}/, why: "dated \u2014 a record of a moment, not a standing rule" },
|
|
723
|
+
{ re: /(^|\/)(HANDOFF|RESUME|CHANGELOG|RELEASE[-_]NOTES)[^/]*$/i, why: "a record of what happened" },
|
|
724
|
+
{ re: /(^|\/)chats?\//i, why: "a pasted conversation" },
|
|
725
|
+
{ re: /(^|\/)chat\d*\.md$/i, why: "a pasted conversation" }
|
|
726
|
+
];
|
|
727
|
+
function sortProjectDocs(files, known = []) {
|
|
728
|
+
const seen = new Set(known.map((k) => k.replace(/\\/g, "/")));
|
|
729
|
+
const candidates2 = [];
|
|
730
|
+
const skipped = [];
|
|
731
|
+
for (const f of files) {
|
|
732
|
+
const path = f.path.replace(/\\/g, "/");
|
|
733
|
+
if (!/\.mdx?$/i.test(path)) continue;
|
|
734
|
+
if (seen.has(path) || seen.has(basename(path))) continue;
|
|
735
|
+
const why = DERIVED.find((d) => d.re.test(path))?.why ?? HISTORY.find((h) => h.re.test(path))?.why;
|
|
736
|
+
if (why) skipped.push({ path, bytes: f.bytes, skipped: why });
|
|
737
|
+
else candidates2.push({ path, bytes: f.bytes });
|
|
738
|
+
}
|
|
739
|
+
const bySize = (a, b) => b.bytes - a.bytes;
|
|
740
|
+
return { candidates: candidates2.sort(bySize), skipped: skipped.sort(bySize) };
|
|
741
|
+
}
|
|
742
|
+
function totalBytes(docs) {
|
|
743
|
+
return docs.reduce((n, d) => n + d.bytes, 0);
|
|
744
|
+
}
|
|
745
|
+
function classifyCalls(bytes, chunk) {
|
|
746
|
+
return Math.max(1, Math.ceil(bytes / chunk));
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/engine/init-project.ts
|
|
750
|
+
var UNCLAIMED_FILE_FLOOR = 50;
|
|
751
|
+
function countFiles(dir, cap = UNCLAIMED_FILE_FLOOR * 4) {
|
|
752
|
+
let n = 0;
|
|
753
|
+
const walk = (d) => {
|
|
754
|
+
if (n >= cap) return;
|
|
755
|
+
let entries;
|
|
756
|
+
try {
|
|
757
|
+
entries = readdirSync(d, { withFileTypes: true });
|
|
758
|
+
} catch {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
for (const e of entries) {
|
|
762
|
+
if (n >= cap) return;
|
|
763
|
+
if (e.isDirectory()) walk(join7(d, e.name));
|
|
764
|
+
else n++;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
walk(dir);
|
|
768
|
+
return n;
|
|
769
|
+
}
|
|
770
|
+
async function initProject(cwd, isIgnored, untracked, knowledge) {
|
|
771
|
+
const changed = await ensureGitignore(cwd);
|
|
772
|
+
const unclaimed = [];
|
|
773
|
+
for (const rel of untracked()) {
|
|
774
|
+
const path = join7(cwd, rel);
|
|
775
|
+
let dir = false;
|
|
776
|
+
try {
|
|
777
|
+
dir = statSync(path).isDirectory();
|
|
778
|
+
} catch {
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (!dir || isIgnored(rel)) continue;
|
|
782
|
+
const files = countFiles(path);
|
|
783
|
+
if (files >= UNCLAIMED_FILE_FLOOR) unclaimed.push({ path: rel, files });
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
changed,
|
|
787
|
+
kept: [`${traceRootRel().replace(/\\/g, "/")}/`, ...sharedDerived()].filter((p) => existsSync2(join7(cwd, p)) || p.endsWith("/")),
|
|
788
|
+
excluded: localOnly(),
|
|
789
|
+
unclaimed: unclaimed.sort((a, b) => b.files - a.files),
|
|
790
|
+
...knowledge ? { knowledge } : {}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function hasKnowledge(k) {
|
|
794
|
+
return !!k && (k.named.length > 0 || k.docs.length > 0);
|
|
795
|
+
}
|
|
796
|
+
function size(bytes) {
|
|
797
|
+
return bytes >= 1024 * 1024 ? `${(bytes / 1024 / 1024).toFixed(1)} MB` : `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
798
|
+
}
|
|
799
|
+
function describeKnowledge(k, chunk = MAX_CHUNK_CHARS) {
|
|
800
|
+
const lines = ["", "**This project already says things about itself.**", ""];
|
|
801
|
+
if (k.named.length) {
|
|
802
|
+
lines.push(`Files a tool is known to write \u2014 ${size(totalBytes(k.named.map((n) => ({ path: n.label, bytes: n.bytes }))))}:`);
|
|
803
|
+
lines.push(...k.named.map((n) => ` \xB7 \`${n.label}\` (${size(n.bytes)})`));
|
|
804
|
+
}
|
|
805
|
+
if (k.docs.length) {
|
|
806
|
+
const bytes = totalBytes(k.docs);
|
|
807
|
+
lines.push("", `The project's other markdown \u2014 ${k.docs.length} file(s), ${size(bytes)}, about ${classifyCalls(bytes, chunk)} classification call(s):`);
|
|
808
|
+
lines.push(...k.docs.slice(0, 8).map((d) => ` \xB7 \`${d.path}\` (${size(d.bytes)})`));
|
|
809
|
+
if (k.docs.length > 8) lines.push(` \xB7 \u2026and ${k.docs.length - 8} more`);
|
|
810
|
+
}
|
|
811
|
+
if (k.skipped.length) {
|
|
812
|
+
const reasons = /* @__PURE__ */ new Map();
|
|
813
|
+
for (const sk of k.skipped) reasons.set(sk.skipped ?? "", (reasons.get(sk.skipped ?? "") ?? 0) + 1);
|
|
814
|
+
lines.push("", `Set aside \u2014 ${k.skipped.length} file(s), ${size(totalBytes(k.skipped))}:`);
|
|
815
|
+
for (const [why, n] of [...reasons].sort((a, b) => b[1] - a[1])) lines.push(` \xB7 ${n} \xD7 ${why}`);
|
|
816
|
+
}
|
|
817
|
+
return lines.join("\n");
|
|
818
|
+
}
|
|
819
|
+
function describeInit(report) {
|
|
820
|
+
const lines = [
|
|
821
|
+
report.changed ? "**Project set up for horse-code** \u2014 `.gitignore` updated." : "**Project is already set up** \u2014 `.gitignore` already says everything needed.",
|
|
822
|
+
"",
|
|
823
|
+
"Committed on purpose, because a clone would otherwise pay to produce it again:",
|
|
824
|
+
...report.kept.map((p) => ` \xB7 \`${p}\``),
|
|
825
|
+
"",
|
|
826
|
+
"Kept out on purpose \u2014 derived from the source, keyed to this checkout, or unmergeable:",
|
|
827
|
+
...report.excluded.map((p) => ` \xB7 \`${p}\``)
|
|
828
|
+
];
|
|
829
|
+
if (report.unclaimed.length) {
|
|
830
|
+
lines.push(
|
|
831
|
+
"",
|
|
832
|
+
"Untracked and covered by no rule \u2014 `git add -A` would commit these. Yours to decide:",
|
|
833
|
+
...report.unclaimed.map((u) => ` \xB7 \`${u.path}\` (${u.files >= UNCLAIMED_FILE_FLOOR * 4 ? `${u.files}+` : u.files} files)`)
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
return lines.join("\n");
|
|
837
|
+
}
|
|
838
|
+
|
|
697
839
|
// src/permission/rules.ts
|
|
698
840
|
import picomatch from "picomatch";
|
|
699
841
|
var DANGEROUS_PATTERNS = [
|
|
@@ -964,8 +1106,8 @@ var ProposalQueue = class {
|
|
|
964
1106
|
};
|
|
965
1107
|
|
|
966
1108
|
// src/speckit/templates.ts
|
|
967
|
-
import { existsSync as
|
|
968
|
-
import { join as
|
|
1109
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1110
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
969
1111
|
var TEMPLATE_FILES = {
|
|
970
1112
|
spec: "spec-template.md",
|
|
971
1113
|
plan: "plan-template.md",
|
|
@@ -988,10 +1130,10 @@ async function loadSpecKit(opts) {
|
|
|
988
1130
|
);
|
|
989
1131
|
}
|
|
990
1132
|
const fetchFn = opts.fetch ?? globalThis.fetch;
|
|
991
|
-
const cacheDir =
|
|
1133
|
+
const cacheDir = join8(opts.home, ".horsecode", "spec-kit", opts.version, "templates");
|
|
992
1134
|
const get = async (relPath) => {
|
|
993
|
-
const cachePath =
|
|
994
|
-
if (
|
|
1135
|
+
const cachePath = join8(cacheDir, relPath);
|
|
1136
|
+
if (existsSync3(cachePath)) return readFileSync3(cachePath, "utf8");
|
|
995
1137
|
const url = `${RAW_BASE}/${opts.version}/templates/${relPath}`;
|
|
996
1138
|
const res = await fetchFn(url);
|
|
997
1139
|
if (!res.ok) {
|
|
@@ -1018,7 +1160,7 @@ Check your network or set specKit.version to a valid tag.`
|
|
|
1018
1160
|
|
|
1019
1161
|
// src/wiring.ts
|
|
1020
1162
|
import { homedir } from "os";
|
|
1021
|
-
import { join as
|
|
1163
|
+
import { join as join9 } from "path";
|
|
1022
1164
|
async function buildJobDeps(opts) {
|
|
1023
1165
|
const { config } = opts;
|
|
1024
1166
|
const roles = {};
|
|
@@ -1064,7 +1206,7 @@ async function buildJobDeps(opts) {
|
|
|
1064
1206
|
const queue = new ProposalQueue();
|
|
1065
1207
|
let kitPromise;
|
|
1066
1208
|
const specKit = () => kitPromise ??= loadSpecKit({ version: config.specKit.version, home: opts.home, fetch: opts.fetch });
|
|
1067
|
-
const fitness = new RoleFitness(
|
|
1209
|
+
const fitness = new RoleFitness(join9(opts.home ?? homedir(), ".horsecode", "model-fitness.json"));
|
|
1068
1210
|
roleRegistry.setFitness(fitness);
|
|
1069
1211
|
return {
|
|
1070
1212
|
provider: opts.provider,
|
|
@@ -1257,9 +1399,9 @@ function redactRecord(record) {
|
|
|
1257
1399
|
|
|
1258
1400
|
// src/obs/sink.ts
|
|
1259
1401
|
import { createWriteStream, mkdirSync as mkdirSync4 } from "fs";
|
|
1260
|
-
import { join as
|
|
1402
|
+
import { join as join10 } from "path";
|
|
1261
1403
|
function telemetryDir(home) {
|
|
1262
|
-
return
|
|
1404
|
+
return join10(home, ".horsecode", "telemetry");
|
|
1263
1405
|
}
|
|
1264
1406
|
var MAX_QUEUE = 1e4;
|
|
1265
1407
|
var FileSink = class {
|
|
@@ -1268,7 +1410,7 @@ var FileSink = class {
|
|
|
1268
1410
|
dropped = 0;
|
|
1269
1411
|
path;
|
|
1270
1412
|
constructor(home, runId) {
|
|
1271
|
-
this.path =
|
|
1413
|
+
this.path = join10(telemetryDir(home), `${runId}.jsonl`);
|
|
1272
1414
|
try {
|
|
1273
1415
|
mkdirSync4(telemetryDir(home), { recursive: true });
|
|
1274
1416
|
this.stream = createWriteStream(this.path, { flags: "a" });
|
|
@@ -1328,12 +1470,12 @@ var FileSink = class {
|
|
|
1328
1470
|
|
|
1329
1471
|
// src/cli-info.ts
|
|
1330
1472
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1331
|
-
import { dirname as dirname4, join as
|
|
1473
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
1332
1474
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1333
1475
|
function hcodeVersion() {
|
|
1334
1476
|
try {
|
|
1335
1477
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
1336
|
-
const pkg = JSON.parse(readFileSync4(
|
|
1478
|
+
const pkg = JSON.parse(readFileSync4(join11(here, "..", "package.json"), "utf8"));
|
|
1337
1479
|
return pkg.version ?? "unknown";
|
|
1338
1480
|
} catch {
|
|
1339
1481
|
return "unknown";
|
|
@@ -1719,14 +1861,43 @@ async function main(argv) {
|
|
|
1719
1861
|
);
|
|
1720
1862
|
const notSignedIn = ambientLogins.filter((a) => !a.status.loggedIn).map((a) => a.kind);
|
|
1721
1863
|
const accountsNote = () => accountsLine(accounts.usage(), notSignedIn);
|
|
1864
|
+
const initProjectFn = async () => {
|
|
1865
|
+
const r = await defaultGitRunner(["ls-files", "--others", "--exclude-standard", "--directory"], cwd);
|
|
1866
|
+
const list = r.stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1867
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
1868
|
+
for (const path of list) {
|
|
1869
|
+
if ((await defaultGitRunner(["check-ignore", "-q", path], cwd)).code === 0) ignored.add(path);
|
|
1870
|
+
}
|
|
1871
|
+
const all = await defaultGitRunner(["ls-files", "--cached", "--others", "--exclude-standard"], cwd);
|
|
1872
|
+
const md = all.stdout.split("\n").map((l) => l.trim()).filter((l) => /\.mdx?$/i.test(l));
|
|
1873
|
+
const withSize = md.map((path) => {
|
|
1874
|
+
try {
|
|
1875
|
+
return { path, bytes: statSync2(join12(cwd, path)).size };
|
|
1876
|
+
} catch {
|
|
1877
|
+
return { path, bytes: 0 };
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1880
|
+
const found = await discover({ cwd, home });
|
|
1881
|
+
const named = found.filter((f) => f.kind === "rules" || f.kind === "mcp").map((f) => ({ label: f.label, bytes: f.bytes }));
|
|
1882
|
+
const { candidates: candidates2, skipped } = sortProjectDocs(withSize, found.map((f) => f.label));
|
|
1883
|
+
const knowledge = { named, docs: candidates2, skipped };
|
|
1884
|
+
const report = await initProject(cwd, (path) => ignored.has(path), () => list, knowledge);
|
|
1885
|
+
const canImport = hasKnowledge(knowledge);
|
|
1886
|
+
const extra = [];
|
|
1887
|
+
for (const d of candidates2) {
|
|
1888
|
+
const r2 = await readFinding(join12(cwd, d.path));
|
|
1889
|
+
if (r2?.text) extra.push({ kind: "rules", tool: "project document", path: join12(cwd, d.path), label: d.path, bytes: r2.bytes, own: true, text: r2.text });
|
|
1890
|
+
}
|
|
1891
|
+
return { text: describeInit(report) + (canImport ? describeKnowledge(knowledge) : ""), canImport, extra };
|
|
1892
|
+
};
|
|
1722
1893
|
const modelsPanelNote = (inUse) => modelsPanel(accounts.usage(), modelsFor, CLI_KINDS, inUse);
|
|
1723
1894
|
const raw = new CliProvider({ readOnly: false, accounts });
|
|
1724
1895
|
const provider = config.telemetry ? telemetryProvider(raw, telemetry()) : raw;
|
|
1725
1896
|
const skillRegistry = new SkillRegistry();
|
|
1726
1897
|
await registerBuiltinSkills(skillRegistry);
|
|
1727
1898
|
await skillRegistry.loadFromDir(externalSkillsDir(home));
|
|
1728
|
-
const skillsDir =
|
|
1729
|
-
if (
|
|
1899
|
+
const skillsDir = join12(cwd, ".horsecode", "skills");
|
|
1900
|
+
if (existsSync4(skillsDir)) await skillRegistry.loadFromDir(skillsDir);
|
|
1730
1901
|
const worktreeHome = await mainWorktreeRoot(defaultGitRunner, cwd);
|
|
1731
1902
|
if (worktreeHome !== cwd) {
|
|
1732
1903
|
console.log(`\u2139\uFE0F Sessions will be opened in ${worktreeHome} \u2014 the repository's main checkout.`);
|
|
@@ -1758,7 +1929,7 @@ async function main(argv) {
|
|
|
1758
1929
|
const useTui = shouldUseTui(!!process.stdin.isTTY, !!process.stdout.isTTY, !!args.noTui);
|
|
1759
1930
|
if (!args.prompt) {
|
|
1760
1931
|
if (useTui) {
|
|
1761
|
-
const { runTuiRepl } = await import("./app-
|
|
1932
|
+
const { runTuiRepl } = await import("./app-4WN37LZ3.js");
|
|
1762
1933
|
const { fetchCatalog, makeProbe, discoverSources } = await import("./discover-G2Z6XC3O.js");
|
|
1763
1934
|
const { loadSourceCache, saveSourceCache } = await import("./source-cache-XEK5WN7I.js");
|
|
1764
1935
|
const manualSources = config.modelSources.length > 0;
|
|
@@ -1809,8 +1980,8 @@ _Expected something like \`https://github.com/owner/repo\` or a link to the dire
|
|
|
1809
1980
|
_Discoverable by every agent. \`/roles adjust\` assigns it to the roles it fits; \`/skills update\` re-installs it from upstream._`;
|
|
1810
1981
|
};
|
|
1811
1982
|
const reloadProjectSkills = async () => {
|
|
1812
|
-
const dir =
|
|
1813
|
-
if (
|
|
1983
|
+
const dir = join12(cwd, ".horsecode", "skills");
|
|
1984
|
+
if (existsSync4(dir)) await skillRegistry.loadFromDir(dir);
|
|
1814
1985
|
};
|
|
1815
1986
|
const updateSkills = async () => {
|
|
1816
1987
|
if (!config.skillSources.length) {
|
|
@@ -1829,7 +2000,7 @@ _Discoverable by every agent. \`/roles adjust\` assigns it to the roles it fits;
|
|
|
1829
2000
|
${lines.join("\n")}${tail}`;
|
|
1830
2001
|
};
|
|
1831
2002
|
const graphStatusText = async () => {
|
|
1832
|
-
const st = await graphStatus(
|
|
2003
|
+
const st = await graphStatus((await derivedWorkdir(false)).dir);
|
|
1833
2004
|
if (!st.built) {
|
|
1834
2005
|
const py = await graphifyPython();
|
|
1835
2006
|
return py ? "No code graph yet. `/graph build` \u2014 AST parsing only, no tokens, a few seconds.\n\n_Without it every agent works blind: it can grep for a name but cannot tell what calls it._" : "No code graph, and graphify is not installed.\n\n`uv tool install graphifyy` (or `pipx install graphifyy`), then `/graph build`.\n\n_MIT, pure tree-sitter AST parsing \u2014 no API key, no tokens._";
|
|
@@ -1848,7 +2019,7 @@ ${lines.join("\n")}${tail}`;
|
|
|
1848
2019
|
|
|
1849
2020
|
_Every agent can query it: \`graph_impact\` (blast radius), \`graph_trace\`, \`graph_find\`, \`graph_context\`, \`graph_overview\`._`;
|
|
1850
2021
|
};
|
|
1851
|
-
const buildGraphText = async () => (await buildProjectGraph(
|
|
2022
|
+
const buildGraphText = async () => (await buildProjectGraph((await derivedWorkdir(true)).dir)).message;
|
|
1852
2023
|
const cleanWorktreesText = async (apply, branch) => {
|
|
1853
2024
|
const { surveySessions, cleanSessions, describeSurvey, describeClean } = await import("./clean-YOQATBMZ.js");
|
|
1854
2025
|
const target = branch?.trim() || fromBranch;
|
|
@@ -1858,31 +2029,42 @@ _Every agent can query it: \`graph_impact\` (blast radius), \`graph_trace\`, \`g
|
|
|
1858
2029
|
return describeClean(await cleanSessions(defaultGitRunner, cwd, target), target);
|
|
1859
2030
|
};
|
|
1860
2031
|
const gitFiles = async () => (await defaultGitRunner(["ls-files", "--cached", "--others", "--exclude-standard"], cwd)).stdout.split("\n").filter(Boolean);
|
|
1861
|
-
const traceableDocs = async () => traceableFiles(
|
|
1862
|
-
const traceableFiles2 = async () => traceableFiles(
|
|
1863
|
-
const
|
|
2032
|
+
const traceableDocs = async (dir = cwd) => traceableFiles(dir, { code: false });
|
|
2033
|
+
const traceableFiles2 = async (dir = cwd) => traceableFiles(dir);
|
|
2034
|
+
const tracerChain = () => {
|
|
1864
2035
|
for (const role of ["tracer", "architect", "senior-coder", "judge"]) {
|
|
1865
|
-
const
|
|
1866
|
-
if (
|
|
2036
|
+
const chain = (config.roles[role]?.models ?? []).filter((m) => m && m !== "default");
|
|
2037
|
+
if (chain.length) return chain;
|
|
1867
2038
|
}
|
|
1868
|
-
return config.model;
|
|
2039
|
+
return [config.model];
|
|
2040
|
+
};
|
|
2041
|
+
const derivedWorkdir = async (create) => {
|
|
2042
|
+
const inSession = sessionBase(cwd);
|
|
2043
|
+
if (inSession) return { dir: inSession };
|
|
2044
|
+
const standing = join12(cwd, ".horsecode", "worktrees", "traces", "base");
|
|
2045
|
+
if (!create) return { dir: existsSync4(standing) ? standing : cwd };
|
|
2046
|
+
const session = await manager.openFixed(fromBranch, "traces");
|
|
2047
|
+
return { dir: session.baseWorktree, session };
|
|
1869
2048
|
};
|
|
1870
2049
|
const planTracesFn = async () => {
|
|
1871
|
-
const
|
|
1872
|
-
|
|
2050
|
+
const { dir } = await derivedWorkdir(false);
|
|
2051
|
+
const plan = await planFor(dir, await traceableFiles2(dir));
|
|
2052
|
+
return { summary: describePlan(plan, tracerChain()), jobs: plan.jobs.length };
|
|
1873
2053
|
};
|
|
1874
2054
|
const runTracesFn = async (onProgress, metered) => {
|
|
1875
|
-
const
|
|
1876
|
-
const
|
|
1877
|
-
const
|
|
2055
|
+
const { dir, session } = await derivedWorkdir(true);
|
|
2056
|
+
const files = await traceableFiles2(dir);
|
|
2057
|
+
const brief = await buildBrief({ cwd: dir, provider: metered ?? provider, models: tracerChain(), files: await traceableDocs(dir) });
|
|
2058
|
+
const plan = await planFor(dir, files);
|
|
1878
2059
|
const res = await runTraces({
|
|
1879
|
-
cwd,
|
|
2060
|
+
cwd: dir,
|
|
1880
2061
|
provider: metered ?? provider,
|
|
1881
|
-
|
|
2062
|
+
models: tracerChain(),
|
|
1882
2063
|
plan,
|
|
1883
2064
|
liveFiles: new Set(files),
|
|
1884
2065
|
...onProgress ? { onProgress } : {}
|
|
1885
2066
|
});
|
|
2067
|
+
if (session) await manager.preserveSession(session, `docs(traces): ${res.written} file(s) described`);
|
|
1886
2068
|
const bits = [`${brief.message}
|
|
1887
2069
|
|
|
1888
2070
|
**Traces written: ${res.written}**`];
|
|
@@ -1890,9 +2072,13 @@ _Every agent can query it: \`graph_impact\` (blast radius), \`graph_trace\`, \`g
|
|
|
1890
2072
|
if (res.pruned.length) bits.push(`${res.pruned.length} removed for deleted files`);
|
|
1891
2073
|
if (res.wroteGitignore) bits.push("\n\n_Added .gitignore rules: traces are committed, the AST cache is not._");
|
|
1892
2074
|
const failures = describeTraceFailures(res.failed);
|
|
2075
|
+
const landed = session ? `
|
|
2076
|
+
|
|
2077
|
+
_Written and committed on \`${session.baseBranch}\`, so your working tree is untouched._
|
|
2078
|
+
_Bring them in with \`git merge ${session.baseBranch}\`, or browse them at \`${session.baseWorktree}\`._` : "\n\n_Written in this session's worktree, on its own branch._";
|
|
1893
2079
|
return `${bits.join(" \xB7 ")}${failures ? `
|
|
1894
2080
|
|
|
1895
|
-
${failures}` : ""}
|
|
2081
|
+
${failures}` : ""}${landed}
|
|
1896
2082
|
|
|
1897
2083
|
_Committed with the repo, so every clone starts with them. Agents read one with \`graph_trace\`._`;
|
|
1898
2084
|
};
|
|
@@ -1901,11 +2087,13 @@ _Committed with the repo, so every clone starts with them. Agents read one with
|
|
|
1901
2087
|
memStore,
|
|
1902
2088
|
accountsNote,
|
|
1903
2089
|
modelsPanel: modelsPanelNote,
|
|
2090
|
+
initProject: initProjectFn,
|
|
1904
2091
|
listSkills,
|
|
1905
2092
|
updateSkills,
|
|
1906
2093
|
addSkill,
|
|
1907
2094
|
reloadProjectSkills,
|
|
1908
2095
|
graphStatus: graphStatusText,
|
|
2096
|
+
derivedDir: async () => (await derivedWorkdir(true)).dir,
|
|
1909
2097
|
buildGraph: buildGraphText,
|
|
1910
2098
|
cleanWorktrees: cleanWorktreesText,
|
|
1911
2099
|
planTraces: planTracesFn,
|
|
@@ -1938,7 +2126,7 @@ _Committed with the repo, so every clone starts with them. Agents read one with
|
|
|
1938
2126
|
...args.revisionRounds !== void 0 && { revisionRounds: args.revisionRounds }
|
|
1939
2127
|
};
|
|
1940
2128
|
if (useTui) {
|
|
1941
|
-
const { runTui } = await import("./app-
|
|
2129
|
+
const { runTui } = await import("./app-4WN37LZ3.js");
|
|
1942
2130
|
const res = await runTui({ buildDeps, job });
|
|
1943
2131
|
console.log(renderResult(res));
|
|
1944
2132
|
return;
|
|
@@ -8,18 +8,19 @@ import {
|
|
|
8
8
|
dirtyPaths,
|
|
9
9
|
runFix,
|
|
10
10
|
runSmallChange
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-3ACDNDCG.js";
|
|
12
|
+
import "./chunk-27F44PBD.js";
|
|
13
13
|
import "./chunk-ZSQ24YDJ.js";
|
|
14
|
+
import "./chunk-YULQ4URQ.js";
|
|
14
15
|
import "./chunk-G45RWL7S.js";
|
|
15
|
-
import "./chunk-MRZVA5JB.js";
|
|
16
16
|
import "./chunk-LPQU436C.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-BFIZMM4G.js";
|
|
18
18
|
import "./chunk-63E73TGI.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-CYLPQWIF.js";
|
|
20
|
+
import "./chunk-6S4WWQMN.js";
|
|
21
|
+
import "./chunk-JLWQCA7B.js";
|
|
22
|
+
import "./chunk-ZPJP2VH5.js";
|
|
23
|
+
import "./chunk-4M6LXNG2.js";
|
|
23
24
|
import "./chunk-6W4UH2BQ.js";
|
|
24
25
|
export {
|
|
25
26
|
FIX_ATTEMPTS,
|
|
@@ -3,12 +3,13 @@ import {
|
|
|
3
3
|
} from "./chunk-YBWTCXUS.js";
|
|
4
4
|
import {
|
|
5
5
|
askInUserLanguage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UTHLEW5V.js";
|
|
7
7
|
import {
|
|
8
8
|
checkpointMtime,
|
|
9
9
|
readCheckpoint
|
|
10
10
|
} from "./chunk-ZSQ24YDJ.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-CYLPQWIF.js";
|
|
12
|
+
import "./chunk-JLWQCA7B.js";
|
|
12
13
|
import "./chunk-6W4UH2BQ.js";
|
|
13
14
|
|
|
14
15
|
// src/engine/ongoing.ts
|