@hawkeyexl/manni 4.5.0 → 4.7.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/README.md +1 -1
- package/dist/batch-U6S2JUUN.js +41 -0
- package/dist/build-4RN6WBCP.js +32 -0
- package/dist/calibrate-6VIZYR4L.js +36 -0
- package/dist/capture-DP3NXAZE.js +34 -0
- package/dist/{check-RQIIIURI.js → check-ERVNGKLC.js} +13 -14
- package/dist/check-IWHUPAZM.js +886 -0
- package/dist/check-IWHUPAZM.js.map +1 -0
- package/dist/{check-ESNS7ZBC.js → check-OAJWC3WD.js} +7 -5
- package/dist/check-STHXZIZO.js +26 -0
- package/dist/{chunk-TA42STP7.js → chunk-2ZWTSRE5.js} +4 -4
- package/dist/{chunk-F2OVHJNC.js → chunk-4Z2MN5FR.js} +11 -11
- package/dist/{chunk-HVFR66GQ.js → chunk-5UB47BEV.js} +3280 -283
- package/dist/chunk-5UB47BEV.js.map +1 -0
- package/dist/{chunk-TCC737IS.js → chunk-5VWDIWOH.js} +4 -4
- package/dist/{chunk-B7XUEQF3.js → chunk-6KKZXB2O.js} +7 -5
- package/dist/{chunk-B7XUEQF3.js.map → chunk-6KKZXB2O.js.map} +1 -1
- package/dist/{chunk-ELZ2IIOF.js → chunk-6URSCTTX.js} +2 -2
- package/dist/{chunk-K3U5F2QT.js → chunk-6VDBP5UV.js} +12 -12
- package/dist/chunk-7EL5LLKD.js +233 -0
- package/dist/chunk-7EL5LLKD.js.map +1 -0
- package/dist/{chunk-C3KI45RF.js → chunk-7XZ6INKO.js} +4 -4
- package/dist/{chunk-ZX43SRZR.js → chunk-AMEILOVV.js} +8 -6
- package/dist/{chunk-ZX43SRZR.js.map → chunk-AMEILOVV.js.map} +1 -1
- package/dist/{chunk-SNEHX4VE.js → chunk-B5CMM57M.js} +2 -2
- package/dist/chunk-BWCWTHYS.js +146 -0
- package/dist/chunk-BWCWTHYS.js.map +1 -0
- package/dist/{chunk-QLUMKWQQ.js → chunk-C2TJG5KV.js} +10 -6
- package/dist/{chunk-QLUMKWQQ.js.map → chunk-C2TJG5KV.js.map} +1 -1
- package/dist/{chunk-GAZFKZHM.js → chunk-CEVU3GFF.js} +6 -6
- package/dist/chunk-CI55ABCK.js +502 -0
- package/dist/chunk-CI55ABCK.js.map +1 -0
- package/dist/{chunk-QCBICNKC.js → chunk-CR3HZERW.js} +202 -54
- package/dist/chunk-CR3HZERW.js.map +1 -0
- package/dist/chunk-CU57HT2E.js +116 -0
- package/dist/chunk-CU57HT2E.js.map +1 -0
- package/dist/chunk-CWXZ2MRT.js +222 -0
- package/dist/chunk-CWXZ2MRT.js.map +1 -0
- package/dist/chunk-D2LBN44V.js +134 -0
- package/dist/chunk-D2LBN44V.js.map +1 -0
- package/dist/chunk-D6RO54QD.js +674 -0
- package/dist/chunk-D6RO54QD.js.map +1 -0
- package/dist/{chunk-52PDIZKV.js → chunk-ECVQNGTH.js} +3 -3
- package/dist/chunk-FQGLRJGX.js +174 -0
- package/dist/chunk-FQGLRJGX.js.map +1 -0
- package/dist/{chunk-NGK3N6F2.js → chunk-GCC52YZD.js} +3 -3
- package/dist/chunk-HX44GB4Z.js +30 -0
- package/dist/chunk-HX44GB4Z.js.map +1 -0
- package/dist/{chunk-NN4ASVTY.js → chunk-IIFADWWI.js} +4 -4
- package/dist/{chunk-OIZ3XMKB.js → chunk-IOL74S4J.js} +4 -4
- package/dist/{chunk-UK2I3ACY.js → chunk-IZBBXKMJ.js} +4 -4
- package/dist/{chunk-YA63PVWI.js → chunk-K52CS4KT.js} +12 -10
- package/dist/{chunk-YA63PVWI.js.map → chunk-K52CS4KT.js.map} +1 -1
- package/dist/{chunk-7OQAU4EA.js → chunk-LDKIIZDW.js} +16 -12
- package/dist/{chunk-7OQAU4EA.js.map → chunk-LDKIIZDW.js.map} +1 -1
- package/dist/{chunk-QY3DOZMS.js → chunk-LONRQBGV.js} +4 -4
- package/dist/{chunk-EL2BZTDL.js → chunk-LXJEAV2V.js} +13 -11
- package/dist/{chunk-EL2BZTDL.js.map → chunk-LXJEAV2V.js.map} +1 -1
- package/dist/{chunk-XWFNO5PO.js → chunk-MIBWI5H7.js} +3 -3
- package/dist/{chunk-527HHQPS.js → chunk-MSMB6XOS.js} +6 -4
- package/dist/{chunk-527HHQPS.js.map → chunk-MSMB6XOS.js.map} +1 -1
- package/dist/{chunk-C6HZ6C5T.js → chunk-NIFEEIGQ.js} +2 -2
- package/dist/{chunk-JQU7FHYH.js → chunk-NZ6TNSCU.js} +10 -10
- package/dist/{chunk-3R5Y3XOL.js → chunk-OOEMKOX6.js} +29 -591
- package/dist/chunk-OOEMKOX6.js.map +1 -0
- package/dist/{chunk-MZC62HPZ.js → chunk-PAOVVHQX.js} +2 -2
- package/dist/chunk-PNX43DYW.js +849 -0
- package/dist/chunk-PNX43DYW.js.map +1 -0
- package/dist/chunk-QCHVR4ZM.js +286 -0
- package/dist/chunk-QCHVR4ZM.js.map +1 -0
- package/dist/{chunk-SJO2FTPG.js → chunk-QP5X66TO.js} +73 -1304
- package/dist/chunk-QP5X66TO.js.map +1 -0
- package/dist/{chunk-HQFVJDEN.js → chunk-QQNPVW3J.js} +19 -19
- package/dist/chunk-TPVOOTMN.js +532 -0
- package/dist/chunk-TPVOOTMN.js.map +1 -0
- package/dist/{chunk-IA7BMIPE.js → chunk-UZKQSWHJ.js} +2 -2
- package/dist/chunk-WQWKYAAK.js +86 -0
- package/dist/chunk-WQWKYAAK.js.map +1 -0
- package/dist/{chunk-CP4ZM43V.js → chunk-WTEILBKC.js} +2 -2
- package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
- package/dist/chunk-XG6PVB5S.js.map +1 -0
- package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
- package/dist/chunk-XKBEHKWO.js.map +1 -0
- package/dist/{chunk-BOIF2XGG.js → chunk-XXDXIIQ4.js} +2 -2
- package/dist/{chunk-6XTQDFGP.js → chunk-YSCZL4U5.js} +3 -3
- package/dist/{chunk-6XTQDFGP.js.map → chunk-YSCZL4U5.js.map} +1 -1
- package/dist/{chunk-7ZUCEMCY.js → chunk-ZEXGGGLW.js} +9 -11
- package/dist/{chunk-7ZUCEMCY.js.map → chunk-ZEXGGGLW.js.map} +1 -1
- package/dist/{chunk-DX32P2DT.js → chunk-ZKAKA54B.js} +2 -2
- package/dist/{chunk-TVIX653Q.js → chunk-ZKCNPKUO.js} +4 -4
- package/dist/{chunk-DE7JSWXV.js → chunk-ZNE25EAR.js} +8 -6
- package/dist/{chunk-DE7JSWXV.js.map → chunk-ZNE25EAR.js.map} +1 -1
- package/dist/{chunk-FLF6AECQ.js → chunk-ZPP75QWG.js} +17 -15
- package/dist/{chunk-FLF6AECQ.js.map → chunk-ZPP75QWG.js.map} +1 -1
- package/dist/{cli-PRZHQ3QB.js → cli-37SRAYNE.js} +14 -15
- package/dist/{cli-PRZHQ3QB.js.map → cli-37SRAYNE.js.map} +1 -1
- package/dist/{cli-TYGOHJOJ.js → cli-HXWYUKGJ.js} +225 -58
- package/dist/cli-HXWYUKGJ.js.map +1 -0
- package/dist/{cli-HUIMNJT6.js → cli-IRHPPUFJ.js} +19 -20
- package/dist/{cli-HUIMNJT6.js.map → cli-IRHPPUFJ.js.map} +1 -1
- package/dist/{cli-6PGVHP7Y.js → cli-KIID535Y.js} +17 -19
- package/dist/{cli-6PGVHP7Y.js.map → cli-KIID535Y.js.map} +1 -1
- package/dist/{cli-AAG3N4NB.js → cli-MTDGQIIV.js} +17 -18
- package/dist/{cli-AAG3N4NB.js.map → cli-MTDGQIIV.js.map} +1 -1
- package/dist/{cli-E3H6ZP4I.js → cli-NM6FZ54R.js} +5 -3
- package/dist/{cli-E3H6ZP4I.js.map → cli-NM6FZ54R.js.map} +1 -1
- package/dist/{cli-MHTYPVOD.js → cli-XIODOEUG.js} +113 -20
- package/dist/{cli-MHTYPVOD.js.map → cli-XIODOEUG.js.map} +1 -1
- package/dist/{cli-YW6VWBHX.js → cli-YL3IDR52.js} +10 -10
- package/dist/{cli-QKIJOXGW.js → cli-ZQUJYQBA.js} +17 -17
- package/dist/cli.js +11 -11
- package/dist/config-O6UBGEDK.js +26 -0
- package/dist/conformance-ALLPNGEQ.js +18 -0
- package/dist/docmeta.js +9 -9
- package/dist/github-UCYLM3DR.js +26 -0
- package/dist/github-VEJFO6JJ.js +31 -0
- package/dist/host-6GURH5HE.js +23 -0
- package/dist/index.d.ts +72 -4
- package/dist/index.js +176 -166
- package/dist/index.js.map +1 -1
- package/dist/{json-4DCKDGUV.js → json-6F52FSXZ.js} +12 -13
- package/dist/lint-X2GNINK6.js +29 -0
- package/dist/list-DP3O3CWB.js +30 -0
- package/dist/list-P77RZFZF.js +28 -0
- package/dist/{page-QEDB7EVU.js → page-WQCMBNRD.js} +11 -12
- package/dist/prepare-52QOK7M4.js +155 -0
- package/dist/prepare-52QOK7M4.js.map +1 -0
- package/dist/{pretty-SQX33UM2.js → pretty-5MMMNSWF.js} +13 -14
- package/dist/{pretty-5DGEBZSV.js → pretty-H4DQREMW.js} +12 -13
- package/dist/provider-AMGRDY6V.js +34 -0
- package/dist/release-CTYRHZL4.js +36 -0
- package/dist/release-CTYRHZL4.js.map +1 -0
- package/dist/{reporters-YELDDYJD.js → reporters-SU3VUKKX.js} +7 -5
- package/dist/{reporters-OHSI3RMV.js → reporters-WIHBEMPG.js} +11 -12
- package/dist/reporters-WIHBEMPG.js.map +1 -0
- package/dist/run-L2DWJ26K.js +32 -0
- package/dist/run-L2DWJ26K.js.map +1 -0
- package/dist/{term-2WMDZ4PY.js → term-PJIJQZNF.js} +16 -17
- package/dist/{term-2WMDZ4PY.js.map → term-PJIJQZNF.js.map} +1 -1
- package/dist/validate-XCDSPURG.js +17 -0
- package/dist/validate-XCDSPURG.js.map +1 -0
- package/package.json +2 -2
- package/plugin/manni/.claude-plugin/plugin.json +1 -1
- package/plugin/manni/hooks/hooks.json +47 -0
- package/dist/batch-GOE3UXOD.js +0 -36
- package/dist/build-M7BMEHL2.js +0 -33
- package/dist/calibrate-6RRAILUY.js +0 -31
- package/dist/capture-RJPQ6B4N.js +0 -32
- package/dist/check-BQD64X3N.js +0 -27
- package/dist/chunk-3R5Y3XOL.js.map +0 -1
- package/dist/chunk-3YHAYXBC.js +0 -164
- package/dist/chunk-3YHAYXBC.js.map +0 -1
- package/dist/chunk-HVFR66GQ.js.map +0 -1
- package/dist/chunk-LJ2RDH3F.js.map +0 -1
- package/dist/chunk-NAZRLJAK.js +0 -3090
- package/dist/chunk-NAZRLJAK.js.map +0 -1
- package/dist/chunk-Q35YIDBV.js.map +0 -1
- package/dist/chunk-QCBICNKC.js.map +0 -1
- package/dist/chunk-SJO2FTPG.js.map +0 -1
- package/dist/chunk-TU2WUBVZ.js +0 -2
- package/dist/cli-TYGOHJOJ.js.map +0 -1
- package/dist/github-3WMSTNTK.js +0 -32
- package/dist/github-OXSJQ4PS.js +0 -27
- package/dist/lint-YDHVVZHT.js +0 -30
- package/dist/list-3ZO2NIYO.js +0 -31
- package/dist/list-EJH6VAOW.js +0 -29
- package/dist/run-IP6NZHAG.js +0 -33
- package/dist/validate-G6HH3YXL.js +0 -16
- /package/dist/{batch-GOE3UXOD.js.map → batch-U6S2JUUN.js.map} +0 -0
- /package/dist/{build-M7BMEHL2.js.map → build-4RN6WBCP.js.map} +0 -0
- /package/dist/{calibrate-6RRAILUY.js.map → calibrate-6VIZYR4L.js.map} +0 -0
- /package/dist/{capture-RJPQ6B4N.js.map → capture-DP3NXAZE.js.map} +0 -0
- /package/dist/{check-BQD64X3N.js.map → check-ERVNGKLC.js.map} +0 -0
- /package/dist/{check-ESNS7ZBC.js.map → check-OAJWC3WD.js.map} +0 -0
- /package/dist/{check-RQIIIURI.js.map → check-STHXZIZO.js.map} +0 -0
- /package/dist/{chunk-TA42STP7.js.map → chunk-2ZWTSRE5.js.map} +0 -0
- /package/dist/{chunk-F2OVHJNC.js.map → chunk-4Z2MN5FR.js.map} +0 -0
- /package/dist/{chunk-TCC737IS.js.map → chunk-5VWDIWOH.js.map} +0 -0
- /package/dist/{chunk-ELZ2IIOF.js.map → chunk-6URSCTTX.js.map} +0 -0
- /package/dist/{chunk-K3U5F2QT.js.map → chunk-6VDBP5UV.js.map} +0 -0
- /package/dist/{chunk-C3KI45RF.js.map → chunk-7XZ6INKO.js.map} +0 -0
- /package/dist/{chunk-SNEHX4VE.js.map → chunk-B5CMM57M.js.map} +0 -0
- /package/dist/{chunk-GAZFKZHM.js.map → chunk-CEVU3GFF.js.map} +0 -0
- /package/dist/{chunk-52PDIZKV.js.map → chunk-ECVQNGTH.js.map} +0 -0
- /package/dist/{chunk-NGK3N6F2.js.map → chunk-GCC52YZD.js.map} +0 -0
- /package/dist/{chunk-NN4ASVTY.js.map → chunk-IIFADWWI.js.map} +0 -0
- /package/dist/{chunk-OIZ3XMKB.js.map → chunk-IOL74S4J.js.map} +0 -0
- /package/dist/{chunk-UK2I3ACY.js.map → chunk-IZBBXKMJ.js.map} +0 -0
- /package/dist/{chunk-QY3DOZMS.js.map → chunk-LONRQBGV.js.map} +0 -0
- /package/dist/{chunk-XWFNO5PO.js.map → chunk-MIBWI5H7.js.map} +0 -0
- /package/dist/{chunk-C6HZ6C5T.js.map → chunk-NIFEEIGQ.js.map} +0 -0
- /package/dist/{chunk-JQU7FHYH.js.map → chunk-NZ6TNSCU.js.map} +0 -0
- /package/dist/{chunk-MZC62HPZ.js.map → chunk-PAOVVHQX.js.map} +0 -0
- /package/dist/{chunk-HQFVJDEN.js.map → chunk-QQNPVW3J.js.map} +0 -0
- /package/dist/{chunk-IA7BMIPE.js.map → chunk-UZKQSWHJ.js.map} +0 -0
- /package/dist/{chunk-CP4ZM43V.js.map → chunk-WTEILBKC.js.map} +0 -0
- /package/dist/{chunk-BOIF2XGG.js.map → chunk-XXDXIIQ4.js.map} +0 -0
- /package/dist/{chunk-DX32P2DT.js.map → chunk-ZKAKA54B.js.map} +0 -0
- /package/dist/{chunk-TVIX653Q.js.map → chunk-ZKCNPKUO.js.map} +0 -0
- /package/dist/{chunk-TU2WUBVZ.js.map → cli-YL3IDR52.js.map} +0 -0
- /package/dist/{cli-QKIJOXGW.js.map → cli-ZQUJYQBA.js.map} +0 -0
- /package/dist/{cli-YW6VWBHX.js.map → config-O6UBGEDK.js.map} +0 -0
- /package/dist/{github-3WMSTNTK.js.map → conformance-ALLPNGEQ.js.map} +0 -0
- /package/dist/{github-OXSJQ4PS.js.map → github-UCYLM3DR.js.map} +0 -0
- /package/dist/{json-4DCKDGUV.js.map → github-VEJFO6JJ.js.map} +0 -0
- /package/dist/{lint-YDHVVZHT.js.map → host-6GURH5HE.js.map} +0 -0
- /package/dist/{list-3ZO2NIYO.js.map → json-6F52FSXZ.js.map} +0 -0
- /package/dist/{list-EJH6VAOW.js.map → lint-X2GNINK6.js.map} +0 -0
- /package/dist/{page-QEDB7EVU.js.map → list-DP3O3CWB.js.map} +0 -0
- /package/dist/{pretty-5DGEBZSV.js.map → list-P77RZFZF.js.map} +0 -0
- /package/dist/{pretty-SQX33UM2.js.map → page-WQCMBNRD.js.map} +0 -0
- /package/dist/{reporters-OHSI3RMV.js.map → pretty-5MMMNSWF.js.map} +0 -0
- /package/dist/{reporters-YELDDYJD.js.map → pretty-H4DQREMW.js.map} +0 -0
- /package/dist/{run-IP6NZHAG.js.map → provider-AMGRDY6V.js.map} +0 -0
- /package/dist/{validate-G6HH3YXL.js.map → reporters-SU3VUKKX.js.map} +0 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
emptyWindow,
|
|
4
|
+
materialize,
|
|
5
|
+
windowOf
|
|
6
|
+
} from "./chunk-CWXZ2MRT.js";
|
|
7
|
+
import {
|
|
8
|
+
extractEvals,
|
|
9
|
+
findGitRoot,
|
|
10
|
+
listInTree,
|
|
11
|
+
resolveArtifacts,
|
|
12
|
+
safeRead
|
|
13
|
+
} from "./chunk-OOEMKOX6.js";
|
|
14
|
+
import {
|
|
15
|
+
configDir
|
|
16
|
+
} from "./chunk-4TSWU2SU.js";
|
|
17
|
+
import {
|
|
18
|
+
makeRedactor
|
|
19
|
+
} from "./chunk-CU57HT2E.js";
|
|
20
|
+
import {
|
|
21
|
+
extractFrontmatter
|
|
22
|
+
} from "./chunk-MSMB6XOS.js";
|
|
23
|
+
|
|
24
|
+
// src/tracevals/rules/turn.ts
|
|
25
|
+
var INJECTED = /<(system-reminder|task-notification|ci-monitor-event|local-command-stdout|local-command-stderr|local-command-caveat)>[\s\S]*?<\/\1>/g;
|
|
26
|
+
var MARKER = /^(?:Stop hook feedback:|SubagentStop hook feedback:|\[Request interrupted by user)/;
|
|
27
|
+
var TYPED_TURN_ORIGINS = /* @__PURE__ */ new Set(["human", "sdk"]);
|
|
28
|
+
var WORK = /* @__PURE__ */ new Set(["assistant", "tool_call", "tool_result"]);
|
|
29
|
+
function isTypedPrompt(event) {
|
|
30
|
+
if (event.kind !== "user" || event.text === void 0) return false;
|
|
31
|
+
const { raw } = event;
|
|
32
|
+
if (raw.isMeta === true || raw.isCompactSummary === true) return false;
|
|
33
|
+
const origin = raw.origin;
|
|
34
|
+
if (typeof origin === "object" && origin !== null && "kind" in origin && typeof origin.kind === "string") {
|
|
35
|
+
if (!TYPED_TURN_ORIGINS.has(origin.kind)) return false;
|
|
36
|
+
} else if (typeof raw.turnOrigin === "string" && !TYPED_TURN_ORIGINS.has(raw.turnOrigin)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const rest = event.text.replace(INJECTED, "").trim();
|
|
40
|
+
return rest !== "" && !MARKER.test(rest);
|
|
41
|
+
}
|
|
42
|
+
function lastTurn(trace) {
|
|
43
|
+
const prompts = trace.events.filter((e) => e.sidechain !== true && isTypedPrompt(e));
|
|
44
|
+
const sessionTurnCount = prompts.length;
|
|
45
|
+
const to = trace.events.length - 1;
|
|
46
|
+
const last = prompts.at(-1);
|
|
47
|
+
if (last === void 0) {
|
|
48
|
+
return {
|
|
49
|
+
window: emptyWindow("turn", "last turn", "the trace holds no prompt the user typed"),
|
|
50
|
+
from: to + 1,
|
|
51
|
+
to,
|
|
52
|
+
sessionTurnCount
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return slice(trace, last.index, [last], /* @__PURE__ */ new Set([void 0]), sessionTurnCount, {
|
|
56
|
+
label: "last turn",
|
|
57
|
+
reason: "nothing happened after the last prompt the user typed"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function subagentTurn(trace) {
|
|
61
|
+
const prompts = trace.events.filter(isTypedPrompt);
|
|
62
|
+
return slice(
|
|
63
|
+
trace,
|
|
64
|
+
0,
|
|
65
|
+
prompts,
|
|
66
|
+
new Set(trace.events.map((e) => e.branchId)),
|
|
67
|
+
prompts.length,
|
|
68
|
+
{ label: "subagent run", reason: "the subagent recorded no work" }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function slice(trace, from, prompts, own, sessionTurnCount, words) {
|
|
72
|
+
const to = trace.events.length - 1;
|
|
73
|
+
const worked = trace.events.some((e) => e.index > from && WORK.has(e.kind));
|
|
74
|
+
if (!worked) {
|
|
75
|
+
return {
|
|
76
|
+
window: emptyWindow("turn", words.label, words.reason),
|
|
77
|
+
from,
|
|
78
|
+
to,
|
|
79
|
+
sessionTurnCount
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const window = materialize(
|
|
83
|
+
trace,
|
|
84
|
+
"turn",
|
|
85
|
+
words.label,
|
|
86
|
+
[{ start: from, end: Number.POSITIVE_INFINITY }],
|
|
87
|
+
void 0,
|
|
88
|
+
own
|
|
89
|
+
);
|
|
90
|
+
window.userMessages = prompts.map((p) => p.text ?? "");
|
|
91
|
+
window.turnCount = sessionTurnCount;
|
|
92
|
+
return { window, from, to, sessionTurnCount };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/tracevals/rules/sources.ts
|
|
96
|
+
import { createHash } from "node:crypto";
|
|
97
|
+
import { homedir } from "node:os";
|
|
98
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
99
|
+
import picomatch from "picomatch";
|
|
100
|
+
var MAX_IMPORT_HOPS = 4;
|
|
101
|
+
var Collector = class {
|
|
102
|
+
constructor(cwd, root, home, exclude) {
|
|
103
|
+
this.cwd = cwd;
|
|
104
|
+
this.root = root;
|
|
105
|
+
this.home = home;
|
|
106
|
+
const match = exclude.length > 0 ? picomatch(exclude, { dot: true }) : null;
|
|
107
|
+
this.excluded = (path) => match !== null && match(this.display(path));
|
|
108
|
+
this.excludedName = (name) => exclude.includes(name);
|
|
109
|
+
}
|
|
110
|
+
cwd;
|
|
111
|
+
root;
|
|
112
|
+
home;
|
|
113
|
+
sources = [];
|
|
114
|
+
warnings = [];
|
|
115
|
+
seen = /* @__PURE__ */ new Set();
|
|
116
|
+
parsed = /* @__PURE__ */ new Map();
|
|
117
|
+
excluded;
|
|
118
|
+
/** Whether `exclude` lists, as it is, the name of a source with no file, such as `prompt`. */
|
|
119
|
+
excludedName;
|
|
120
|
+
display(path) {
|
|
121
|
+
const inRoot = relPosix(this.root, path);
|
|
122
|
+
if (inRoot !== null) return inRoot;
|
|
123
|
+
const inHome = relPosix(this.home, path);
|
|
124
|
+
if (inHome !== null) return `~/${inHome}`;
|
|
125
|
+
return path.split(sep).join("/");
|
|
126
|
+
}
|
|
127
|
+
has(path) {
|
|
128
|
+
return this.seen.has(key(path));
|
|
129
|
+
}
|
|
130
|
+
/** The file's front matter, or null when it has none or it does not parse. */
|
|
131
|
+
front(path, content) {
|
|
132
|
+
const k = key(path);
|
|
133
|
+
if (this.parsed.has(k)) return this.parsed.get(k) ?? null;
|
|
134
|
+
let parsed = null;
|
|
135
|
+
try {
|
|
136
|
+
const fm = extractFrontmatter(content, "markdown");
|
|
137
|
+
parsed = fm.present ? fm : null;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
const loose = isCursorRule(path) ? cursorFront(content) : null;
|
|
140
|
+
if (loose !== null) {
|
|
141
|
+
this.parsed.set(k, loose);
|
|
142
|
+
return loose;
|
|
143
|
+
}
|
|
144
|
+
const first = (err instanceof Error ? err.message : String(err)).split("\n")[0] ?? "";
|
|
145
|
+
this.warnings.push(
|
|
146
|
+
`${this.display(path)}: front matter does not parse (${first}), so it is read as having none`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
this.parsed.set(k, parsed);
|
|
150
|
+
return parsed;
|
|
151
|
+
}
|
|
152
|
+
/** Adds a source once. Returns it, or null when excluded or already listed. */
|
|
153
|
+
add(path, content, format, trigger, skill) {
|
|
154
|
+
if (this.has(path) || this.excluded(path)) return null;
|
|
155
|
+
this.seen.add(key(path));
|
|
156
|
+
const source = {
|
|
157
|
+
path,
|
|
158
|
+
displayPath: this.display(path),
|
|
159
|
+
format,
|
|
160
|
+
trigger,
|
|
161
|
+
content,
|
|
162
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
163
|
+
blocks: true
|
|
164
|
+
};
|
|
165
|
+
if (skill !== void 0) source.skill = skill;
|
|
166
|
+
const declared = this.declared(path, content);
|
|
167
|
+
if (declared !== void 0) source.declaredRules = declared;
|
|
168
|
+
this.sources.push(source);
|
|
169
|
+
return source;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Adds a source that says what the session was asked, or one the transcript
|
|
173
|
+
* recorded. These never declare evals. With no file, its display path is its
|
|
174
|
+
* format, and `exclude` matches that name.
|
|
175
|
+
*/
|
|
176
|
+
addRequest(file, content, format, trigger, blocks = true) {
|
|
177
|
+
if (EXCLUDABLE_BY_NAME.has(format) && this.excludedName(format)) return;
|
|
178
|
+
if (file !== null) {
|
|
179
|
+
if (this.has(file) || this.excluded(file)) return;
|
|
180
|
+
this.seen.add(key(file));
|
|
181
|
+
}
|
|
182
|
+
this.sources.push({
|
|
183
|
+
path: file ?? "",
|
|
184
|
+
displayPath: file === null ? format : this.display(file),
|
|
185
|
+
format,
|
|
186
|
+
trigger,
|
|
187
|
+
content,
|
|
188
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
189
|
+
blocks
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
declared(path, content) {
|
|
193
|
+
const fm = this.front(path, content);
|
|
194
|
+
if (fm === null) return void 0;
|
|
195
|
+
const extracted = extractEvals(
|
|
196
|
+
{ name: basename(path), type: "project-rules", path, content, origin: "project" },
|
|
197
|
+
fm
|
|
198
|
+
);
|
|
199
|
+
if (extracted.errors.length > 0) {
|
|
200
|
+
this.warnings.push(
|
|
201
|
+
`${this.display(path)}: metadata.evals does not validate (${extracted.errors[0]?.message ?? "invalid"}), so it declares no rules`
|
|
202
|
+
);
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
const rules = [];
|
|
206
|
+
for (const entry of extracted.evals) {
|
|
207
|
+
if (entry.grader !== "ai" || entry.assertion === void 0) continue;
|
|
208
|
+
const rule = { id: entry.id, text: entry.assertion };
|
|
209
|
+
const when = entry.options?.when;
|
|
210
|
+
if (isRecord(when)) rule.when = when;
|
|
211
|
+
rules.push(rule);
|
|
212
|
+
}
|
|
213
|
+
return rules.length > 0 ? rules : void 0;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
async function resolveAlwaysSources(opts) {
|
|
217
|
+
const c = await collector(opts);
|
|
218
|
+
for (const candidate of await catalog(c, opts)) {
|
|
219
|
+
if (candidate.trigger.kind === "always") await addWithImports(c, candidate, "always");
|
|
220
|
+
}
|
|
221
|
+
await addDesignated(c, opts);
|
|
222
|
+
return { sources: c.sources, warnings: c.warnings };
|
|
223
|
+
}
|
|
224
|
+
async function resolveTurnSources(trace, turn, opts) {
|
|
225
|
+
const c = await collector(opts);
|
|
226
|
+
const touches = trace.fileAccesses.filter((a) => a.index <= turn.to).map((a) => {
|
|
227
|
+
const abs = resolve(c.cwd, a.path);
|
|
228
|
+
return { abs, rel: relPosix(c.root, abs), read: a.op === "read" };
|
|
229
|
+
});
|
|
230
|
+
const wasRead = new Set(touches.filter((t) => t.read).map((t) => key(t.abs)));
|
|
231
|
+
for (const candidate of await catalog(c, opts)) {
|
|
232
|
+
const { trigger } = candidate;
|
|
233
|
+
if (trigger.kind === "always") {
|
|
234
|
+
await addWithImports(c, candidate, "always");
|
|
235
|
+
} else if (trigger.kind === "read") {
|
|
236
|
+
if (wasRead.has(key(candidate.path))) await addWithImports(c, candidate, "read");
|
|
237
|
+
} else {
|
|
238
|
+
const match = picomatch(trigger.patterns, { dot: true });
|
|
239
|
+
const hit = touches.find((t) => t.rel !== null && match(t.rel));
|
|
240
|
+
if (hit !== void 0 && hit.rel !== null) {
|
|
241
|
+
await addWithImports(c, candidate, `${trigger.key} matched ${hit.rel}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const nested = /* @__PURE__ */ new Map();
|
|
246
|
+
for (const touch of touches) {
|
|
247
|
+
if (touch.rel === null) continue;
|
|
248
|
+
for (const dir of dirsBelowCwd(c, dirname(touch.abs))) {
|
|
249
|
+
if (!nested.has(dir)) nested.set(dir, touch.rel);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const dirs = [...nested.keys()].sort((a, b) => a.length - b.length);
|
|
253
|
+
for (const dir of dirs) {
|
|
254
|
+
const touched = nested.get(dir) ?? "";
|
|
255
|
+
for (const [name, format] of NESTED_FILES) {
|
|
256
|
+
const path = join(dir, name);
|
|
257
|
+
const content = await safeRead(path);
|
|
258
|
+
if (content !== null) {
|
|
259
|
+
await addWithImports(c, { path, format, trigger: { kind: "always" } }, `touched ${touched}`, content);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
await addWindowed(c, trace, turn, touches, opts);
|
|
264
|
+
await addDesignated(c, opts);
|
|
265
|
+
addPrompts(c, trace, turn);
|
|
266
|
+
await addPlan(c, trace, turn, opts);
|
|
267
|
+
await addSpecs(c, touches, opts.plans ?? []);
|
|
268
|
+
await addSystemPrompt(c, trace, turn, opts);
|
|
269
|
+
return { sources: c.sources, warnings: c.warnings };
|
|
270
|
+
}
|
|
271
|
+
var EXCLUDABLE_BY_NAME = /* @__PURE__ */ new Set(["prompt", "plan", "system-prompt"]);
|
|
272
|
+
var BOUNDARY_MARKER = "__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__";
|
|
273
|
+
function lastAttachment(trace, turn, type) {
|
|
274
|
+
for (let i = trace.events.length - 1; i >= 0; i -= 1) {
|
|
275
|
+
const event = trace.events[i];
|
|
276
|
+
if (event === void 0 || event.kind !== "meta" || event.index > turn.to) continue;
|
|
277
|
+
const attachment = event.raw.attachment;
|
|
278
|
+
if (event.raw.type === "attachment" && isRecord(attachment) && attachment.type === type) {
|
|
279
|
+
return { index: event.index, record: event.raw, attachment };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return void 0;
|
|
283
|
+
}
|
|
284
|
+
async function addSystemPrompt(c, trace, turn, opts) {
|
|
285
|
+
const scrub = makeRedactor(opts.redact ?? []);
|
|
286
|
+
const style = lastAttachment(trace, turn, "output_style_instructions")?.attachment.style;
|
|
287
|
+
const styleName = isRecord(style) && typeof style.name === "string" ? style.name : void 0;
|
|
288
|
+
const stylePrompt = isRecord(style) && typeof style.prompt === "string" ? style.prompt : void 0;
|
|
289
|
+
let builtIn;
|
|
290
|
+
if (styleName !== void 0 && stylePrompt !== void 0) {
|
|
291
|
+
const file = await userStyleFile(c, opts, styleName);
|
|
292
|
+
if (file === void 0) builtIn = stylePrompt;
|
|
293
|
+
else c.addRequest(file, scrub(stylePrompt), "output-style", `style ${styleName}`);
|
|
294
|
+
}
|
|
295
|
+
const snapshot = lastAttachment(trace, turn, "prompt_snapshot");
|
|
296
|
+
const recorded = snapshot?.attachment.systemPrompt;
|
|
297
|
+
if (snapshot === void 0 || !Array.isArray(recorded)) return;
|
|
298
|
+
const blocks = recorded.filter((b) => typeof b === "string");
|
|
299
|
+
const custom = !blocks.includes(BOUNDARY_MARKER);
|
|
300
|
+
const content = scrub([...blocks, ...builtIn !== void 0 ? [builtIn] : []].join("\n\n"));
|
|
301
|
+
const { version } = snapshot.record;
|
|
302
|
+
const by = typeof version === "string" ? `, Claude Code ${version}` : "";
|
|
303
|
+
const trigger = `recorded at turn ${String(snapshot.index)}${by}${custom ? ", custom" : ""}`;
|
|
304
|
+
c.addRequest(
|
|
305
|
+
null,
|
|
306
|
+
content,
|
|
307
|
+
"system-prompt",
|
|
308
|
+
trigger,
|
|
309
|
+
/* blocks= */
|
|
310
|
+
custom
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
async function userStyleFile(c, opts, name) {
|
|
314
|
+
const dirs = [.../* @__PURE__ */ new Set([c.cwd, c.root])].map((b) => join(b, ".claude", "output-styles"));
|
|
315
|
+
dirs.push(join(configDir(opts.env), "output-styles"));
|
|
316
|
+
for (const dir of dirs) {
|
|
317
|
+
for (const path of await listInTree(dir, (p) => p.endsWith(".md"), { maxDepth: 0 })) {
|
|
318
|
+
const declared = (await frontOf(c, path))?.name;
|
|
319
|
+
if ((typeof declared === "string" ? declared : basename(path, ".md")) === name) return path;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function addPrompts(c, trace, turn) {
|
|
325
|
+
const typed = trace.events.filter((e) => e.index <= turn.to && isTypedPrompt(e));
|
|
326
|
+
const main = typed.filter((e) => e.sidechain !== true);
|
|
327
|
+
const prompts = main.length > 0 ? main : typed;
|
|
328
|
+
if (prompts.length === 0) return;
|
|
329
|
+
const content = prompts.map((p, i) => `${String(i + 1)}. ${(p.text ?? "").trim()}`).join("\n\n");
|
|
330
|
+
c.addRequest(null, content, "prompt", `typed prompts 1-${String(prompts.length)}`);
|
|
331
|
+
}
|
|
332
|
+
async function addPlan(c, trace, turn, opts) {
|
|
333
|
+
const approved = trace.toolCalls.filter((call) => call.name === "ExitPlanMode" && call.index <= turn.to).filter((call) => isApproved(trace, call.index, turn.to)).at(-1);
|
|
334
|
+
if (approved === void 0) return;
|
|
335
|
+
const trigger = `approved at turn ${String(approved.index)}`;
|
|
336
|
+
const plan = approved.input.plan;
|
|
337
|
+
if (typeof plan === "string" && plan.trim() !== "") {
|
|
338
|
+
c.addRequest(null, plan, "plan", trigger);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const plansDir = join(configDir(opts.env), "plans");
|
|
342
|
+
const file = trace.fileAccesses.filter((a) => a.op !== "read" && a.index < approved.index).map((a) => resolve(c.cwd, a.path)).filter((abs) => relPosix(plansDir, abs) !== null).at(-1);
|
|
343
|
+
if (file === void 0) return;
|
|
344
|
+
const content = await safeRead(file);
|
|
345
|
+
if (content !== null) c.addRequest(file, content, "plan", trigger);
|
|
346
|
+
}
|
|
347
|
+
function isApproved(trace, index, to) {
|
|
348
|
+
const id = trace.events.find((e) => e.index === index)?.raw.id;
|
|
349
|
+
if (typeof id !== "string") return false;
|
|
350
|
+
for (const event of trace.events) {
|
|
351
|
+
if (event.kind !== "tool_result" || event.index <= index || event.index > to) continue;
|
|
352
|
+
const message = event.raw.message;
|
|
353
|
+
const blocks = isRecord(message) ? message.content : void 0;
|
|
354
|
+
if (!Array.isArray(blocks)) continue;
|
|
355
|
+
for (const block of blocks) {
|
|
356
|
+
if (isRecord(block) && block.type === "tool_result" && block.tool_use_id === id) {
|
|
357
|
+
return block.is_error !== true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
var SPEC_FILES = {
|
|
364
|
+
"speckit-spec": ["spec.md", "plan.md", "tasks.md"],
|
|
365
|
+
"kiro-spec": ["requirements.md", "design.md", "tasks.md"]
|
|
366
|
+
};
|
|
367
|
+
function specDirOf(rel) {
|
|
368
|
+
const parts = rel.split("/");
|
|
369
|
+
const [first, second, third] = parts;
|
|
370
|
+
if (first === "specs" && second !== void 0 && parts.length > 2) {
|
|
371
|
+
return { dir: `specs/${second}`, format: "speckit-spec" };
|
|
372
|
+
}
|
|
373
|
+
if (first === ".kiro" && second === "specs" && third !== void 0 && parts.length > 3) {
|
|
374
|
+
return { dir: `.kiro/specs/${third}`, format: "kiro-spec" };
|
|
375
|
+
}
|
|
376
|
+
if (first === "openspec" && second === "changes" && third !== void 0 && third !== "archive" && parts.length > 3) {
|
|
377
|
+
return { dir: `openspec/changes/${third}`, format: "openspec-change" };
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
async function addSpecs(c, touches, plans) {
|
|
382
|
+
const isPlan = plans.length > 0 ? picomatch(plans, { dot: true }) : null;
|
|
383
|
+
const done = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const touch of touches) {
|
|
385
|
+
const { rel } = touch;
|
|
386
|
+
if (rel === null) continue;
|
|
387
|
+
const trigger = `touched ${rel}`;
|
|
388
|
+
const spec = specDirOf(rel);
|
|
389
|
+
if (spec !== null && !done.has(spec.dir)) {
|
|
390
|
+
done.add(spec.dir);
|
|
391
|
+
const dir = join(c.root, ...spec.dir.split("/"));
|
|
392
|
+
const files = spec.format === "openspec-change" ? await listInTree(dir, (p) => p.endsWith(".md")) : SPEC_FILES[spec.format].map((name) => join(dir, name));
|
|
393
|
+
for (const file of files) {
|
|
394
|
+
const content = await safeRead(file);
|
|
395
|
+
if (content !== null) c.addRequest(file, content, spec.format, trigger);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (isPlan?.(rel) === true) {
|
|
399
|
+
const content = await safeRead(touch.abs);
|
|
400
|
+
if (content !== null) c.addRequest(touch.abs, content, "plans", trigger);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async function addDesignated(c, opts) {
|
|
405
|
+
const include = opts.include ?? [];
|
|
406
|
+
if (include.length === 0) return;
|
|
407
|
+
const match = picomatch(include, { dot: true });
|
|
408
|
+
const found = await listInTree(c.root, (abs) => {
|
|
409
|
+
const rel = relPosix(c.root, abs);
|
|
410
|
+
return rel !== null && match(rel);
|
|
411
|
+
});
|
|
412
|
+
for (const abs of found) {
|
|
413
|
+
if (c.has(abs)) continue;
|
|
414
|
+
const content = await safeRead(abs);
|
|
415
|
+
if (content === null) {
|
|
416
|
+
c.warnings.push(`${c.display(abs)}: matched conformance.include but could not be read`);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
c.add(abs, content, "designated", "always");
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function addWindowed(c, trace, turn, touches, opts) {
|
|
423
|
+
const { artifacts } = await resolveArtifacts(
|
|
424
|
+
{
|
|
425
|
+
...trace,
|
|
426
|
+
agentSpawns: opts.agentType === void 0 ? [] : [{ subagentType: opts.agentType, index: 0 }]
|
|
427
|
+
},
|
|
428
|
+
{ projectDir: c.cwd, projectRoot: c.root, ...opts.env ? { env: opts.env } : {} }
|
|
429
|
+
);
|
|
430
|
+
for (const artifact of artifacts) {
|
|
431
|
+
if (artifact.type === "agent") {
|
|
432
|
+
c.add(artifact.path, artifact.content, "agent", "subagent run");
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (artifact.type !== "skill" && artifact.type !== "slash-command") continue;
|
|
436
|
+
const window = windowOf(trace, artifact);
|
|
437
|
+
if (!window.events.some((e) => e.index <= turn.to)) continue;
|
|
438
|
+
if (artifact.type === "slash-command") {
|
|
439
|
+
c.add(artifact.path, artifact.content, "slash-command", "slash command window");
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
c.add(artifact.path, artifact.content, "skill", "skill window", artifact.name);
|
|
443
|
+
const skillDir = dirname(artifact.path);
|
|
444
|
+
for (const touch of touches) {
|
|
445
|
+
if (!touch.read || relPosix(skillDir, touch.abs) === null || c.has(touch.abs)) continue;
|
|
446
|
+
const content = await safeRead(touch.abs);
|
|
447
|
+
if (content !== null) {
|
|
448
|
+
c.add(touch.abs, content, "skill", `read under skill ${artifact.name}`, artifact.name);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
var ANCESTOR_FILES = [
|
|
454
|
+
["CLAUDE.md", "claude-md"],
|
|
455
|
+
["CLAUDE.local.md", "claude-md"],
|
|
456
|
+
[join(".claude", "CLAUDE.md"), "claude-md"],
|
|
457
|
+
["AGENTS.md", "agents-md"],
|
|
458
|
+
[join(".claude", "AGENTS.md"), "agents-md"],
|
|
459
|
+
["GEMINI.md", "gemini-md"]
|
|
460
|
+
];
|
|
461
|
+
var NESTED_FILES = [
|
|
462
|
+
["CLAUDE.md", "claude-md"],
|
|
463
|
+
["AGENTS.md", "agents-md"],
|
|
464
|
+
["GEMINI.md", "gemini-md"]
|
|
465
|
+
];
|
|
466
|
+
async function catalog(c, opts) {
|
|
467
|
+
const out = [];
|
|
468
|
+
const userClaude = configDir(opts.env);
|
|
469
|
+
const always = { kind: "always" };
|
|
470
|
+
out.push({ path: join(userClaude, "CLAUDE.md"), format: "claude-md", trigger: always });
|
|
471
|
+
out.push({ path: join(c.home, ".gemini", "GEMINI.md"), format: "gemini-md", trigger: always });
|
|
472
|
+
for (const dir of ancestors(c.cwd, c.root).reverse()) {
|
|
473
|
+
for (const [name, format] of ANCESTOR_FILES) {
|
|
474
|
+
out.push({ path: join(dir, name), format, trigger: always });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const bases = c.cwd === c.root ? [c.root] : [c.root, c.cwd];
|
|
478
|
+
for (const dir of [join(userClaude, "rules"), ...bases.map((b) => join(b, ".claude", "rules"))]) {
|
|
479
|
+
for (const path of await listInTree(dir, (p) => p.endsWith(".md"))) {
|
|
480
|
+
out.push({ path, format: "claude-rule", trigger: await claudeRuleTrigger(c, path) });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
for (const base of bases) {
|
|
484
|
+
const dir = join(base, ".cursor", "rules");
|
|
485
|
+
for (const path of await listInTree(dir, (p) => p.endsWith(".mdc") || p.endsWith(".md"))) {
|
|
486
|
+
const trigger = await cursorTrigger(c, path);
|
|
487
|
+
if (trigger !== null) out.push({ path, format: "cursor-rule", trigger });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
for (const dir of [join(c.home, ".kiro", "steering"), ...bases.map((b) => join(b, ".kiro", "steering"))]) {
|
|
491
|
+
for (const path of await listInTree(dir, (p) => p.endsWith(".md"), { maxDepth: 0 })) {
|
|
492
|
+
out.push({ path, format: "kiro-steering", trigger: await kiroTrigger(c, path) });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
for (const base of bases) {
|
|
496
|
+
out.push({
|
|
497
|
+
path: join(base, ".specify", "memory", "constitution.md"),
|
|
498
|
+
format: "speckit-constitution",
|
|
499
|
+
trigger: always
|
|
500
|
+
});
|
|
501
|
+
out.push({ path: join(base, "openspec", "project.md"), format: "openspec-project", trigger: { kind: "read" } });
|
|
502
|
+
}
|
|
503
|
+
return out;
|
|
504
|
+
}
|
|
505
|
+
async function frontOf(c, path) {
|
|
506
|
+
const content = await safeRead(path);
|
|
507
|
+
if (content === null) return null;
|
|
508
|
+
return c.front(path, content)?.data ?? null;
|
|
509
|
+
}
|
|
510
|
+
async function claudeRuleTrigger(c, path) {
|
|
511
|
+
const patterns = globList((await frontOf(c, path))?.paths);
|
|
512
|
+
return patterns.length > 0 ? { kind: "globs", key: "paths", patterns } : { kind: "always" };
|
|
513
|
+
}
|
|
514
|
+
async function cursorTrigger(c, path) {
|
|
515
|
+
const fm = await frontOf(c, path);
|
|
516
|
+
if (fm === null && !path.endsWith(".mdc")) return null;
|
|
517
|
+
if (fm?.alwaysApply === true) return { kind: "always" };
|
|
518
|
+
const patterns = globList(fm?.globs);
|
|
519
|
+
return patterns.length > 0 ? { kind: "globs", key: "globs", patterns } : { kind: "read" };
|
|
520
|
+
}
|
|
521
|
+
async function kiroTrigger(c, path) {
|
|
522
|
+
const fm = await frontOf(c, path);
|
|
523
|
+
const inclusion = fm?.inclusion;
|
|
524
|
+
if (inclusion === void 0 || inclusion === "always") return { kind: "always" };
|
|
525
|
+
if (inclusion === "fileMatch") {
|
|
526
|
+
const patterns = globList(fm?.fileMatchPattern);
|
|
527
|
+
if (patterns.length > 0) return { kind: "globs", key: "fileMatchPattern", patterns };
|
|
528
|
+
}
|
|
529
|
+
return { kind: "read" };
|
|
530
|
+
}
|
|
531
|
+
async function addWithImports(c, candidate, trigger, known) {
|
|
532
|
+
if (c.has(candidate.path)) return;
|
|
533
|
+
const content = known ?? await safeRead(candidate.path);
|
|
534
|
+
if (content === null) return;
|
|
535
|
+
const added = c.add(candidate.path, content, candidate.format, trigger);
|
|
536
|
+
if (added === null || candidate.format !== "claude-md" && candidate.format !== "gemini-md") {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
let frontier = [added];
|
|
540
|
+
for (let hop = 1; hop <= MAX_IMPORT_HOPS && frontier.length > 0; hop += 1) {
|
|
541
|
+
const next = [];
|
|
542
|
+
for (const importer of frontier) {
|
|
543
|
+
for (const ref of importsOf(importer.content)) {
|
|
544
|
+
const path = ref.startsWith("~/") ? join(c.home, ref.slice(2)) : resolve(dirname(importer.path), ref);
|
|
545
|
+
if (c.has(path)) continue;
|
|
546
|
+
const body = await safeRead(path);
|
|
547
|
+
if (body === null) continue;
|
|
548
|
+
const source = c.add(path, body, "import", `imported by ${importer.displayPath}`);
|
|
549
|
+
if (source !== null) next.push(source);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
frontier = next;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function importsOf(content) {
|
|
556
|
+
const refs = [];
|
|
557
|
+
let fence = null;
|
|
558
|
+
for (const line of content.split(/\r?\n/)) {
|
|
559
|
+
const marker = /^\s*(`{3,}|~{3,})/.exec(line)?.[1];
|
|
560
|
+
if (marker !== void 0) {
|
|
561
|
+
const char = marker.charAt(0);
|
|
562
|
+
if (fence === null) {
|
|
563
|
+
fence = { char, length: marker.length };
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (char === fence.char && marker.length >= fence.length && line.trim() === marker) {
|
|
567
|
+
fence = null;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (fence !== null) continue;
|
|
572
|
+
const prose = line.replace(/`[^`]*`/g, "");
|
|
573
|
+
for (const m of prose.matchAll(/(?:^|\s)@([^\s`]+)/g)) {
|
|
574
|
+
const ref = m[1]?.replace(/[.,;:!?)\]]+$/, "");
|
|
575
|
+
if (ref) refs.push(ref);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return refs;
|
|
579
|
+
}
|
|
580
|
+
async function collector(opts) {
|
|
581
|
+
const cwd = resolve(opts.projectDir);
|
|
582
|
+
const root = resolve(opts.projectRoot ?? await findGitRoot(cwd) ?? cwd);
|
|
583
|
+
return new Collector(cwd, root, homeDir(opts.env), opts.exclude ?? []);
|
|
584
|
+
}
|
|
585
|
+
function homeDir(env = process.env) {
|
|
586
|
+
const set = process.platform === "win32" ? env.USERPROFILE ?? env.HOME : env.HOME;
|
|
587
|
+
return resolve(set ?? homedir());
|
|
588
|
+
}
|
|
589
|
+
function ancestors(cwd, root) {
|
|
590
|
+
const dirs = [];
|
|
591
|
+
let current = cwd;
|
|
592
|
+
for (let depth = 0; depth < 32; depth += 1) {
|
|
593
|
+
dirs.push(current);
|
|
594
|
+
if (current === root) break;
|
|
595
|
+
const parent = dirname(current);
|
|
596
|
+
if (parent === current) break;
|
|
597
|
+
current = parent;
|
|
598
|
+
}
|
|
599
|
+
return dirs;
|
|
600
|
+
}
|
|
601
|
+
function dirsBelowCwd(c, dir) {
|
|
602
|
+
const dirs = [];
|
|
603
|
+
let current = dir;
|
|
604
|
+
for (let depth = 0; depth < 64; depth += 1) {
|
|
605
|
+
if (relPosix(c.root, current) === null || relPosix(current, c.cwd) !== null) break;
|
|
606
|
+
dirs.push(current);
|
|
607
|
+
const parent = dirname(current);
|
|
608
|
+
if (parent === current) break;
|
|
609
|
+
current = parent;
|
|
610
|
+
}
|
|
611
|
+
return dirs;
|
|
612
|
+
}
|
|
613
|
+
function isCursorRule(path) {
|
|
614
|
+
return path.split(sep).join("/").includes("/.cursor/rules/");
|
|
615
|
+
}
|
|
616
|
+
function cursorFront(content) {
|
|
617
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content)?.[1];
|
|
618
|
+
if (block === void 0) return null;
|
|
619
|
+
const data = {};
|
|
620
|
+
for (const line of block.split(/\r?\n/)) {
|
|
621
|
+
const m = /^(description|globs|alwaysApply):\s*(.*)$/.exec(line);
|
|
622
|
+
const name = m?.[1];
|
|
623
|
+
const value = m?.[2]?.trim();
|
|
624
|
+
if (name === void 0 || value === void 0) continue;
|
|
625
|
+
data[name] = name === "alwaysApply" ? value === "true" : value;
|
|
626
|
+
}
|
|
627
|
+
if (Object.keys(data).length === 0) return null;
|
|
628
|
+
return { data, present: true, format: "markdown", lineFor: () => void 0 };
|
|
629
|
+
}
|
|
630
|
+
function globList(value) {
|
|
631
|
+
if (Array.isArray(value)) {
|
|
632
|
+
return value.filter((v) => typeof v === "string" && v.trim() !== "").map((v) => v.trim());
|
|
633
|
+
}
|
|
634
|
+
if (typeof value !== "string") return [];
|
|
635
|
+
const out = [];
|
|
636
|
+
let depth = 0;
|
|
637
|
+
let current = "";
|
|
638
|
+
for (const ch of value) {
|
|
639
|
+
if (ch === "{") depth += 1;
|
|
640
|
+
if (ch === "}") depth = Math.max(0, depth - 1);
|
|
641
|
+
if (ch === "," && depth === 0) {
|
|
642
|
+
out.push(current);
|
|
643
|
+
current = "";
|
|
644
|
+
} else {
|
|
645
|
+
current += ch;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
out.push(current);
|
|
649
|
+
return out.map((p) => p.trim()).filter((p) => p !== "");
|
|
650
|
+
}
|
|
651
|
+
function relPosix(base, path) {
|
|
652
|
+
const rel = relative(base, path);
|
|
653
|
+
if (rel === "") return "";
|
|
654
|
+
if (rel.startsWith("..") || isAbsolute(rel)) return null;
|
|
655
|
+
return rel.split(sep).join("/");
|
|
656
|
+
}
|
|
657
|
+
function key(path) {
|
|
658
|
+
const abs = resolve(path);
|
|
659
|
+
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
660
|
+
}
|
|
661
|
+
var pathKey = key;
|
|
662
|
+
function isRecord(value) {
|
|
663
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
export {
|
|
667
|
+
isTypedPrompt,
|
|
668
|
+
lastTurn,
|
|
669
|
+
subagentTurn,
|
|
670
|
+
resolveAlwaysSources,
|
|
671
|
+
resolveTurnSources,
|
|
672
|
+
pathKey
|
|
673
|
+
};
|
|
674
|
+
//# sourceMappingURL=chunk-D6RO54QD.js.map
|