@letrquan/book 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2747 -40
- package/README.md +132 -1311
- package/dist/additional-roots-H7UNAEHS.js +31 -0
- package/dist/{app-MOXXWEOY.js → app-GE4OHJOC.js} +5740 -4045
- package/dist/app-GE4OHJOC.js.map +1 -0
- package/dist/{chunk-5GDZ22YP.js → chunk-224FJZLH.js} +99 -108
- package/dist/chunk-224FJZLH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-4AS7LKKQ.js} +6 -4
- package/dist/chunk-4AS7LKKQ.js.map +1 -0
- package/dist/chunk-525CAZPY.js +120 -0
- package/dist/chunk-525CAZPY.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-56SIEMDX.js} +2 -2
- package/dist/chunk-56SIEMDX.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-5YHTKWJN.js} +5420 -4031
- package/dist/chunk-5YHTKWJN.js.map +1 -0
- package/dist/chunk-62QL5QUP.js +283 -0
- package/dist/chunk-62QL5QUP.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-67UZEWWF.js} +3 -3
- package/dist/chunk-67UZEWWF.js.map +1 -0
- package/dist/chunk-6OQQOMZR.js +785 -0
- package/dist/chunk-6OQQOMZR.js.map +1 -0
- package/dist/{chunk-GTVB4HEM.js → chunk-AQEQX4FF.js} +344 -315
- package/dist/chunk-AQEQX4FF.js.map +1 -0
- package/dist/chunk-C7ADUNPR.js +273 -0
- package/dist/chunk-C7ADUNPR.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-EFJ2NH6E.js} +12 -58
- package/dist/chunk-EFJ2NH6E.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-HQI7NYGX.js} +5 -3
- package/dist/chunk-HQI7NYGX.js.map +1 -0
- package/dist/chunk-IME7R2UI.js +6541 -0
- package/dist/chunk-IME7R2UI.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/{chunk-MAQXCUR4.js → chunk-J62VZYOB.js} +444 -112
- package/dist/chunk-J62VZYOB.js.map +1 -0
- package/dist/chunk-JTPOI3KU.js +826 -0
- package/dist/chunk-JTPOI3KU.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-KBQS35OV.js} +2 -2
- package/dist/chunk-KBQS35OV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-QYBD22H5.js} +1 -1
- package/dist/chunk-QYBD22H5.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-RFJ6Q6LE.js} +763 -198
- package/dist/chunk-RFJ6Q6LE.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/{chunk-C7D4NJ2D.js → chunk-S4JMID4G.js} +68 -26
- package/dist/chunk-S4JMID4G.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-SOW4WRQP.js} +3 -3
- package/dist/chunk-SOW4WRQP.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-TLX3H2CL.js} +518 -79
- package/dist/chunk-TLX3H2CL.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-VPNLCMVR.js} +22 -9
- package/dist/chunk-VPNLCMVR.js.map +1 -0
- package/dist/{chunk-BTZZUCLI.js → chunk-W3XFHEF4.js} +173 -29
- package/dist/chunk-W3XFHEF4.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-X4NJT436.js} +130 -60
- package/dist/chunk-X4NJT436.js.map +1 -0
- package/dist/chunk-X66I2NQO.js +137 -0
- package/dist/chunk-X66I2NQO.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-Y2VHN7TC.js} +2 -2
- package/dist/chunk-Y2VHN7TC.js.map +1 -0
- package/dist/chunk-YC6W4EJH.js +947 -0
- package/dist/chunk-YC6W4EJH.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-VBIU6BLM.js} +6 -5
- package/dist/context-CBOBDY5Q.js +38 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-ZVT4V7IM.js} +5 -5
- package/dist/index.js +996 -155
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-T4I4SEHU.js} +13 -9
- package/dist/interactive-assets-T4I4SEHU.js.map +1 -0
- package/dist/job-runner.js +159 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +112 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-S77OVTHH.js +18 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-HJ2VDYMC.js} +5 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-RNB5LEVE.js} +3 -3
- package/dist/memory-store-DZUT5HUH.js +75 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-MTSQ5JDJ.js} +8 -4
- package/dist/openai-compatible-2OB54YST.js +14 -0
- package/dist/{permission-approvals-76PVREFD.js → permission-approvals-YLV6UC5W.js} +5 -5
- package/dist/permissions-DHOKF3W2.js +45 -0
- package/dist/sandbox-WRYTZH3D.js +41 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-35ME2GFX.js} +32 -22
- package/dist/scrollback-35ME2GFX.js.map +1 -0
- package/dist/sdk.d.ts +2219 -2361
- package/dist/sdk.js +53 -38
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-JUNPGAAM.js +35 -0
- package/dist/settings-loader-JUNPGAAM.js.map +1 -0
- package/dist/{shell-selection-V6QAYXPX.js → shell-selection-CDHGJ4GC.js} +2 -2
- package/dist/shell-selection-CDHGJ4GC.js.map +1 -0
- package/package.json +23 -22
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-H7UNAEHS.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-VBIU6BLM.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-CBOBDY5Q.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-ZVT4V7IM.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-S77OVTHH.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-HJ2VDYMC.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-RNB5LEVE.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-DZUT5HUH.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-MTSQ5JDJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-2OB54YST.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-YLV6UC5W.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-DHOKF3W2.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-WRYTZH3D.js.map} +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// src/debug-log.ts
|
|
2
|
+
import {
|
|
3
|
+
appendFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
renameSync,
|
|
8
|
+
statSync,
|
|
9
|
+
unlinkSync
|
|
10
|
+
} from "fs";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
var DEBUG_ENABLED = process.env.BOOK_DEBUG === "1";
|
|
13
|
+
var UI_DEBUG_ENABLED = process.env.BOOK_DEBUG_UI === "1" || DEBUG_ENABLED;
|
|
14
|
+
var RENDER_DEBUG_ENABLED = process.env.BOOK_DEBUG_RENDER === "1";
|
|
15
|
+
var FLOW_DEBUG_ENABLED = process.env.BOOK_DEBUG_FLOW === "1";
|
|
16
|
+
var debugLogPath;
|
|
17
|
+
var debugLogSizes = /* @__PURE__ */ new Map();
|
|
18
|
+
var DEFAULT_DEBUG_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
19
|
+
var MIN_DEBUG_LOG_MAX_BYTES = 4;
|
|
20
|
+
var DEFAULT_DEBUG_LOG_BACKUPS = 3;
|
|
21
|
+
var DEFAULT_LOCAL_DATA_RETENTION_DAYS = 30;
|
|
22
|
+
function positiveInteger(value, fallback) {
|
|
23
|
+
const parsed = Number(value);
|
|
24
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
25
|
+
}
|
|
26
|
+
function debugLogMaxBytes() {
|
|
27
|
+
return Math.max(
|
|
28
|
+
MIN_DEBUG_LOG_MAX_BYTES,
|
|
29
|
+
positiveInteger(process.env.BOOK_DEBUG_MAX_BYTES, DEFAULT_DEBUG_LOG_MAX_BYTES)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
function rotateDebugLog(target, incomingBytes) {
|
|
33
|
+
if (!existsSync(target)) {
|
|
34
|
+
debugLogSizes.set(target, 0);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const maxBytes = debugLogMaxBytes();
|
|
38
|
+
const currentBytes = debugLogSizes.get(target) ?? statSync(target).size;
|
|
39
|
+
debugLogSizes.set(target, currentBytes);
|
|
40
|
+
if (currentBytes + incomingBytes <= maxBytes) return;
|
|
41
|
+
const backups = positiveInteger(process.env.BOOK_DEBUG_BACKUPS, DEFAULT_DEBUG_LOG_BACKUPS);
|
|
42
|
+
const oldest = `${target}.${backups}`;
|
|
43
|
+
if (existsSync(oldest)) unlinkSync(oldest);
|
|
44
|
+
for (let index = backups - 1; index >= 1; index--) {
|
|
45
|
+
const source = `${target}.${index}`;
|
|
46
|
+
if (existsSync(source)) renameSync(source, `${target}.${index + 1}`);
|
|
47
|
+
}
|
|
48
|
+
renameSync(target, `${target}.1`);
|
|
49
|
+
debugLogSizes.set(target, 0);
|
|
50
|
+
}
|
|
51
|
+
function defaultDebugLogPath() {
|
|
52
|
+
return join(process.env.BOOK_WORKSPACE || process.cwd(), ".book", "debug.log");
|
|
53
|
+
}
|
|
54
|
+
function getDebugLogPath() {
|
|
55
|
+
if (process.env.BOOK_DEBUG_STDERR === "1") return void 0;
|
|
56
|
+
if (process.env.BOOK_DEBUG_FILE) return process.env.BOOK_DEBUG_FILE;
|
|
57
|
+
if (process.stderr.isTTY) return defaultDebugLogPath();
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
function cleanupDebugLogs(days = DEFAULT_LOCAL_DATA_RETENTION_DAYS, target = getDebugLogPath(), now = Date.now()) {
|
|
61
|
+
if (!target) return 0;
|
|
62
|
+
const cutoff = now - Math.max(1, days) * 864e5;
|
|
63
|
+
const directory = dirname(target);
|
|
64
|
+
const prefix = `${target.split(/[\\/]/).pop()}.`;
|
|
65
|
+
let removed = 0;
|
|
66
|
+
try {
|
|
67
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
68
|
+
if (!entry.isFile() || !entry.name.startsWith(prefix)) continue;
|
|
69
|
+
const path = join(directory, entry.name);
|
|
70
|
+
if (statSync(path).mtimeMs >= cutoff) continue;
|
|
71
|
+
unlinkSync(path);
|
|
72
|
+
debugLogSizes.delete(path);
|
|
73
|
+
removed++;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
return removed;
|
|
78
|
+
}
|
|
79
|
+
function writeDebugLine(line) {
|
|
80
|
+
const target = debugLogPath ?? (debugLogPath = getDebugLogPath());
|
|
81
|
+
if (!target) {
|
|
82
|
+
process.stderr.write(line);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
87
|
+
const lineBytes = Buffer.byteLength(line);
|
|
88
|
+
rotateDebugLog(target, lineBytes);
|
|
89
|
+
appendFileSync(target, line, "utf8");
|
|
90
|
+
debugLogSizes.set(target, (debugLogSizes.get(target) ?? 0) + lineBytes);
|
|
91
|
+
} catch {
|
|
92
|
+
process.stderr.write(line);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function writeSizeBoundedDebugLine(line, maxBytes) {
|
|
96
|
+
if (Buffer.byteLength(line) <= maxBytes) {
|
|
97
|
+
writeDebugLine(line);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let chunk = "";
|
|
101
|
+
let chunkBytes = 0;
|
|
102
|
+
for (const character of line) {
|
|
103
|
+
const characterBytes = Buffer.byteLength(character);
|
|
104
|
+
if (chunk && chunkBytes + characterBytes > maxBytes) {
|
|
105
|
+
writeDebugLine(chunk);
|
|
106
|
+
chunk = "";
|
|
107
|
+
chunkBytes = 0;
|
|
108
|
+
}
|
|
109
|
+
chunk += character;
|
|
110
|
+
chunkBytes += characterBytes;
|
|
111
|
+
}
|
|
112
|
+
if (chunk) writeDebugLine(chunk);
|
|
113
|
+
}
|
|
114
|
+
var pendingRenderDebug = [];
|
|
115
|
+
var renderDebugFlush = null;
|
|
116
|
+
function writeRenderDebugLine(line) {
|
|
117
|
+
pendingRenderDebug.push(line);
|
|
118
|
+
if (renderDebugFlush !== null) return;
|
|
119
|
+
renderDebugFlush = setImmediate(() => {
|
|
120
|
+
renderDebugFlush = null;
|
|
121
|
+
const batch = pendingRenderDebug;
|
|
122
|
+
pendingRenderDebug = [];
|
|
123
|
+
const maxBytes = debugLogMaxBytes();
|
|
124
|
+
let chunk = "";
|
|
125
|
+
let chunkBytes = 0;
|
|
126
|
+
for (const pendingLine of batch) {
|
|
127
|
+
const lineBytes = Buffer.byteLength(pendingLine);
|
|
128
|
+
if (chunk && chunkBytes + lineBytes > maxBytes) {
|
|
129
|
+
writeDebugLine(chunk);
|
|
130
|
+
chunk = "";
|
|
131
|
+
chunkBytes = 0;
|
|
132
|
+
}
|
|
133
|
+
if (lineBytes > maxBytes) {
|
|
134
|
+
writeSizeBoundedDebugLine(pendingLine, maxBytes);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
chunk += pendingLine;
|
|
138
|
+
chunkBytes += lineBytes;
|
|
139
|
+
}
|
|
140
|
+
if (chunk) writeDebugLine(chunk);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
var namespaceCounters = /* @__PURE__ */ new Map();
|
|
144
|
+
function nextEventId(namespace) {
|
|
145
|
+
const n = (namespaceCounters.get(namespace) ?? 0) + 1;
|
|
146
|
+
namespaceCounters.set(namespace, n);
|
|
147
|
+
return n;
|
|
148
|
+
}
|
|
149
|
+
function isDebugEnabled() {
|
|
150
|
+
return DEBUG_ENABLED;
|
|
151
|
+
}
|
|
152
|
+
function isUiDebugEnabled() {
|
|
153
|
+
return UI_DEBUG_ENABLED;
|
|
154
|
+
}
|
|
155
|
+
function isRenderDebugEnabled() {
|
|
156
|
+
return RENDER_DEBUG_ENABLED;
|
|
157
|
+
}
|
|
158
|
+
function timestamp() {
|
|
159
|
+
const now = /* @__PURE__ */ new Date();
|
|
160
|
+
const h = String(now.getHours()).padStart(2, "0");
|
|
161
|
+
const m = String(now.getMinutes()).padStart(2, "0");
|
|
162
|
+
const s = String(now.getSeconds()).padStart(2, "0");
|
|
163
|
+
const ms = String(now.getMilliseconds()).padStart(3, "0");
|
|
164
|
+
return `${h}:${m}:${s}.${ms}`;
|
|
165
|
+
}
|
|
166
|
+
function truncate(value, max = 80) {
|
|
167
|
+
const single = value.replace(/\s+/g, " ").trim();
|
|
168
|
+
if (single.length <= max) return single;
|
|
169
|
+
return `${single.slice(0, max)}\u2026`;
|
|
170
|
+
}
|
|
171
|
+
function formatValue(a) {
|
|
172
|
+
if (typeof a === "string") return a;
|
|
173
|
+
if (a instanceof Error) return a.message;
|
|
174
|
+
try {
|
|
175
|
+
return JSON.stringify(a);
|
|
176
|
+
} catch {
|
|
177
|
+
return String(a);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function formatMessage(...args) {
|
|
181
|
+
return args.map(formatValue).join(" ");
|
|
182
|
+
}
|
|
183
|
+
function formatMeta(meta) {
|
|
184
|
+
const parts = [];
|
|
185
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
186
|
+
let repr;
|
|
187
|
+
if (typeof value === "string") {
|
|
188
|
+
repr = truncate(value, 120);
|
|
189
|
+
} else if (value === null) {
|
|
190
|
+
repr = "null";
|
|
191
|
+
} else if (value === void 0) {
|
|
192
|
+
repr = "undefined";
|
|
193
|
+
} else {
|
|
194
|
+
try {
|
|
195
|
+
repr = JSON.stringify(value);
|
|
196
|
+
} catch {
|
|
197
|
+
repr = String(value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
parts.push(`${key}=${repr}`);
|
|
201
|
+
}
|
|
202
|
+
return parts.join(" ");
|
|
203
|
+
}
|
|
204
|
+
function noop() {
|
|
205
|
+
}
|
|
206
|
+
var NOOP_LOGGER = {
|
|
207
|
+
debug: noop,
|
|
208
|
+
info: noop,
|
|
209
|
+
warn: noop,
|
|
210
|
+
event: noop
|
|
211
|
+
};
|
|
212
|
+
function makePrefix(namespace, level) {
|
|
213
|
+
return `[${timestamp()}] [${namespace}] [${level}]`;
|
|
214
|
+
}
|
|
215
|
+
function buildLogger(namespace, write = writeDebugLine) {
|
|
216
|
+
return {
|
|
217
|
+
debug: (...args) => {
|
|
218
|
+
write(`${makePrefix(namespace, "DEBUG")} ${formatMessage(...args)}
|
|
219
|
+
`);
|
|
220
|
+
},
|
|
221
|
+
info: (...args) => {
|
|
222
|
+
write(`${makePrefix(namespace, "INFO")} ${formatMessage(...args)}
|
|
223
|
+
`);
|
|
224
|
+
},
|
|
225
|
+
warn: (...args) => {
|
|
226
|
+
write(`${makePrefix(namespace, "WARN")} ${formatMessage(...args)}
|
|
227
|
+
`);
|
|
228
|
+
},
|
|
229
|
+
event: (event, meta) => {
|
|
230
|
+
const suffix = meta && Object.keys(meta).length > 0 ? ` ${formatMeta(meta)}` : "";
|
|
231
|
+
write(`${makePrefix(namespace, "DEBUG")} ${event}${suffix}
|
|
232
|
+
`);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function createDebugLogger(namespace) {
|
|
237
|
+
if (!DEBUG_ENABLED) return NOOP_LOGGER;
|
|
238
|
+
return buildLogger(namespace);
|
|
239
|
+
}
|
|
240
|
+
function createDebugLoggerWithCounter(namespace) {
|
|
241
|
+
if (!DEBUG_ENABLED) return NOOP_LOGGER;
|
|
242
|
+
const wrap = (level) => (...args) => {
|
|
243
|
+
const id = nextEventId(namespace);
|
|
244
|
+
writeDebugLine(
|
|
245
|
+
`[${timestamp()}] [${namespace}] [${level.toUpperCase()}#${id}] ${formatMessage(...args)}
|
|
246
|
+
`
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
return {
|
|
250
|
+
debug: wrap("debug"),
|
|
251
|
+
info: wrap("info"),
|
|
252
|
+
warn: wrap("warn"),
|
|
253
|
+
event: (event, meta) => {
|
|
254
|
+
const id = nextEventId(namespace);
|
|
255
|
+
const suffix = meta && Object.keys(meta).length > 0 ? ` ${formatMeta(meta)}` : "";
|
|
256
|
+
writeDebugLine(`[${timestamp()}] [${namespace}] [DEBUG#${id}] ${event}${suffix}
|
|
257
|
+
`);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function createUiDebugLogger(namespace) {
|
|
262
|
+
if (!UI_DEBUG_ENABLED) return NOOP_LOGGER;
|
|
263
|
+
return buildLogger(namespace);
|
|
264
|
+
}
|
|
265
|
+
function createRenderDebugLogger(namespace) {
|
|
266
|
+
if (!RENDER_DEBUG_ENABLED) return NOOP_LOGGER;
|
|
267
|
+
return buildLogger(namespace, writeRenderDebugLine);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export {
|
|
271
|
+
DEFAULT_LOCAL_DATA_RETENTION_DAYS,
|
|
272
|
+
positiveInteger,
|
|
273
|
+
getDebugLogPath,
|
|
274
|
+
cleanupDebugLogs,
|
|
275
|
+
isDebugEnabled,
|
|
276
|
+
isUiDebugEnabled,
|
|
277
|
+
isRenderDebugEnabled,
|
|
278
|
+
createDebugLogger,
|
|
279
|
+
createDebugLoggerWithCounter,
|
|
280
|
+
createUiDebugLogger,
|
|
281
|
+
createRenderDebugLogger
|
|
282
|
+
};
|
|
283
|
+
//# sourceMappingURL=chunk-62QL5QUP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debug-log.ts"],"sourcesContent":["/**\n * Lightweight debug logger controlled by environment variables.\n *\n * Writes timestamped messages to a log file when stderr is an interactive TTY,\n * because Ink renders to the same terminal and raw stderr writes corrupt the UI.\n * In non-TTY contexts (redirected stderr, CI), logs continue to use stderr.\n * Most functions are no-ops when the relevant env var is not set to '1'.\n *\n * Gated by separate flags so output volume stays controllable:\n * BOOK_DEBUG=1 — provider + agent + TUI lifecycle events\n * BOOK_DEBUG_UI=1 — input handling, modal toggles, permission prompts,\n * component mount/unmount, layout state changes\n * BOOK_DEBUG_RENDER=1 — per-render details (noisy; can flood during streaming)\n * BOOK_DEBUG_FLOW=1 — enter/exit timing around expensive flows\n * BOOK_DEBUG_FILE=path — write logs to a specific file\n * BOOK_DEBUG_STDERR=1 — force stderr even when attached to a TTY\n *\n * `BOOK_DEBUG=1` covers the union of \"core + UI\" (lifecycle). UI hooks read\n * `isUiDebugEnabled()`, which returns true if either BOOK_DEBUG=1 or\n * BOOK_DEBUG_UI=1 so the default mode surfaces UI events too.\n *\n * Usage:\n * import { createDebugLogger } from '../debug-log.js';\n * const log = createDebugLogger('provider');\n * log.debug('Sending request', { model: 'gpt-4' });\n */\n\nimport {\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n} from 'fs';\nimport { dirname, join } from 'path';\n\n// Core flag — provider, agent loop, and TUI lifecycle.\nconst DEBUG_ENABLED = process.env.BOOK_DEBUG === '1';\n// UI flag — input, modals, permissions, mount/unmount, layout changes.\n// Falls back to core flag so default debug mode surfaces UI events too.\nconst UI_DEBUG_ENABLED = process.env.BOOK_DEBUG_UI === '1' || DEBUG_ENABLED;\n// Noisy per-render flag — opt-in only.\nconst RENDER_DEBUG_ENABLED = process.env.BOOK_DEBUG_RENDER === '1';\n// Flow/tracing flag — enter/exit pairs with timing — opt-in only.\nconst FLOW_DEBUG_ENABLED = process.env.BOOK_DEBUG_FLOW === '1';\n\nlet debugLogPath: string | undefined;\nconst debugLogSizes = new Map<string, number>();\nconst DEFAULT_DEBUG_LOG_MAX_BYTES = 5 * 1024 * 1024;\nconst MIN_DEBUG_LOG_MAX_BYTES = 4;\nconst DEFAULT_DEBUG_LOG_BACKUPS = 3;\nexport const DEFAULT_LOCAL_DATA_RETENTION_DAYS = 30;\n\nexport function positiveInteger(value: string | undefined, fallback: number): number {\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction debugLogMaxBytes(): number {\n // Four bytes is the largest UTF-8 encoding for a single Unicode code point.\n return Math.max(\n MIN_DEBUG_LOG_MAX_BYTES,\n positiveInteger(process.env.BOOK_DEBUG_MAX_BYTES, DEFAULT_DEBUG_LOG_MAX_BYTES),\n );\n}\n\nfunction rotateDebugLog(target: string, incomingBytes: number): void {\n if (!existsSync(target)) {\n debugLogSizes.set(target, 0);\n return;\n }\n const maxBytes = debugLogMaxBytes();\n const currentBytes = debugLogSizes.get(target) ?? statSync(target).size;\n debugLogSizes.set(target, currentBytes);\n if (currentBytes + incomingBytes <= maxBytes) return;\n\n const backups = positiveInteger(process.env.BOOK_DEBUG_BACKUPS, DEFAULT_DEBUG_LOG_BACKUPS);\n const oldest = `${target}.${backups}`;\n if (existsSync(oldest)) unlinkSync(oldest);\n for (let index = backups - 1; index >= 1; index--) {\n const source = `${target}.${index}`;\n if (existsSync(source)) renameSync(source, `${target}.${index + 1}`);\n }\n renameSync(target, `${target}.1`);\n debugLogSizes.set(target, 0);\n}\n\nfunction defaultDebugLogPath(): string {\n return join(process.env.BOOK_WORKSPACE || process.cwd(), '.book', 'debug.log');\n}\n\nexport function getDebugLogPath(): string | undefined {\n if (process.env.BOOK_DEBUG_STDERR === '1') return undefined;\n if (process.env.BOOK_DEBUG_FILE) return process.env.BOOK_DEBUG_FILE;\n if (process.stderr.isTTY) return defaultDebugLogPath();\n return undefined;\n}\n\n/** Remove expired rotated logs while always preserving the active log file. */\nexport function cleanupDebugLogs(\n days = DEFAULT_LOCAL_DATA_RETENTION_DAYS,\n target = getDebugLogPath(),\n now = Date.now(),\n): number {\n if (!target) return 0;\n const cutoff = now - Math.max(1, days) * 86_400_000;\n const directory = dirname(target);\n const prefix = `${target.split(/[\\\\/]/).pop()}.`;\n let removed = 0;\n try {\n for (const entry of readdirSync(directory, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.startsWith(prefix)) continue;\n const path = join(directory, entry.name);\n if (statSync(path).mtimeMs >= cutoff) continue;\n unlinkSync(path);\n debugLogSizes.delete(path);\n removed++;\n }\n } catch {\n // Retention cleanup is best effort and must never block startup.\n }\n return removed;\n}\n\nfunction writeDebugLine(line: string): void {\n const target = debugLogPath ?? (debugLogPath = getDebugLogPath());\n if (!target) {\n process.stderr.write(line);\n return;\n }\n\n try {\n mkdirSync(dirname(target), { recursive: true });\n const lineBytes = Buffer.byteLength(line);\n rotateDebugLog(target, lineBytes);\n appendFileSync(target, line, 'utf8');\n debugLogSizes.set(target, (debugLogSizes.get(target) ?? 0) + lineBytes);\n } catch {\n process.stderr.write(line);\n }\n}\n\nfunction writeSizeBoundedDebugLine(line: string, maxBytes: number): void {\n if (Buffer.byteLength(line) <= maxBytes) {\n writeDebugLine(line);\n return;\n }\n\n // Split oversized render events on UTF-8 character boundaries so one burst\n // cannot bypass the configured active-log cap.\n let chunk = '';\n let chunkBytes = 0;\n for (const character of line) {\n const characterBytes = Buffer.byteLength(character);\n if (chunk && chunkBytes + characterBytes > maxBytes) {\n writeDebugLine(chunk);\n chunk = '';\n chunkBytes = 0;\n }\n chunk += character;\n chunkBytes += characterBytes;\n }\n if (chunk) writeDebugLine(chunk);\n}\n\nlet pendingRenderDebug: string[] = [];\nlet renderDebugFlush: ReturnType<typeof setImmediate> | null = null;\n\nfunction writeRenderDebugLine(line: string): void {\n pendingRenderDebug.push(line);\n if (renderDebugFlush !== null) return;\n renderDebugFlush = setImmediate(() => {\n renderDebugFlush = null;\n const batch = pendingRenderDebug;\n pendingRenderDebug = [];\n const maxBytes = debugLogMaxBytes();\n let chunk = '';\n let chunkBytes = 0;\n\n for (const pendingLine of batch) {\n const lineBytes = Buffer.byteLength(pendingLine);\n if (chunk && chunkBytes + lineBytes > maxBytes) {\n writeDebugLine(chunk);\n chunk = '';\n chunkBytes = 0;\n }\n if (lineBytes > maxBytes) {\n writeSizeBoundedDebugLine(pendingLine, maxBytes);\n continue;\n }\n chunk += pendingLine;\n chunkBytes += lineBytes;\n }\n if (chunk) writeDebugLine(chunk);\n });\n}\n\n// Per-namespace monotonic event counter, used when a logger is created with\n// `createDebugLoggerWithCounter`. Useful for tracing event ordering across\n// concurrent sources (streaming flushes vs. user input).\nconst namespaceCounters = new Map<string, number>();\n\nfunction nextEventId(namespace: string): number {\n const n = (namespaceCounters.get(namespace) ?? 0) + 1;\n namespaceCounters.set(namespace, n);\n return n;\n}\n\n/** True if core debug (provider/agent/TUI lifecycle) is on. */\nexport function isDebugEnabled(): boolean {\n return DEBUG_ENABLED;\n}\n\n/** True if UI debug is on (BOOK_DEBUG=1 already enables this). */\nexport function isUiDebugEnabled(): boolean {\n return UI_DEBUG_ENABLED;\n}\n\n/** True if per-render debug is on (opt-in via BOOK_DEBUG_RENDER=1). */\nexport function isRenderDebugEnabled(): boolean {\n return RENDER_DEBUG_ENABLED;\n}\n\n/** True if flow tracing is on (opt-in via BOOK_DEBUG_FLOW=1). */\nexport function isFlowDebugEnabled(): boolean {\n return FLOW_DEBUG_ENABLED;\n}\n\nfunction timestamp(): string {\n const now = new Date();\n const h = String(now.getHours()).padStart(2, '0');\n const m = String(now.getMinutes()).padStart(2, '0');\n const s = String(now.getSeconds()).padStart(2, '0');\n const ms = String(now.getMilliseconds()).padStart(3, '0');\n return `${h}:${m}:${s}.${ms}`;\n}\n\n/** Truncate a string for safe preview in logs. */\nfunction truncate(value: string, max = 80): string {\n const single = value.replace(/\\s+/g, ' ').trim();\n if (single.length <= max) return single;\n return `${single.slice(0, max)}…`;\n}\n\nfunction formatValue(a: unknown): string {\n if (typeof a === 'string') return a;\n if (a instanceof Error) return a.message;\n try {\n return JSON.stringify(a);\n } catch {\n return String(a);\n }\n}\n\nfunction formatMessage(...args: unknown[]): string {\n return args.map(formatValue).join(' ');\n}\n\n/** Format a metadata object, truncating any long string values. */\nfunction formatMeta(meta: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(meta)) {\n let repr: string;\n if (typeof value === 'string') {\n repr = truncate(value, 120);\n } else if (value === null) {\n repr = 'null';\n } else if (value === undefined) {\n repr = 'undefined';\n } else {\n try {\n repr = JSON.stringify(value);\n } catch {\n repr = String(value);\n }\n }\n parts.push(`${key}=${repr}`);\n }\n return parts.join(' ');\n}\n\nexport interface DebugLogger {\n debug: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n /** Structured log with key=value metadata (long strings are truncated). */\n event: (event: string, meta?: Record<string, unknown>) => void;\n}\n\nfunction noop(): void {}\n\nconst NOOP_LOGGER: DebugLogger = {\n debug: noop,\n info: noop,\n warn: noop,\n event: noop,\n};\n\nfunction makePrefix(namespace: string, level: string): string {\n return `[${timestamp()}] [${namespace}] [${level}]`;\n}\n\n/** Build a writing logger for an enabled namespace. */\nfunction buildLogger(\n namespace: string,\n write: (line: string) => void = writeDebugLine,\n): DebugLogger {\n return {\n debug: (...args: unknown[]) => {\n write(`${makePrefix(namespace, 'DEBUG')} ${formatMessage(...args)}\\n`);\n },\n info: (...args: unknown[]) => {\n write(`${makePrefix(namespace, 'INFO')} ${formatMessage(...args)}\\n`);\n },\n warn: (...args: unknown[]) => {\n write(`${makePrefix(namespace, 'WARN')} ${formatMessage(...args)}\\n`);\n },\n event: (event, meta) => {\n const suffix = meta && Object.keys(meta).length > 0 ? ` ${formatMeta(meta)}` : '';\n write(`${makePrefix(namespace, 'DEBUG')} ${event}${suffix}\\n`);\n },\n };\n}\n\n/**\n * Create a debug logger gated by `BOOK_DEBUG=1`. Returns a no-op logger when\n * debug is off, so callers can call freely without env checks at call sites.\n */\nexport function createDebugLogger(namespace: string): DebugLogger {\n if (!DEBUG_ENABLED) return NOOP_LOGGER;\n return buildLogger(namespace);\n}\n\n/**\n * Like {@link createDebugLogger} but emits a monotonic event id per namespace\n * in every message, e.g. `[tui:input] [DEBUG#42] key=Escape`. Helpful when\n * tracing event ordering across concurrent sources.\n *\n * Activation matches {@link createDebugLogger}: gated by `BOOK_DEBUG=1`.\n */\nexport function createDebugLoggerWithCounter(namespace: string): DebugLogger {\n if (!DEBUG_ENABLED) return NOOP_LOGGER;\n const wrap =\n (level: 'debug' | 'info' | 'warn') =>\n (...args: unknown[]) => {\n const id = nextEventId(namespace);\n writeDebugLine(\n `[${timestamp()}] [${namespace}] [${level.toUpperCase()}#${id}] ${formatMessage(...args)}\\n`,\n );\n };\n return {\n debug: wrap('debug'),\n info: wrap('info'),\n warn: wrap('warn'),\n event: (event, meta) => {\n const id = nextEventId(namespace);\n const suffix = meta && Object.keys(meta).length > 0 ? ` ${formatMeta(meta)}` : '';\n writeDebugLine(`[${timestamp()}] [${namespace}] [DEBUG#${id}] ${event}${suffix}\\n`);\n },\n };\n}\n\n/**\n * Create a debug logger gated by `BOOK_DEBUG_UI=1` (also active when\n * `BOOK_DEBUG=1`). No-op otherwise. Used for input, modal, permission, and\n * mount/unmount events.\n */\nexport function createUiDebugLogger(namespace: string): DebugLogger {\n if (!UI_DEBUG_ENABLED) return NOOP_LOGGER;\n return buildLogger(namespace);\n}\n\n/**\n * Create a debug logger gated by `BOOK_DEBUG_RENDER=1` only. No-op otherwise.\n * Used for noisy per-render logs. Render logs must be opt-in to keep stderr\n * readable during streaming.\n */\nexport function createRenderDebugLogger(namespace: string): DebugLogger {\n if (!RENDER_DEBUG_ENABLED) return NOOP_LOGGER;\n return buildLogger(namespace, writeRenderDebugLine);\n}\n\n/**\n * Create a debug logger gated by `BOOK_DEBUG_FLOW=1` only. No-op otherwise.\n * Used for enter/exit trace pairs around expensive flows.\n */\nexport function createFlowDebugLogger(namespace: string): DebugLogger {\n if (!FLOW_DEBUG_ENABLED) return NOOP_LOGGER;\n return buildLogger(namespace);\n}\n\n/** Throttle a callback to at most one invocation per `intervalMs`. */\nexport function throttle<A extends unknown[]>(\n fn: (...args: A) => void,\n intervalMs: number,\n): (...args: A) => void {\n let last = 0;\n return (...args: A) => {\n const now = Date.now();\n if (now - last < intervalMs) return;\n last = now;\n fn(...args);\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseFrontmatter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KXAYUIKP.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveBookHome
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZPSZZUWF.js";
|
|
7
7
|
|
|
8
8
|
// src/skills.ts
|
|
9
9
|
import {
|
|
@@ -623,4 +623,4 @@ export {
|
|
|
623
623
|
generateSkillListing,
|
|
624
624
|
explicitSkillMentions
|
|
625
625
|
};
|
|
626
|
-
//# sourceMappingURL=chunk-
|
|
626
|
+
//# sourceMappingURL=chunk-67UZEWWF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/skills.ts"],"sourcesContent":["import {\n closeSync,\n existsSync,\n lstatSync,\n openSync,\n readFileSync,\n readSync,\n readdirSync,\n realpathSync,\n statSync,\n} from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve, sep } from 'node:path';\nimport { parseFrontmatter } from './frontmatter.js';\nimport { resolveBookHome } from './book-home.js';\nimport type { SkillActivation, SkillExecution } from './settings.js';\n\nexport const SKILL_ENTRY_FILENAME = 'SKILL.md';\nexport const MAX_SKILL_HEADER_BYTES = 64 * 1024;\nexport const MAX_SKILL_BODY_BYTES = 100 * 1024;\nexport const MAX_SKILL_RESOURCES = 200;\nexport const MAX_SKILL_RESOURCE_DEPTH = 4;\nexport const MAX_SKILL_RESOURCE_BYTES = 2 * 1024 * 1024;\n\nconst SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst KNOWN_FRONTMATTER_FIELDS = new Set([\n 'name',\n 'description',\n 'license',\n 'compatibility',\n 'version',\n 'metadata',\n 'allowed-tools',\n 'tools',\n 'when_to_use',\n 'model',\n 'lifetime',\n 'disabled',\n // Interoperable Claude/Codex skill metadata. Invocation controls are\n // advisory to Book's explicit `$name` UI and do not grant permissions.\n 'disable-model-invocation',\n 'user-invocable',\n 'argument-hint',\n]);\n\nexport type SkillSource = 'user' | 'project';\nexport type SkillRootKind = 'book' | 'agents' | 'claude' | 'opencode';\nexport type SkillIssueSeverity = 'error' | 'warning';\n\nexport interface SkillIssue {\n code: string;\n message: string;\n severity: SkillIssueSeverity;\n}\n\nexport interface SkillResource {\n relativePath: string;\n byteSize: number;\n mtimeMs: number;\n digest: string;\n}\n\nexport interface ShadowedSkill {\n path: string;\n source: SkillSource;\n rootKind: SkillRootKind;\n}\n\n/** Metadata discovered without retaining or injecting the SKILL.md body. */\nexport interface Skill {\n id: string;\n version: string;\n descriptorDigest: string;\n resourceDigest: string;\n name: string;\n description: string;\n whenToUse?: string;\n license?: string;\n compatibility?: string;\n metadata: Record<string, string>;\n /** Requested tool ceiling. It never grants permission by itself. */\n allowedTools?: string[];\n model?: string;\n lifetime: 'turn' | 'run';\n /** Absolute path to the skill entrypoint. */\n path: string;\n /** Absolute directory that owns the skill and its resources. */\n rootPath: string;\n source: SkillSource;\n rootKind: SkillRootKind;\n activation: SkillActivation;\n execution: SkillExecution;\n invocationCount: number;\n entryByteSize: number;\n entryMtimeMs: number;\n resources: SkillResource[];\n issues: SkillIssue[];\n valid: boolean;\n shadowed: ShadowedSkill[];\n}\n\nexport interface LoadedSkillBody {\n body: string;\n digest: string;\n byteSize: number;\n}\n\nexport interface SkillRoot {\n path: string;\n source: SkillSource;\n kind: SkillRootKind;\n}\n\nexport interface DiscoverSkillsOptions {\n homeDir?: string;\n bookHomeDir?: string;\n includeUser?: boolean;\n projectRoot?: string;\n includeOpenCode?: boolean;\n enabled?: boolean;\n executionOverrides?: Readonly<Record<string, SkillExecution>>;\n}\n\nfunction pathKey(path: string): string {\n return process.platform === 'win32' ? resolve(path).toLowerCase() : resolve(path);\n}\n\nfunction isInside(root: string, candidate: string): boolean {\n const rel = relative(root, candidate);\n return rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel));\n}\n\nfunction projectRootFor(workspace: string): string {\n let current = resolve(workspace);\n const filesystemRoot = parse(current).root;\n while (true) {\n if (existsSync(join(current, '.git'))) return current;\n if (current === filesystemRoot) return resolve(workspace);\n current = dirname(current);\n }\n}\n\nfunction directoriesFromRoot(root: string, workspace: string): string[] {\n const normalizedRoot = resolve(root);\n const normalizedWorkspace = resolve(workspace);\n if (!isInside(normalizedRoot, normalizedWorkspace)) return [normalizedWorkspace];\n const directories: string[] = [];\n let current = normalizedWorkspace;\n while (true) {\n directories.push(current);\n if (pathKey(current) === pathKey(normalizedRoot)) break;\n current = dirname(current);\n }\n return directories.reverse();\n}\n\n/** Roots are ordered from lowest to highest precedence. */\nexport function skillRoots(workspace: string, options: DiscoverSkillsOptions = {}): SkillRoot[] {\n const roots: SkillRoot[] = [];\n if (options.includeUser !== false) {\n const home = options.homeDir ?? homedir();\n const bookHome =\n options.bookHomeDir ?? (options.homeDir ? join(home, '.book') : resolveBookHome());\n roots.push(\n { path: join(home, '.claude', 'skills'), source: 'user', kind: 'claude' },\n { path: join(home, '.agents', 'skills'), source: 'user', kind: 'agents' },\n ...(options.includeOpenCode === false\n ? []\n : [\n {\n path: join(home, '.config', 'opencode', 'skills'),\n source: 'user' as const,\n kind: 'opencode' as const,\n },\n ]),\n { path: join(bookHome, 'skills'), source: 'user', kind: 'book' },\n );\n }\n\n const projectRoot = options.projectRoot ?? projectRootFor(workspace);\n for (const directory of directoriesFromRoot(projectRoot, workspace)) {\n roots.push(\n { path: join(directory, '.claude', 'skills'), source: 'project', kind: 'claude' },\n { path: join(directory, '.agents', 'skills'), source: 'project', kind: 'agents' },\n ...(options.includeOpenCode === false\n ? []\n : [\n {\n path: join(directory, '.opencode', 'skills'),\n source: 'project' as const,\n kind: 'opencode' as const,\n },\n ]),\n { path: join(directory, '.book', 'skills'), source: 'project', kind: 'book' },\n );\n }\n const byPath = new Map<string, SkillRoot>();\n for (const root of roots) byPath.set(pathKey(root.path), root);\n return [...byPath.values()];\n}\n\nfunction readFrontmatterPrefix(path: string): {\n frontmatter: Record<string, unknown>;\n headerText: string;\n issues: SkillIssue[];\n} {\n const issues: SkillIssue[] = [];\n const fd = openSync(path, 'r');\n const chunks: Buffer[] = [];\n let total = 0;\n try {\n while (total < MAX_SKILL_HEADER_BYTES) {\n const chunk = Buffer.alloc(Math.min(4096, MAX_SKILL_HEADER_BYTES - total));\n const count = readSync(fd, chunk, 0, chunk.length, total);\n if (count === 0) break;\n chunks.push(chunk.subarray(0, count));\n total += count;\n const text = Buffer.concat(chunks).toString('utf8');\n if (/^---\\r?\\n[\\s\\S]*?\\r?\\n---(?:\\r?\\n|$)/.test(text)) {\n const match = text.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---(?:\\r?\\n|$)/);\n const headerText = match?.[0] ?? '';\n return { frontmatter: parseFrontmatter(headerText).frontmatter, headerText, issues };\n }\n }\n } finally {\n closeSync(fd);\n }\n issues.push({\n code: 'invalid_frontmatter',\n message: `Missing YAML frontmatter or closing delimiter within ${MAX_SKILL_HEADER_BYTES} bytes.`,\n severity: 'error',\n });\n return { frontmatter: {}, headerText: '', issues };\n}\n\nfunction stringField(frontmatter: Record<string, unknown>, name: string): string | undefined {\n const value = frontmatter[name];\n return typeof value === 'string' && value.trim() ? value.trim() : undefined;\n}\n\nfunction metadataFromHeader(headerText: string): Record<string, string> {\n const lines = headerText.split(/\\r?\\n/);\n const metadata: Record<string, string> = {};\n const start = lines.findIndex((line) => /^metadata\\s*:\\s*$/.test(line.trim()));\n if (start < 0) return metadata;\n for (const line of lines.slice(start + 1)) {\n if (/^---\\s*$/.test(line)) break;\n if (line.trim() && !/^\\s+/.test(line)) break;\n const match = line.match(/^\\s+([A-Za-z0-9_.-]+)\\s*:\\s*(.*?)\\s*$/);\n if (!match) continue;\n metadata[match[1]] = match[2].replace(/^[\"'](.*)[\"']$/, '$1');\n }\n return metadata;\n}\n\nfunction allowedToolsFrom(frontmatter: Record<string, unknown>): string[] | undefined {\n const value = frontmatter['allowed-tools'] ?? frontmatter.tools;\n if (Array.isArray(value)) {\n const tools = value\n .map(String)\n .map((tool) => tool.trim())\n .filter(Boolean);\n return tools.length ? tools : undefined;\n }\n if (typeof value === 'string') {\n const tools = value.match(/[^\\s]+(?:\\([^)]*\\))?/g)?.map((tool) => tool.trim()) ?? [];\n return tools.length ? tools : undefined;\n }\n return undefined;\n}\n\nfunction collectResources(\n rootPath: string,\n entryPath: string,\n): {\n resources: SkillResource[];\n issues: SkillIssue[];\n} {\n const resources: SkillResource[] = [];\n const issues: SkillIssue[] = [];\n let depthLimitReported = false;\n const visit = (directory: string, depth: number): void => {\n if (depth > MAX_SKILL_RESOURCE_DEPTH) {\n if (!depthLimitReported) {\n depthLimitReported = true;\n issues.push({\n code: 'resource_depth_limit',\n message: `Resources deeper than ${MAX_SKILL_RESOURCE_DEPTH} directories are ignored.`,\n severity: 'warning',\n });\n }\n return;\n }\n if (resources.length >= MAX_SKILL_RESOURCES) return;\n let entries: string[];\n try {\n entries = readdirSync(directory).sort();\n } catch (error) {\n issues.push({\n code: 'resource_unreadable',\n message: `Could not inspect resources in ${directory}: ${error instanceof Error ? error.message : String(error)}`,\n severity: 'warning',\n });\n return;\n }\n for (const entry of entries) {\n if (resources.length >= MAX_SKILL_RESOURCES) break;\n const fullPath = join(directory, entry);\n if (pathKey(fullPath) === pathKey(entryPath)) continue;\n let info;\n try {\n info = lstatSync(fullPath);\n } catch {\n continue;\n }\n if (info.isSymbolicLink()) {\n issues.push({\n code: 'resource_symlink_ignored',\n message: `Ignored symbolic-link resource: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n continue;\n }\n if (info.isDirectory()) {\n visit(fullPath, depth + 1);\n continue;\n }\n if (!info.isFile()) continue;\n let digest = createHash('sha256')\n .update(`unavailable:${info.size}:${info.mtimeMs}`)\n .digest('hex');\n if (info.size > MAX_SKILL_RESOURCE_BYTES) {\n issues.push({\n code: 'resource_too_large',\n message: `Resource exceeds ${MAX_SKILL_RESOURCE_BYTES} bytes: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n }\n try {\n const fd = openSync(fullPath, 'r');\n try {\n const prefix = Buffer.alloc(Math.min(info.size, 8192));\n const read = readSync(fd, prefix, 0, prefix.length, 0);\n if (prefix.subarray(0, read).includes(0)) {\n issues.push({\n code: 'binary_resource',\n message: `Binary resource cannot be loaded into model context: ${relative(rootPath, fullPath)}`,\n severity: 'warning',\n });\n }\n } finally {\n closeSync(fd);\n }\n } catch (error) {\n issues.push({\n code: 'resource_unreadable',\n message: `Could not validate resource ${relative(rootPath, fullPath)}: ${error instanceof Error ? error.message : String(error)}`,\n severity: 'warning',\n });\n }\n if (info.size <= MAX_SKILL_RESOURCE_BYTES) {\n try {\n digest = createHash('sha256').update(readFileSync(fullPath)).digest('hex');\n } catch {\n // The visible unreadable-resource issue above is the actionable diagnostic.\n }\n }\n resources.push({\n relativePath: relative(rootPath, fullPath).split(sep).join('/'),\n byteSize: info.size,\n mtimeMs: info.mtimeMs,\n digest,\n });\n }\n };\n visit(rootPath, 1);\n if (resources.length >= MAX_SKILL_RESOURCES) {\n issues.push({\n code: 'resource_limit',\n message: `Only the first ${MAX_SKILL_RESOURCES} resources are available.`,\n severity: 'warning',\n });\n }\n return { resources, issues };\n}\n\nfunction descriptorFromEntry(\n entryPath: string,\n root: SkillRoot,\n directoryName: string,\n flat = false,\n): Skill {\n const { frontmatter, headerText, issues: headerIssues } = readFrontmatterPrefix(entryPath);\n const issues = [...headerIssues];\n const declaredName = stringField(frontmatter, 'name');\n const name = declaredName ?? directoryName;\n const description = stringField(frontmatter, 'description') ?? '';\n\n if (!SKILL_NAME_PATTERN.test(name) || name.length > 64) {\n issues.push({\n code: 'invalid_name',\n message: 'Skill names must be 1-64 lowercase letters, numbers, or single hyphens.',\n severity: 'error',\n });\n }\n if (!flat && declaredName !== directoryName) {\n issues.push({\n code: 'name_directory_mismatch',\n message: `Declared name \"${declaredName ?? '(missing)'}\" must match directory \"${directoryName}\".`,\n severity: 'error',\n });\n }\n if (!declaredName && flat) {\n issues.push({\n code: 'legacy_flat_skill',\n message: 'Flat .skill.md files are supported for compatibility; use <name>/SKILL.md.',\n severity: 'warning',\n });\n }\n if (!description || description.length > 1024) {\n issues.push({\n code: 'invalid_description',\n message: 'Skill description is required and must be at most 1024 characters.',\n severity: 'error',\n });\n }\n const compatibility = stringField(frontmatter, 'compatibility');\n if (compatibility && compatibility.length > 500) {\n issues.push({\n code: 'invalid_compatibility',\n message: 'Skill compatibility must be at most 500 characters.',\n severity: 'error',\n });\n }\n const whenToUse = stringField(frontmatter, 'when_to_use');\n if (whenToUse && whenToUse.length > 1024) {\n issues.push({\n code: 'invalid_when_to_use',\n message: 'Skill when_to_use must be at most 1024 characters.',\n severity: 'error',\n });\n }\n const model = stringField(frontmatter, 'model');\n if (model) {\n issues.push({\n code: 'unsupported_model_hint',\n message: 'The model field is retained for compatibility but does not change Book models.',\n severity: 'warning',\n });\n }\n const requestedLifetime = stringField(frontmatter, 'lifetime');\n if (requestedLifetime && requestedLifetime !== 'run' && requestedLifetime !== 'turn') {\n issues.push({\n code: 'invalid_lifetime',\n message: 'Skill lifetime must be run or turn; Book will use run.',\n severity: 'warning',\n });\n }\n const lifetime = requestedLifetime === 'turn' ? 'turn' : 'run';\n for (const field of Object.keys(frontmatter)) {\n if (!KNOWN_FRONTMATTER_FIELDS.has(field)) {\n issues.push({\n code: 'unknown_frontmatter_field',\n message: `Unknown frontmatter field: ${field}`,\n severity: 'warning',\n });\n }\n }\n\n let entryByteSize = 0;\n let entryMtimeMs = 0;\n try {\n const entryInfo = statSync(entryPath);\n entryByteSize = entryInfo.size;\n entryMtimeMs = entryInfo.mtimeMs;\n if (entryByteSize > MAX_SKILL_BODY_BYTES + MAX_SKILL_HEADER_BYTES) {\n issues.push({\n code: 'body_too_large',\n message: `SKILL.md exceeds the ${MAX_SKILL_BODY_BYTES}-byte body budget.`,\n severity: 'error',\n });\n }\n } catch {\n issues.push({\n code: 'skill_unreadable',\n message: 'Skill entrypoint is unreadable.',\n severity: 'error',\n });\n }\n\n const rootPath = dirname(entryPath);\n const resourceResult = flat\n ? { resources: [] as SkillResource[], issues: [] as SkillIssue[] }\n : collectResources(rootPath, entryPath);\n issues.push(...resourceResult.issues);\n\n const metadata = metadataFromHeader(headerText);\n const version = stringField(frontmatter, 'version') ?? metadata.version ?? 'unversioned';\n const resourceDigest = createHash('sha256')\n .update(JSON.stringify(resourceResult.resources))\n .digest('hex');\n const descriptorDigest = createHash('sha256')\n .update(\n JSON.stringify({\n name,\n description,\n whenToUse,\n version,\n source: root.source,\n rootKind: root.kind,\n path: pathKey(entryPath),\n allowedTools: allowedToolsFrom(frontmatter),\n lifetime,\n resourceDigest,\n }),\n )\n .digest('hex');\n\n return {\n id: `${root.kind}:${pathKey(entryPath)}`,\n version,\n descriptorDigest,\n resourceDigest,\n name,\n description,\n whenToUse,\n license: stringField(frontmatter, 'license'),\n compatibility,\n metadata,\n allowedTools: allowedToolsFrom(frontmatter),\n model,\n lifetime,\n path: entryPath,\n rootPath,\n source: root.source,\n rootKind: root.kind,\n activation: String(frontmatter.disabled).toLowerCase() === 'true' ? 'off' : 'manual',\n execution: 'inherit',\n invocationCount: 0,\n entryByteSize,\n entryMtimeMs,\n resources: resourceResult.resources,\n issues,\n valid: !issues.some((issue) => issue.severity === 'error'),\n shadowed: [],\n };\n}\n\nfunction skillsUnderRoot(root: SkillRoot): Skill[] {\n if (!existsSync(root.path)) return [];\n let entries: string[];\n try {\n entries = readdirSync(root.path).sort();\n } catch {\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n const fullPath = join(root.path, entry);\n let info;\n try {\n info = lstatSync(fullPath);\n } catch {\n continue;\n }\n let directory = info.isDirectory();\n if (info.isSymbolicLink()) {\n try {\n directory = statSync(fullPath).isDirectory();\n } catch {\n continue;\n }\n }\n if (directory) {\n const entryPath = join(fullPath, SKILL_ENTRY_FILENAME);\n if (existsSync(entryPath) && statSync(entryPath).isFile()) {\n skills.push(descriptorFromEntry(entryPath, root, entry));\n }\n } else if (info.isFile() && entry.endsWith('.skill.md')) {\n skills.push(descriptorFromEntry(fullPath, root, entry.replace(/\\.skill\\.md$/, ''), true));\n }\n }\n return skills;\n}\n\nexport function discoverSkills(\n workspace: string,\n overrides: Readonly<Record<string, SkillActivation>> = {},\n options: DiscoverSkillsOptions = {},\n): Skill[] {\n const byName = new Map<string, Skill>();\n for (const root of skillRoots(workspace, options)) {\n for (const skill of skillsUnderRoot(root)) {\n const previous = byName.get(skill.name);\n if (previous) {\n skill.shadowed = [\n ...previous.shadowed,\n { path: previous.path, source: previous.source, rootKind: previous.rootKind },\n ];\n }\n byName.set(skill.name, skill);\n }\n }\n return applySkillOverrides(\n [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)),\n overrides,\n options.executionOverrides ?? {},\n options.enabled ?? true,\n );\n}\n\nexport function applySkillOverrides(\n skills: readonly Skill[],\n overrides: Readonly<Record<string, SkillActivation>>,\n executionOverrides: Readonly<Record<string, SkillExecution>> = {},\n enabled = true,\n): Skill[] {\n return skills.map((skill) => ({\n ...skill,\n activation: enabled ? (overrides[skill.name] ?? skill.activation ?? 'manual') : 'off',\n execution: executionOverrides[skill.name] ?? skill.execution ?? 'inherit',\n }));\n}\n\nexport function loadSkillBody(skill: Skill): LoadedSkillBody {\n if (!skill.valid) throw new Error(`Skill \"${skill.name}\" is invalid.`);\n const canonicalRoot = realpathSync(skill.rootPath);\n const canonicalEntry = realpathSync(skill.path);\n if (!isInside(canonicalRoot, canonicalEntry)) {\n throw new Error(`Skill entrypoint escapes its root: ${skill.path}`);\n }\n const current = statSync(canonicalEntry);\n if (current.size !== skill.entryByteSize || current.mtimeMs !== skill.entryMtimeMs) {\n throw new Error(`Skill \"${skill.name}\" changed after discovery; reload skills before use.`);\n }\n const buffer = readFileSync(canonicalEntry);\n if (buffer.includes(0)) {\n throw new Error(`Skill \"${skill.name}\" contains binary content.`);\n }\n const raw = buffer.toString('utf8');\n const { body } = parseFrontmatter(raw);\n const byteSize = Buffer.byteLength(body);\n if (byteSize > MAX_SKILL_BODY_BYTES) {\n throw new Error(`Skill body exceeds ${MAX_SKILL_BODY_BYTES} bytes.`);\n }\n return {\n body,\n digest: createHash('sha256').update(body).digest('hex'),\n byteSize,\n };\n}\n\nexport interface SkillListingResult {\n text: string;\n budgetChars: number;\n visibleCount: number;\n included: string[];\n omitted: string[];\n collapsed: string[];\n charCount: number;\n}\n\n/**\n * Characters the skill listing may occupy in the system prompt.\n *\n * Lives here so the listing shipped to the model and the /skills diagnostics\n * that describe it cannot drift: both call this, neither re-derives the share\n * or the cap.\n */\nexport function skillListingBudgetChars(contextWindow: number): number {\n return Math.min(8000, Math.max(512, Math.floor(contextWindow * 0.08)));\n}\n\nexport function buildSkillListing(\n skills: readonly Skill[],\n budgetChars = 8000,\n): SkillListingResult {\n const visible = skills.filter(\n (skill) => skill.valid && (skill.activation === 'auto' || skill.activation === 'name-only'),\n );\n if (visible.length === 0) {\n return {\n text: '',\n budgetChars,\n visibleCount: 0,\n included: [],\n omitted: [],\n collapsed: [],\n charCount: 0,\n };\n }\n\n const sorted = [...visible].sort(\n (a, b) => b.invocationCount - a.invocationCount || a.name.localeCompare(b.name),\n );\n const lines = [\n '## Available skills',\n 'Use InvokeSkill when a request matches a description. A user can force exact use with `$skill-name`.',\n ];\n let remainingBudget = budgetChars - lines.join('\\n').length;\n const includedNames: string[] = [];\n const collapsedNames = new Set<string>();\n\n let included = 0;\n for (const skill of sorted) {\n if (skill.activation === 'name-only') {\n const bare = `- **${skill.name}**`;\n if (bare.length > remainingBudget) break;\n lines.push(bare);\n remainingBudget -= bare.length + 1;\n included++;\n includedNames.push(skill.name);\n collapsedNames.add(skill.name);\n continue;\n }\n const entry = `- **${skill.name}**: ${skill.description}`;\n const trigger = skill.whenToUse ? `\\n when: ${skill.whenToUse}` : '';\n const full = `${entry}${trigger}`;\n if (full.length > remainingBudget) {\n const bare = `- **${skill.name}**`;\n if (bare.length <= remainingBudget) {\n lines.push(bare);\n included++;\n includedNames.push(skill.name);\n collapsedNames.add(skill.name);\n }\n break;\n }\n lines.push(full);\n remainingBudget -= full.length + 1;\n included++;\n includedNames.push(skill.name);\n }\n let omitted = sorted.length - included;\n if (omitted > 0) {\n let warning = `- ${omitted} additional skill${omitted === 1 ? ' was' : 's were'} omitted from this prompt budget. Use /skills to inspect the full catalog.`;\n while (warning.length > remainingBudget && lines.length > 2) {\n const removed = lines.pop();\n if (!removed) break;\n remainingBudget += removed.length + 1;\n included--;\n const removedName = includedNames.pop();\n if (removedName) collapsedNames.delete(removedName);\n omitted = sorted.length - included;\n warning = `- ${omitted} additional skill${omitted === 1 ? ' was' : 's were'} omitted from this prompt budget. Use /skills to inspect the full catalog.`;\n }\n if (warning.length <= remainingBudget) lines.push(warning);\n }\n const text = lines.join('\\n');\n const includedSet = new Set(includedNames);\n return {\n text,\n budgetChars,\n visibleCount: sorted.length,\n included: includedNames,\n omitted: sorted.filter((skill) => !includedSet.has(skill.name)).map((skill) => skill.name),\n collapsed: includedNames.filter((name) => collapsedNames.has(name)),\n charCount: text.length,\n };\n}\n\nexport function generateSkillListing(skills: readonly Skill[], budgetChars = 8000): string {\n return buildSkillListing(skills, budgetChars).text;\n}\n\nexport function explicitSkillMentions(prompt: string, skills: readonly Skill[]): string[] {\n const available = new Set(skills.map((skill) => skill.name));\n const mentions: string[] = [];\n const pattern = /(?<![A-Za-z0-9_-])\\$([a-z0-9]+(?:-[a-z0-9]+)*)(?![A-Za-z0-9_-])/g;\n for (const match of prompt.matchAll(pattern)) {\n const name = match[1];\n if (available.has(name) && !mentions.includes(name)) mentions.push(name);\n }\n return mentions;\n}\n"],"mappings":"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","names":[]}
|