@intentic/claude-plugin 1.317.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/.claude-plugin/plugin.json +82 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/bin/fileq +9 -0
- package/bin/intentic-notes-evidence +3 -0
- package/bin/intentic-stats +3 -0
- package/bin/retrieve-output +3 -0
- package/dist/fileq.mjs +147667 -0
- package/dist/notes-evidence.mjs +749 -0
- package/dist/post-bash.mjs +927 -0
- package/dist/post-edit.mjs +252 -0
- package/dist/prompt-submit.mjs +220 -0
- package/dist/retrieve-output.mjs +38 -0
- package/dist/session-start.mjs +1658 -0
- package/dist/stats.mjs +1847 -0
- package/generated/iq-nudge.txt +3 -0
- package/generated/output-styles/intentic.md +13 -0
- package/generated/skills/field-notes/SKILL.md +40 -0
- package/generated/skills/fileq/SKILL.md +56 -0
- package/generated/skills/iq/SKILL.md +38 -0
- package/generated/skills/stats/SKILL.md +11 -0
- package/hooks/hooks.json +49 -0
- package/package.json +60 -0
|
@@ -0,0 +1,1658 @@
|
|
|
1
|
+
import { createRequire as __intenticRequire } from 'node:module'; const require = __intenticRequire(import.meta.url);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
throw mod = 0, e;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
|
|
32
|
+
// ../../node_modules/.pnpm/ignore@7.0.10/node_modules/ignore/index.js
|
|
33
|
+
var require_ignore = __commonJS({
|
|
34
|
+
"../../node_modules/.pnpm/ignore@7.0.10/node_modules/ignore/index.js"(exports, module) {
|
|
35
|
+
function makeArray(subject) {
|
|
36
|
+
return Array.isArray(subject) ? subject : [subject];
|
|
37
|
+
}
|
|
38
|
+
var UNDEFINED = void 0;
|
|
39
|
+
var EMPTY = "";
|
|
40
|
+
var SPACE = " ";
|
|
41
|
+
var ESCAPE = "\\";
|
|
42
|
+
var REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/;
|
|
43
|
+
var REGEX_TEST_BLANK_LINE = /^\uFEFF? *$/;
|
|
44
|
+
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
45
|
+
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
46
|
+
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
47
|
+
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
48
|
+
var DOUBLE_SLASH = "//";
|
|
49
|
+
var SLASH_CODE = 47;
|
|
50
|
+
var DOT_CODE = 46;
|
|
51
|
+
var SLASH = "/";
|
|
52
|
+
var TMP_KEY_IGNORE = "node-ignore";
|
|
53
|
+
if (typeof Symbol !== "undefined") {
|
|
54
|
+
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
55
|
+
}
|
|
56
|
+
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
57
|
+
var define = (object, key, value) => {
|
|
58
|
+
Object.defineProperty(object, key, { value });
|
|
59
|
+
return value;
|
|
60
|
+
};
|
|
61
|
+
var RETURN_FALSE = () => false;
|
|
62
|
+
var cleanRangeBackSlash = (slashes) => {
|
|
63
|
+
const { length } = slashes;
|
|
64
|
+
return slashes.slice(0, length - length % 2);
|
|
65
|
+
};
|
|
66
|
+
var POSIX_CLASSES = {
|
|
67
|
+
alnum: "0-9A-Za-z",
|
|
68
|
+
alpha: "A-Za-z",
|
|
69
|
+
blank: " \\t",
|
|
70
|
+
cntrl: "\\x00-\\x1f\\x7f",
|
|
71
|
+
digit: "0-9",
|
|
72
|
+
graph: "!-.0-~",
|
|
73
|
+
lower: "a-z",
|
|
74
|
+
print: " -.0-~",
|
|
75
|
+
punct: "!-.:-@\\[-`{-~",
|
|
76
|
+
// git's `sane-ctype.h` classifies \v and \f as control, not space,
|
|
77
|
+
// unlike C's `isspace`
|
|
78
|
+
space: " \\t\\n\\r",
|
|
79
|
+
upper: "A-Z",
|
|
80
|
+
xdigit: "0-9A-Fa-f"
|
|
81
|
+
};
|
|
82
|
+
var CLASS_MEMBERS_TO_ESCAPE = "\\]^-[";
|
|
83
|
+
var escapeMember = (char) => CLASS_MEMBERS_TO_ESCAPE.indexOf(char) < 0 ? char : ESCAPE + char;
|
|
84
|
+
var NON_SLASH = "(?!\\/)";
|
|
85
|
+
var classSource = (negated, body) => {
|
|
86
|
+
if (negated) {
|
|
87
|
+
return `[^\\/${body}]`;
|
|
88
|
+
}
|
|
89
|
+
const source = `[${body}]`;
|
|
90
|
+
return new RegExp(source).test("/") ? NON_SLASH + source : source;
|
|
91
|
+
};
|
|
92
|
+
var scanBracket = (pattern, start) => {
|
|
93
|
+
const { length } = pattern;
|
|
94
|
+
let index = start + 1;
|
|
95
|
+
let negated = EMPTY;
|
|
96
|
+
const lead = pattern[index];
|
|
97
|
+
if (lead === "!" || lead === "^") {
|
|
98
|
+
negated = "^";
|
|
99
|
+
index++;
|
|
100
|
+
}
|
|
101
|
+
let body = EMPTY;
|
|
102
|
+
let prev = EMPTY;
|
|
103
|
+
for (; ; ) {
|
|
104
|
+
const char = pattern[index];
|
|
105
|
+
if (char === UNDEFINED) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (char === ESCAPE) {
|
|
109
|
+
const escaped = pattern[index + 1];
|
|
110
|
+
if (escaped === UNDEFINED) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
body += escapeMember(escaped);
|
|
114
|
+
prev = escaped;
|
|
115
|
+
index++;
|
|
116
|
+
} else if (char === "-" && prev && index + 1 < length && pattern[index + 1] !== "]") {
|
|
117
|
+
index++;
|
|
118
|
+
let to = pattern[index];
|
|
119
|
+
if (to === ESCAPE) {
|
|
120
|
+
to = pattern[index += 1];
|
|
121
|
+
}
|
|
122
|
+
if (prev <= to) {
|
|
123
|
+
body += `-${escapeMember(to)}`;
|
|
124
|
+
}
|
|
125
|
+
prev = EMPTY;
|
|
126
|
+
} else if (char === "[" && pattern[index + 1] === ":") {
|
|
127
|
+
const nameStart = index + 2;
|
|
128
|
+
let end = nameStart;
|
|
129
|
+
while (end < length && pattern[end] !== "]") {
|
|
130
|
+
end++;
|
|
131
|
+
}
|
|
132
|
+
if (end === length) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (end > nameStart && pattern[end - 1] === ":") {
|
|
136
|
+
const expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)];
|
|
137
|
+
if (expanded === UNDEFINED) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
body += expanded;
|
|
141
|
+
prev = EMPTY;
|
|
142
|
+
index = end;
|
|
143
|
+
} else {
|
|
144
|
+
body += escapeMember("[");
|
|
145
|
+
prev = "[";
|
|
146
|
+
index = nameStart - 2;
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
body += escapeMember(char);
|
|
150
|
+
prev = char;
|
|
151
|
+
}
|
|
152
|
+
index++;
|
|
153
|
+
if (pattern[index] === "]") {
|
|
154
|
+
return {
|
|
155
|
+
end: index,
|
|
156
|
+
source: classSource(negated, body)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var NEVER_MATCH = "[]";
|
|
162
|
+
var PLACEHOLDER = "\0";
|
|
163
|
+
var REGEX_RESTORE_PLACEHOLDER = new RegExp(
|
|
164
|
+
`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`,
|
|
165
|
+
"g"
|
|
166
|
+
);
|
|
167
|
+
var TRAILING_WILDCARD = "\uE000";
|
|
168
|
+
var extractBrackets = (pattern) => {
|
|
169
|
+
const sources = [];
|
|
170
|
+
const hold = (source) => `${PLACEHOLDER}${sources.push(source) - 1}${PLACEHOLDER}`;
|
|
171
|
+
const { length } = pattern;
|
|
172
|
+
let out = EMPTY;
|
|
173
|
+
let index = 0;
|
|
174
|
+
while (index < length) {
|
|
175
|
+
const char = pattern[index];
|
|
176
|
+
if (char === ESCAPE) {
|
|
177
|
+
const escaped = pattern[index + 1];
|
|
178
|
+
if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) {
|
|
179
|
+
out += pattern.slice(index, index + 2);
|
|
180
|
+
} else {
|
|
181
|
+
out += hold(
|
|
182
|
+
REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : escaped
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
index += 2;
|
|
186
|
+
} else if (char === PLACEHOLDER) {
|
|
187
|
+
out += hold(`[${PLACEHOLDER}]`);
|
|
188
|
+
index++;
|
|
189
|
+
} else if (char === "[") {
|
|
190
|
+
const scanned = scanBracket(pattern, index);
|
|
191
|
+
if (scanned === null) {
|
|
192
|
+
out += hold(NEVER_MATCH);
|
|
193
|
+
index = length;
|
|
194
|
+
} else {
|
|
195
|
+
out += hold(scanned.source);
|
|
196
|
+
index = scanned.end + 1;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
out += char;
|
|
200
|
+
index++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
source: out,
|
|
205
|
+
sources
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
var DIRECT = null;
|
|
209
|
+
var REGEX_INNER_SLASH = /\/(?!$)/;
|
|
210
|
+
var REPLACERS = [
|
|
211
|
+
[
|
|
212
|
+
// Remove BOM
|
|
213
|
+
// TODO:
|
|
214
|
+
// Other similar zero-width characters?
|
|
215
|
+
/^\uFEFF/,
|
|
216
|
+
() => EMPTY,
|
|
217
|
+
"\uFEFF"
|
|
218
|
+
],
|
|
219
|
+
[
|
|
220
|
+
// A trailing line terminator, left on when a whole file's contents are
|
|
221
|
+
// added as one pattern rather than split into lines. git never sees one
|
|
222
|
+
// -- it reads a `.gitignore` line by line -- so it is not part of the
|
|
223
|
+
// pattern and is dropped here, apart from the trailing-space trimming,
|
|
224
|
+
// which follows git in touching spaces and nothing else.
|
|
225
|
+
/[\r\n]+$/,
|
|
226
|
+
() => EMPTY
|
|
227
|
+
],
|
|
228
|
+
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
229
|
+
[
|
|
230
|
+
// Only spaces, never tabs or other whitespace: git trims a trailing run
|
|
231
|
+
// of `' '` and nothing else (dir.c, `trim_trailing_spaces`, a single
|
|
232
|
+
// `case ' '`), so a pattern ending in a tab keeps it as a literal.
|
|
233
|
+
// (a\ ) -> (a )
|
|
234
|
+
// (a ) -> (a)
|
|
235
|
+
// (a ) -> (a)
|
|
236
|
+
// (a \ ) -> (a )
|
|
237
|
+
/((?:\\\\)*?)(\\? +)$/,
|
|
238
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
239
|
+
],
|
|
240
|
+
// Replace (\ ) with ' '
|
|
241
|
+
// Only a space: an escaped tab or other whitespace is already a literal by
|
|
242
|
+
// the time it reaches here, and a bare tab must be left as one, not turned
|
|
243
|
+
// into a space.
|
|
244
|
+
// (\ ) -> ' '
|
|
245
|
+
// (\\ ) -> '\\ '
|
|
246
|
+
// (\\\ ) -> '\\ '
|
|
247
|
+
[
|
|
248
|
+
/(\\+?) /g,
|
|
249
|
+
(_, m1) => {
|
|
250
|
+
const { length } = m1;
|
|
251
|
+
return m1.slice(0, length - length % 2) + SPACE;
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
// Escape metacharacters
|
|
255
|
+
// which is written down by users but means special for regular expressions.
|
|
256
|
+
// > There are 12 characters with special meanings:
|
|
257
|
+
// > - the backslash \,
|
|
258
|
+
// > - the caret ^,
|
|
259
|
+
// > - the dollar sign $,
|
|
260
|
+
// > - the period or dot .,
|
|
261
|
+
// > - the vertical bar or pipe symbol |,
|
|
262
|
+
// > - the question mark ?,
|
|
263
|
+
// > - the asterisk or star *,
|
|
264
|
+
// > - the plus sign +,
|
|
265
|
+
// > - the opening parenthesis (,
|
|
266
|
+
// > - the closing parenthesis ),
|
|
267
|
+
// > - and the opening square bracket [,
|
|
268
|
+
// > - the opening curly brace {,
|
|
269
|
+
// > These special characters are often called "metacharacters".
|
|
270
|
+
[
|
|
271
|
+
/[\\$.|*+(){^]/g,
|
|
272
|
+
(match) => `\\${match}`
|
|
273
|
+
],
|
|
274
|
+
[
|
|
275
|
+
// > a question mark (?) matches a single character
|
|
276
|
+
/(?!\\)\?/g,
|
|
277
|
+
() => "[^/]",
|
|
278
|
+
"?"
|
|
279
|
+
],
|
|
280
|
+
// leading slash
|
|
281
|
+
[
|
|
282
|
+
// > A leading slash matches the beginning of the pathname.
|
|
283
|
+
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
284
|
+
// A leading slash matches the beginning of the pathname
|
|
285
|
+
/^\//,
|
|
286
|
+
() => "^",
|
|
287
|
+
SLASH
|
|
288
|
+
],
|
|
289
|
+
// replace special metacharacter slash after the leading slash
|
|
290
|
+
[
|
|
291
|
+
/\//g,
|
|
292
|
+
() => "\\/",
|
|
293
|
+
SLASH
|
|
294
|
+
],
|
|
295
|
+
[
|
|
296
|
+
// > A leading "**" followed by a slash means match in all directories.
|
|
297
|
+
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
298
|
+
// > the same as pattern "foo".
|
|
299
|
+
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
300
|
+
// > under directory "foo".
|
|
301
|
+
// Notice that the '*'s have been replaced as '\\*'
|
|
302
|
+
/^\^*(?:\\\*\\\*\\\/)+/,
|
|
303
|
+
// '**/foo' <-> 'foo'
|
|
304
|
+
() => "^(?:.*\\/)?",
|
|
305
|
+
"*"
|
|
306
|
+
],
|
|
307
|
+
// starting
|
|
308
|
+
[
|
|
309
|
+
// there will be no leading '/'
|
|
310
|
+
// (which has been replaced by section "leading slash")
|
|
311
|
+
// If starts with '**', adding a '^' to the regular expression also works
|
|
312
|
+
DIRECT,
|
|
313
|
+
(source, pattern) => {
|
|
314
|
+
if (!source || source[0] === "^") {
|
|
315
|
+
return source;
|
|
316
|
+
}
|
|
317
|
+
const anchor = !REGEX_INNER_SLASH.test(pattern) ? "(?:^|\\/)" : "^";
|
|
318
|
+
return anchor + source;
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
// two globstars
|
|
322
|
+
[
|
|
323
|
+
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
324
|
+
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
325
|
+
// Zero, one or several directories
|
|
326
|
+
// should not use '*', or it will be replaced by the next replacer
|
|
327
|
+
// Check if it is not the last `'/**'`
|
|
328
|
+
(_, index, str) => index + 6 < str.length ? str.slice(index + 6) === "\\/" ? "(?:\\/[^\\/]+)+" : "(?:\\/[^\\/]+)*" : "\\/.+",
|
|
329
|
+
"*"
|
|
330
|
+
],
|
|
331
|
+
// normal intermediate wildcards
|
|
332
|
+
[
|
|
333
|
+
// Never replace escaped '*'
|
|
334
|
+
// ignore rule '\*' will match the path '*'
|
|
335
|
+
// 'abc.*/' -> go
|
|
336
|
+
// 'abc.*' -> skip this rule,
|
|
337
|
+
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
338
|
+
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
339
|
+
// '*.js' matches '.js'
|
|
340
|
+
// '*.js' doesn't match 'abc'
|
|
341
|
+
(_, p1, p2) => {
|
|
342
|
+
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
343
|
+
return p1 + unescaped;
|
|
344
|
+
},
|
|
345
|
+
"*"
|
|
346
|
+
],
|
|
347
|
+
// trailing wildcard, held apart from a literal star
|
|
348
|
+
[
|
|
349
|
+
// The step above leaves a trailing `*` alone, so a single `\*` is all that
|
|
350
|
+
// can be left at the end here. Whether it is a wildcard or a literal
|
|
351
|
+
// turns on the backslashes the user put in front of it: the escaper has
|
|
352
|
+
// since doubled every one, so what stands here is those `2N` doubled
|
|
353
|
+
// backslashes and then the star's own escape. An even number of the
|
|
354
|
+
// original `N` leaves the star unescaped -- a wildcard -- and an odd
|
|
355
|
+
// number escapes it -- a literal. This runs while the two are still
|
|
356
|
+
// distinct, before the unescape steps below collapse the literal onto
|
|
357
|
+
// the very `\*` a wildcard leaves behind.
|
|
358
|
+
/(^|[^\\])((?:\\\\)*)\\\*$/,
|
|
359
|
+
(match, p1, p2) => (
|
|
360
|
+
// `p2` holds the doubled user backslashes; half of them is `N`.
|
|
361
|
+
p2.length / 2 % 2 === 0 ? p1 + p2 + TRAILING_WILDCARD : match
|
|
362
|
+
),
|
|
363
|
+
"*"
|
|
364
|
+
],
|
|
365
|
+
[
|
|
366
|
+
// unescape, revert step 3 except for back slash
|
|
367
|
+
// For example, if a user escape a '\\*',
|
|
368
|
+
// after step 3, the result will be '\\\\\\*'
|
|
369
|
+
/\\\\\\(?=[$.|*+(){^])/g,
|
|
370
|
+
() => ESCAPE,
|
|
371
|
+
ESCAPE + ESCAPE
|
|
372
|
+
],
|
|
373
|
+
[
|
|
374
|
+
// '\\\\' -> '\\'
|
|
375
|
+
/\\\\/g,
|
|
376
|
+
() => ESCAPE,
|
|
377
|
+
ESCAPE + ESCAPE
|
|
378
|
+
],
|
|
379
|
+
[
|
|
380
|
+
// Every real bracket expression -- POSIX classes included -- has already
|
|
381
|
+
// been held aside by `extractBrackets`, so the only `[` left in the
|
|
382
|
+
// pattern is an escaped, literal one.
|
|
383
|
+
// `\` is escaped by step 3
|
|
384
|
+
/\\\[([^\]/]*?)(\\*)($|\])/g,
|
|
385
|
+
// '\\[bar]' -> '\\\\[bar\\]'
|
|
386
|
+
(match, range, endEscape, close) => `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`,
|
|
387
|
+
"["
|
|
388
|
+
],
|
|
389
|
+
// ending
|
|
390
|
+
[
|
|
391
|
+
// 'js' will not match 'js.'
|
|
392
|
+
// 'ab' will not match 'abc'
|
|
393
|
+
DIRECT,
|
|
394
|
+
// WTF!
|
|
395
|
+
// https://git-scm.com/docs/gitignore
|
|
396
|
+
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
397
|
+
// which re-fixes #24, #38
|
|
398
|
+
// > If there is a separator at the end of the pattern then the pattern
|
|
399
|
+
// > will only match directories, otherwise the pattern can match both
|
|
400
|
+
// > files and directories.
|
|
401
|
+
// 'js*' will not match 'a.js'
|
|
402
|
+
// 'js/' will not match 'a.js'
|
|
403
|
+
// 'js' will match 'a.js' and 'a.js/'
|
|
404
|
+
(source) => {
|
|
405
|
+
const last = source[source.length - 1];
|
|
406
|
+
if (!last || last === TRAILING_WILDCARD) {
|
|
407
|
+
return source;
|
|
408
|
+
}
|
|
409
|
+
return last === SLASH ? `${source}$` : `${source}(?=$|\\/$)`;
|
|
410
|
+
}
|
|
411
|
+
]
|
|
412
|
+
];
|
|
413
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\uE000$/;
|
|
414
|
+
var MODE_IGNORE = "regex";
|
|
415
|
+
var MODE_CHECK_IGNORE = "checkRegex";
|
|
416
|
+
var UNDERSCORE = "_";
|
|
417
|
+
var TRAILING_WILD_CARD_REPLACERS = {
|
|
418
|
+
[MODE_IGNORE](_, p1) {
|
|
419
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
420
|
+
return `${prefix}(?=$|\\/$)`;
|
|
421
|
+
},
|
|
422
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
423
|
+
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
424
|
+
return `${prefix}(?=$|\\/$)`;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
var WILDCARD = "[^\\/]*";
|
|
428
|
+
var separatorAfter = (run, at) => {
|
|
429
|
+
let separator = EMPTY;
|
|
430
|
+
for (let index = at + 1; index < run.length && !run[index].wildcard; index++) {
|
|
431
|
+
separator += run[index].single;
|
|
432
|
+
}
|
|
433
|
+
return separator;
|
|
434
|
+
};
|
|
435
|
+
var pinWildcards = (source) => {
|
|
436
|
+
if (source.indexOf(WILDCARD) < 0) {
|
|
437
|
+
return source;
|
|
438
|
+
}
|
|
439
|
+
const tokens = [];
|
|
440
|
+
const { length } = source;
|
|
441
|
+
let index = 0;
|
|
442
|
+
while (index < length) {
|
|
443
|
+
const char = source[index];
|
|
444
|
+
if (source.startsWith(WILDCARD, index)) {
|
|
445
|
+
tokens.push({ wildcard: true });
|
|
446
|
+
index += WILDCARD.length;
|
|
447
|
+
} else if (char === "[") {
|
|
448
|
+
let end = index + 1;
|
|
449
|
+
if (source[end] === "^") {
|
|
450
|
+
end++;
|
|
451
|
+
}
|
|
452
|
+
if (source[end] === "]") {
|
|
453
|
+
end++;
|
|
454
|
+
}
|
|
455
|
+
while (end < length && source[end] !== "]") {
|
|
456
|
+
end += source[end] === ESCAPE ? 2 : 1;
|
|
457
|
+
}
|
|
458
|
+
end++;
|
|
459
|
+
tokens.push({ single: source.slice(index, end) });
|
|
460
|
+
index = end;
|
|
461
|
+
} else if (char === ESCAPE) {
|
|
462
|
+
tokens.push({ single: source.slice(index, index + 2) });
|
|
463
|
+
index += 2;
|
|
464
|
+
} else if (char === "(") {
|
|
465
|
+
let depth = 0;
|
|
466
|
+
let end = index;
|
|
467
|
+
do {
|
|
468
|
+
if (source[end] === ESCAPE) {
|
|
469
|
+
end++;
|
|
470
|
+
} else if (source[end] === "(") {
|
|
471
|
+
depth++;
|
|
472
|
+
} else if (source[end] === ")") {
|
|
473
|
+
depth--;
|
|
474
|
+
}
|
|
475
|
+
end++;
|
|
476
|
+
} while (end < length && depth > 0);
|
|
477
|
+
if ("*+?".indexOf(source[end]) >= 0) {
|
|
478
|
+
end++;
|
|
479
|
+
}
|
|
480
|
+
tokens.push({ boundary: source.slice(index, end) });
|
|
481
|
+
index = end;
|
|
482
|
+
} else if (char === "^" || char === "$") {
|
|
483
|
+
tokens.push({ boundary: char });
|
|
484
|
+
index++;
|
|
485
|
+
} else {
|
|
486
|
+
tokens.push({ single: char });
|
|
487
|
+
index++;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
let out = EMPTY;
|
|
491
|
+
let run = [];
|
|
492
|
+
const flush = () => {
|
|
493
|
+
let lastWildcard;
|
|
494
|
+
run.forEach((token, at) => {
|
|
495
|
+
if (token.wildcard) {
|
|
496
|
+
lastWildcard = at;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
run.forEach((token, at) => {
|
|
500
|
+
if (!token.wildcard) {
|
|
501
|
+
out += token.single;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
out += at === lastWildcard ? WILDCARD : `(?:(?!${separatorAfter(run, at)})[^\\/])*`;
|
|
505
|
+
});
|
|
506
|
+
run = [];
|
|
507
|
+
};
|
|
508
|
+
tokens.forEach((token) => {
|
|
509
|
+
if (token.boundary === void 0) {
|
|
510
|
+
run.push(token);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
flush();
|
|
514
|
+
out += token.boundary;
|
|
515
|
+
});
|
|
516
|
+
flush();
|
|
517
|
+
return out;
|
|
518
|
+
};
|
|
519
|
+
var makeRegexPrefix = (pattern) => {
|
|
520
|
+
const { source, sources } = extractBrackets(pattern);
|
|
521
|
+
const replaced = REPLACERS.reduce(
|
|
522
|
+
// A pass whose matcher finds nothing hands back the very string it was
|
|
523
|
+
// given, so asking first costs a search and saves a rewrite. Ten of the
|
|
524
|
+
// fifteen passes never fire for a typical .gitignore line, and between
|
|
525
|
+
// them they were 45% of this chain.
|
|
526
|
+
(prev, [matcher, replacer, required]) => {
|
|
527
|
+
if (matcher === DIRECT) {
|
|
528
|
+
return replacer(prev, pattern);
|
|
529
|
+
}
|
|
530
|
+
if (required !== UNDEFINED && prev.indexOf(required) < 0) {
|
|
531
|
+
return prev;
|
|
532
|
+
}
|
|
533
|
+
return matcher.test(prev) ? prev.replace(matcher, replacer.bind(pattern)) : prev;
|
|
534
|
+
},
|
|
535
|
+
source
|
|
536
|
+
);
|
|
537
|
+
return sources.length ? replaced.replace(
|
|
538
|
+
REGEX_RESTORE_PLACEHOLDER,
|
|
539
|
+
(match, index) => sources[index]
|
|
540
|
+
) : replaced;
|
|
541
|
+
};
|
|
542
|
+
var matchesBasename = (body) => {
|
|
543
|
+
const index = body.indexOf(SLASH);
|
|
544
|
+
return index < 0 || index === body.length - 1;
|
|
545
|
+
};
|
|
546
|
+
var basenameOf = (path) => {
|
|
547
|
+
const end = path.length - 1;
|
|
548
|
+
const index = path.lastIndexOf(
|
|
549
|
+
SLASH,
|
|
550
|
+
path[end] === SLASH ? end - 1 : end
|
|
551
|
+
);
|
|
552
|
+
return index < 0 ? path : path.slice(index + 1);
|
|
553
|
+
};
|
|
554
|
+
var parentOf = (path) => {
|
|
555
|
+
if (path.charCodeAt(0) === SLASH_CODE || path.indexOf(DOUBLE_SLASH) >= 0) {
|
|
556
|
+
const slices = path.split(SLASH).filter(Boolean);
|
|
557
|
+
slices.pop();
|
|
558
|
+
return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
|
|
559
|
+
}
|
|
560
|
+
const end = path.length - 1;
|
|
561
|
+
const cut = path.lastIndexOf(
|
|
562
|
+
SLASH,
|
|
563
|
+
path.charCodeAt(end) === SLASH_CODE ? end - 1 : end
|
|
564
|
+
);
|
|
565
|
+
return cut < 0 ? EMPTY : path.slice(0, cut + 1);
|
|
566
|
+
};
|
|
567
|
+
var isString = (subject) => typeof subject === "string";
|
|
568
|
+
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
569
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
|
570
|
+
var IgnoreRule = class {
|
|
571
|
+
constructor(pattern, mark, body, ignoreCase, negative, prefix) {
|
|
572
|
+
this.pattern = pattern;
|
|
573
|
+
this.mark = mark;
|
|
574
|
+
this.negative = negative;
|
|
575
|
+
define(this, "body", body);
|
|
576
|
+
define(this, "ignoreCase", ignoreCase);
|
|
577
|
+
define(this, "regexPrefix", prefix);
|
|
578
|
+
}
|
|
579
|
+
// Worked out on first use and kept behind an own property, the way `regex`
|
|
580
|
+
// caches itself in `_regex`. Deciding it in the constructor instead would
|
|
581
|
+
// add a fourth `defineProperty` to every rule ever built, which cost 4% of
|
|
582
|
+
// every compile -- including the compiles of rules that are never matched
|
|
583
|
+
// against anything.
|
|
584
|
+
get _basenameOnly() {
|
|
585
|
+
return define(this, "_basenameOnly", matchesBasename(this.body));
|
|
586
|
+
}
|
|
587
|
+
get regex() {
|
|
588
|
+
const key = UNDERSCORE + MODE_IGNORE;
|
|
589
|
+
if (this[key]) {
|
|
590
|
+
return this[key];
|
|
591
|
+
}
|
|
592
|
+
return this._make(MODE_IGNORE, key);
|
|
593
|
+
}
|
|
594
|
+
get checkRegex() {
|
|
595
|
+
const key = UNDERSCORE + MODE_CHECK_IGNORE;
|
|
596
|
+
if (this[key]) {
|
|
597
|
+
return this[key];
|
|
598
|
+
}
|
|
599
|
+
return this._make(MODE_CHECK_IGNORE, key);
|
|
600
|
+
}
|
|
601
|
+
_make(mode, key) {
|
|
602
|
+
const str = pinWildcards(this.regexPrefix.replace(
|
|
603
|
+
REGEX_REPLACE_TRAILING_WILDCARD,
|
|
604
|
+
// It does not need to bind pattern
|
|
605
|
+
TRAILING_WILD_CARD_REPLACERS[mode]
|
|
606
|
+
));
|
|
607
|
+
const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
|
|
608
|
+
return define(this, key, regex);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
var createRule = ({
|
|
612
|
+
pattern,
|
|
613
|
+
mark
|
|
614
|
+
}, ignoreCase) => {
|
|
615
|
+
let negative = false;
|
|
616
|
+
let body = pattern;
|
|
617
|
+
if (body.indexOf("!") === 0) {
|
|
618
|
+
negative = true;
|
|
619
|
+
body = body.substr(1);
|
|
620
|
+
}
|
|
621
|
+
body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
622
|
+
const regexPrefix = makeRegexPrefix(body);
|
|
623
|
+
return new IgnoreRule(
|
|
624
|
+
pattern,
|
|
625
|
+
mark,
|
|
626
|
+
body,
|
|
627
|
+
ignoreCase,
|
|
628
|
+
negative,
|
|
629
|
+
regexPrefix
|
|
630
|
+
);
|
|
631
|
+
};
|
|
632
|
+
var RuleManager = class {
|
|
633
|
+
constructor(ignoreCase) {
|
|
634
|
+
this._ignoreCase = ignoreCase;
|
|
635
|
+
this._rules = [];
|
|
636
|
+
this._basenameCount = 0;
|
|
637
|
+
}
|
|
638
|
+
_add(pattern) {
|
|
639
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
640
|
+
this._rules = this._rules.concat(pattern._rules._rules);
|
|
641
|
+
this._basenameCount += pattern._rules._basenameCount;
|
|
642
|
+
this._added = true;
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
if (isString(pattern)) {
|
|
646
|
+
pattern = {
|
|
647
|
+
pattern
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
if (checkPattern(pattern.pattern)) {
|
|
651
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
652
|
+
this._added = true;
|
|
653
|
+
this._rules.push(rule);
|
|
654
|
+
if (matchesBasename(rule.body)) {
|
|
655
|
+
this._basenameCount++;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
660
|
+
add(pattern) {
|
|
661
|
+
this._added = false;
|
|
662
|
+
makeArray(
|
|
663
|
+
isString(pattern) ? splitPattern(pattern) : pattern
|
|
664
|
+
).forEach(this._add, this);
|
|
665
|
+
return this._added;
|
|
666
|
+
}
|
|
667
|
+
// Test one single path without recursively checking parent directories
|
|
668
|
+
//
|
|
669
|
+
// - checkUnignored `boolean` whether should check if the path is unignored,
|
|
670
|
+
// setting `checkUnignored` to `false` could reduce additional
|
|
671
|
+
// path matching.
|
|
672
|
+
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
673
|
+
// @returns {TestResult} true if a file is ignored
|
|
674
|
+
test(path, checkUnignored, mode) {
|
|
675
|
+
let ignored = false;
|
|
676
|
+
let unignored = false;
|
|
677
|
+
let matchedRule;
|
|
678
|
+
const rules = this._rules;
|
|
679
|
+
const { length } = rules;
|
|
680
|
+
const shortcut = this._basenameCount * 2 >= length;
|
|
681
|
+
const basename2 = shortcut ? basenameOf(path) : path;
|
|
682
|
+
for (let index = 0; index < length; index++) {
|
|
683
|
+
const rule = rules[index];
|
|
684
|
+
const { negative } = rule;
|
|
685
|
+
const skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
|
|
686
|
+
if (!skip && rule[mode].test(
|
|
687
|
+
shortcut && rule._basenameOnly ? basename2 : path
|
|
688
|
+
)) {
|
|
689
|
+
ignored = !negative;
|
|
690
|
+
unignored = negative;
|
|
691
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const ret = {
|
|
695
|
+
ignored,
|
|
696
|
+
unignored
|
|
697
|
+
};
|
|
698
|
+
if (matchedRule) {
|
|
699
|
+
ret.rule = matchedRule;
|
|
700
|
+
}
|
|
701
|
+
return ret;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
var throwError = (message, Ctor) => {
|
|
705
|
+
throw new Ctor(message);
|
|
706
|
+
};
|
|
707
|
+
var checkPath = (path, originalPath, doThrow) => {
|
|
708
|
+
if (!isString(path)) {
|
|
709
|
+
return doThrow(
|
|
710
|
+
`path must be a string, but got \`${originalPath}\``,
|
|
711
|
+
TypeError
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
if (!path) {
|
|
715
|
+
return doThrow(`path must not be empty`, TypeError);
|
|
716
|
+
}
|
|
717
|
+
if (checkPath.isNotRelative(path)) {
|
|
718
|
+
const r = "`path.relative()`d";
|
|
719
|
+
return doThrow(
|
|
720
|
+
`path should be a ${r} string, but got "${originalPath}"`,
|
|
721
|
+
RangeError
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
return true;
|
|
725
|
+
};
|
|
726
|
+
var isNotRelative = (path) => {
|
|
727
|
+
const first = path.charCodeAt(0);
|
|
728
|
+
if (first === SLASH_CODE) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
if (first !== DOT_CODE) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
if (path.length === 1) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
const second = path.charCodeAt(1);
|
|
738
|
+
if (second === SLASH_CODE) {
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
if (second !== DOT_CODE) {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
return path.length === 2 || path.charCodeAt(2) === SLASH_CODE;
|
|
745
|
+
};
|
|
746
|
+
checkPath.isNotRelative = isNotRelative;
|
|
747
|
+
checkPath.convert = (p) => p;
|
|
748
|
+
var Ignore = class {
|
|
749
|
+
constructor({
|
|
750
|
+
ignorecase = true,
|
|
751
|
+
ignoreCase = ignorecase,
|
|
752
|
+
allowRelativePaths = false
|
|
753
|
+
} = {}) {
|
|
754
|
+
define(this, KEY_IGNORE, true);
|
|
755
|
+
this._rules = new RuleManager(ignoreCase);
|
|
756
|
+
this._strictPathCheck = !allowRelativePaths;
|
|
757
|
+
this._initCache();
|
|
758
|
+
}
|
|
759
|
+
_initCache() {
|
|
760
|
+
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
761
|
+
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
762
|
+
}
|
|
763
|
+
add(pattern) {
|
|
764
|
+
if (this._rules.add(pattern)) {
|
|
765
|
+
this._initCache();
|
|
766
|
+
}
|
|
767
|
+
return this;
|
|
768
|
+
}
|
|
769
|
+
// legacy
|
|
770
|
+
addPattern(pattern) {
|
|
771
|
+
return this.add(pattern);
|
|
772
|
+
}
|
|
773
|
+
// @returns {TestResult}
|
|
774
|
+
_test(originalPath, cache2, checkUnignored) {
|
|
775
|
+
const path = originalPath && checkPath.convert(originalPath);
|
|
776
|
+
checkPath(
|
|
777
|
+
path,
|
|
778
|
+
originalPath,
|
|
779
|
+
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
780
|
+
);
|
|
781
|
+
return this._t(path, cache2, checkUnignored);
|
|
782
|
+
}
|
|
783
|
+
checkIgnore(path) {
|
|
784
|
+
if (path.charCodeAt(path.length - 1) !== SLASH_CODE) {
|
|
785
|
+
return this.test(path);
|
|
786
|
+
}
|
|
787
|
+
const parentPath = parentOf(path);
|
|
788
|
+
if (parentPath) {
|
|
789
|
+
const parent = this._t(parentPath, this._testCache, true);
|
|
790
|
+
if (parent.ignored) {
|
|
791
|
+
return parent;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return this._rules.test(path, false, MODE_CHECK_IGNORE);
|
|
795
|
+
}
|
|
796
|
+
_t(path, cache2, checkUnignored) {
|
|
797
|
+
if (path in cache2) {
|
|
798
|
+
return cache2[path];
|
|
799
|
+
}
|
|
800
|
+
const parentPath = parentOf(path);
|
|
801
|
+
const parent = parentPath ? this._t(parentPath, cache2, checkUnignored) : UNDEFINED;
|
|
802
|
+
return cache2[path] = parent && parent.ignored ? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
803
|
+
}
|
|
804
|
+
ignores(path) {
|
|
805
|
+
return this._test(path, this._ignoreCache, false).ignored;
|
|
806
|
+
}
|
|
807
|
+
createFilter() {
|
|
808
|
+
return (path) => !this.ignores(path);
|
|
809
|
+
}
|
|
810
|
+
filter(paths) {
|
|
811
|
+
return makeArray(paths).filter(this.createFilter());
|
|
812
|
+
}
|
|
813
|
+
// @returns {TestResult}
|
|
814
|
+
test(path) {
|
|
815
|
+
return this._test(path, this._testCache, true);
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
var factory = (options) => new Ignore(options);
|
|
819
|
+
var isPathValid = (path) => checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
|
|
820
|
+
var setupWindows = () => {
|
|
821
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
822
|
+
checkPath.convert = makePosix;
|
|
823
|
+
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
824
|
+
checkPath.isNotRelative = (path) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
|
|
825
|
+
};
|
|
826
|
+
if (
|
|
827
|
+
// Detect `process` so that it can run in browsers.
|
|
828
|
+
typeof process !== "undefined" && process.platform === "win32"
|
|
829
|
+
) {
|
|
830
|
+
setupWindows();
|
|
831
|
+
}
|
|
832
|
+
module.exports = factory;
|
|
833
|
+
factory.default = factory;
|
|
834
|
+
module.exports.isPathValid = isPathValid;
|
|
835
|
+
define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// src/session-start.ts
|
|
840
|
+
import { appendFileSync as appendFileSync2, existsSync } from "node:fs";
|
|
841
|
+
import { join as join7 } from "node:path";
|
|
842
|
+
|
|
843
|
+
// ../agent-context/src/field-notes.ts
|
|
844
|
+
import { createHash } from "node:crypto";
|
|
845
|
+
import { readFileSync, statSync } from "node:fs";
|
|
846
|
+
var FIELD_NOTES_NOTE_TITLE = "Field notes for this sandbox";
|
|
847
|
+
var FIELD_NOTES_NOTE_HEADER = `## ${FIELD_NOTES_NOTE_TITLE}`;
|
|
848
|
+
var ALWAYS = ["meta"];
|
|
849
|
+
var ROW = /^(\d+),([a-z][a-z0-9_-]*),/;
|
|
850
|
+
var TOP_LEVEL = /^([a-z][a-z0-9_]*)(\[|:)/;
|
|
851
|
+
var blocksOf = (lines) => {
|
|
852
|
+
const opens = [];
|
|
853
|
+
for (const [at, line] of lines.entries()) {
|
|
854
|
+
const key = TOP_LEVEL.exec(line)?.[1];
|
|
855
|
+
if (key !== void 0) {
|
|
856
|
+
opens.push({ key, at });
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
860
|
+
for (const [index, open] of opens.entries()) {
|
|
861
|
+
blocks.set(open.key, lines.slice(open.at, opens[index + 1]?.at).join("\n").trimEnd());
|
|
862
|
+
}
|
|
863
|
+
return blocks;
|
|
864
|
+
};
|
|
865
|
+
var indexOf = (block) => {
|
|
866
|
+
if (block === void 0) {
|
|
867
|
+
return void 0;
|
|
868
|
+
}
|
|
869
|
+
const rows = [];
|
|
870
|
+
for (const line of block.split("\n").slice(1)) {
|
|
871
|
+
const trimmed = line.trim();
|
|
872
|
+
if (trimmed === "") {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const row = ROW.exec(trimmed);
|
|
876
|
+
if (row?.[1] === void 0 || row[2] === void 0) {
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
879
|
+
rows.push({ rank: Number(row[1]), id: row[2] });
|
|
880
|
+
}
|
|
881
|
+
return rows.length === 0 ? void 0 : rows.toSorted((left, right) => left.rank - right.rank);
|
|
882
|
+
};
|
|
883
|
+
var withinBudget = (sections, spent, budget) => {
|
|
884
|
+
const kept = [];
|
|
885
|
+
let used = spent;
|
|
886
|
+
for (const section of sections) {
|
|
887
|
+
const cost = section.text.length + 1;
|
|
888
|
+
if (used + cost > budget) {
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
used += cost;
|
|
892
|
+
kept.push(section);
|
|
893
|
+
}
|
|
894
|
+
return kept;
|
|
895
|
+
};
|
|
896
|
+
var readFieldNotesFile = (file) => {
|
|
897
|
+
try {
|
|
898
|
+
const text = readFileSync(file, "utf8");
|
|
899
|
+
return text.trim() === "" ? void 0 : { text, writtenAt: statSync(file).mtimeMs };
|
|
900
|
+
} catch {
|
|
901
|
+
return void 0;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var fieldNotes = ({ file: path, budget, onUnreadable, title = FIELD_NOTES_NOTE_TITLE }) => {
|
|
905
|
+
const file = readFieldNotesFile(path);
|
|
906
|
+
if (file === void 0) {
|
|
907
|
+
return void 0;
|
|
908
|
+
}
|
|
909
|
+
const lines = file.text.split("\n");
|
|
910
|
+
const blocks = blocksOf(lines);
|
|
911
|
+
const index = indexOf(blocks.get("priority"));
|
|
912
|
+
if (index === void 0) {
|
|
913
|
+
onUnreadable?.(`no readable \`priority\` table, so there is no order to send it in`);
|
|
914
|
+
return void 0;
|
|
915
|
+
}
|
|
916
|
+
const missing = index.filter(({ id }) => !blocks.has(id)).map(({ id }) => id);
|
|
917
|
+
if (missing.length > 0) {
|
|
918
|
+
onUnreadable?.(`the index names sections the file does not carry: ${missing.join(", ")}`);
|
|
919
|
+
return void 0;
|
|
920
|
+
}
|
|
921
|
+
const head = ALWAYS.flatMap((key) => {
|
|
922
|
+
const block = blocks.get(key);
|
|
923
|
+
return block === void 0 ? [] : [block];
|
|
924
|
+
});
|
|
925
|
+
const ranked = index.flatMap(({ id, rank }) => {
|
|
926
|
+
const text2 = blocks.get(id);
|
|
927
|
+
return text2 === void 0 ? [] : [{ id, rank, text: text2 }];
|
|
928
|
+
});
|
|
929
|
+
const header = `## ${title}`;
|
|
930
|
+
const spent = header.length + head.reduce((sum, block) => sum + block.length + 1, 0);
|
|
931
|
+
const kept = withinBudget(ranked, spent, budget);
|
|
932
|
+
const dropped = ranked.filter((section) => !kept.includes(section));
|
|
933
|
+
const disclosure = dropped.length === 0 ? `# every section of ${path} is below.` : `# ranks ${kept.length + 1}-${ranked.length} of ${path} are NOT below (${dropped.map((section) => section.id).join(", ")}); read the file itself if a turn needs them.`;
|
|
934
|
+
const text = [header, "", disclosure, ...head, ...kept.map((section) => section.text)].join("\n");
|
|
935
|
+
return {
|
|
936
|
+
text,
|
|
937
|
+
chars: text.length,
|
|
938
|
+
revision: createHash("sha256").update(file.text).digest("hex").slice(0, 8),
|
|
939
|
+
ranksSent: kept.length,
|
|
940
|
+
ranksTotal: ranked.length,
|
|
941
|
+
writtenAt: file.writtenAt
|
|
942
|
+
};
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
// ../agent-context/src/workspace-map.ts
|
|
946
|
+
import { readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
947
|
+
import { basename, dirname, join, relative, sep } from "node:path";
|
|
948
|
+
|
|
949
|
+
// ../../_shared/workspace-ignore/src/index.ts
|
|
950
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
951
|
+
|
|
952
|
+
// ../../_tools/constants/src/hosted-tiers.ts
|
|
953
|
+
var HOSTED_TIERS = [
|
|
954
|
+
{ id: "free", name: "Free", cpuKind: "shared", cpus: 4, memoryMb: 4096, volumeGb: 10, monthlyHours: 40, priceUsd: 0, flyHourUsd: 0.0329 },
|
|
955
|
+
{ id: "standard", name: "Standard", cpuKind: "shared", cpus: 8, memoryMb: 8192, volumeGb: 25, monthlyHours: 220, priceUsd: 20, flyHourUsd: 0.0657 },
|
|
956
|
+
{ id: "max", name: "Max", cpuKind: "shared", cpus: 8, memoryMb: 16384, volumeGb: 50, monthlyHours: 320, priceUsd: 50, flyHourUsd: 0.1234 }
|
|
957
|
+
];
|
|
958
|
+
var FREE_TIER = HOSTED_TIERS[0];
|
|
959
|
+
var PAID_TIERS = HOSTED_TIERS.filter((tier) => tier.priceUsd > 0);
|
|
960
|
+
|
|
961
|
+
// ../../_tools/constants/src/index.ts
|
|
962
|
+
var REFERENCE_DIR = "refs";
|
|
963
|
+
var PUBLIC_DIR = "public";
|
|
964
|
+
var GOOGLE_CLIENT_ID = "481795963975-cq9msl6higcd91joidrfp8mjlkuq5fk3.apps.googleusercontent.com";
|
|
965
|
+
var GOOGLE_TOKEN_STORAGE_KEY = `intentic.gid.${GOOGLE_CLIENT_ID}`;
|
|
966
|
+
|
|
967
|
+
// ../../_shared/workspace-ignore/src/constants.ts
|
|
968
|
+
var GIT_DIR = ".git";
|
|
969
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
970
|
+
"node_modules",
|
|
971
|
+
GIT_DIR,
|
|
972
|
+
".tmp",
|
|
973
|
+
"dist",
|
|
974
|
+
".cache",
|
|
975
|
+
".turbo",
|
|
976
|
+
".next",
|
|
977
|
+
".angular",
|
|
978
|
+
".pnpm-store",
|
|
979
|
+
".yarn",
|
|
980
|
+
".venv",
|
|
981
|
+
"venv",
|
|
982
|
+
"__pycache__",
|
|
983
|
+
".pytest_cache",
|
|
984
|
+
".mypy_cache",
|
|
985
|
+
".ruff_cache",
|
|
986
|
+
".gradle"
|
|
987
|
+
]);
|
|
988
|
+
var isSeparator = (char) => char === "/" || char === "\\";
|
|
989
|
+
var firstSegment = (relPath) => {
|
|
990
|
+
let start = 0;
|
|
991
|
+
while (isSeparator(relPath[start])) {
|
|
992
|
+
start++;
|
|
993
|
+
}
|
|
994
|
+
let end = start;
|
|
995
|
+
while (end < relPath.length && !isSeparator(relPath[end])) {
|
|
996
|
+
end++;
|
|
997
|
+
}
|
|
998
|
+
return relPath.slice(start, end);
|
|
999
|
+
};
|
|
1000
|
+
var isReferencePath = (relPath) => firstSegment(relPath) === REFERENCE_DIR;
|
|
1001
|
+
var isPublicPath = (relPath) => firstSegment(relPath) === PUBLIC_DIR;
|
|
1002
|
+
|
|
1003
|
+
// ../agent-context/src/workspace-map.ts
|
|
1004
|
+
var WORKSPACE_MAP_NOTE_HEADER = "## Map of this project";
|
|
1005
|
+
var MAX_NOTE_CHARS = 2800;
|
|
1006
|
+
var MAX_PURPOSE_CHARS = 100;
|
|
1007
|
+
var MAX_AREA_DEPTH = 4;
|
|
1008
|
+
var MAX_AREA_ENTRIES = 4e3;
|
|
1009
|
+
var MAX_AREAS = 24;
|
|
1010
|
+
var CONTAINER_MIN_CHILDREN = 3;
|
|
1011
|
+
var DOMINANT_SHARE = 0.5;
|
|
1012
|
+
var MAX_EXPANDED = 12;
|
|
1013
|
+
var MANIFESTS = ["package.json", "pyproject.toml", "Cargo.toml", "composer.json", "go.mod", "build.gradle", "pom.xml"];
|
|
1014
|
+
var posix = (path) => path.split(sep).join("/");
|
|
1015
|
+
var isBrowsable = (name, relPath) => !name.startsWith(".") && !IGNORED_DIRS.has(name) && !isReferencePath(relPath) && !isPublicPath(relPath);
|
|
1016
|
+
var childDirs = (dir, root) => {
|
|
1017
|
+
let entries;
|
|
1018
|
+
try {
|
|
1019
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1020
|
+
} catch {
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
return entries.filter((entry) => entry.isDirectory() && isBrowsable(entry.name, posix(relative(root, join(dir, entry.name))))).map((entry) => entry.name).toSorted();
|
|
1024
|
+
};
|
|
1025
|
+
var readIfPresent = (path) => {
|
|
1026
|
+
try {
|
|
1027
|
+
return readFileSync2(path, "utf8");
|
|
1028
|
+
} catch {
|
|
1029
|
+
return void 0;
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
var hasManifest = (dir) => MANIFESTS.some((name) => readIfPresent(join(dir, name)) !== void 0);
|
|
1033
|
+
var describeManifest = (dir, name) => {
|
|
1034
|
+
const source = readIfPresent(join(dir, name));
|
|
1035
|
+
if (source === void 0) {
|
|
1036
|
+
return void 0;
|
|
1037
|
+
}
|
|
1038
|
+
if (name.endsWith(".json")) {
|
|
1039
|
+
try {
|
|
1040
|
+
const description = JSON.parse(source).description;
|
|
1041
|
+
return typeof description === "string" && description.trim() !== "" ? description.trim() : void 0;
|
|
1042
|
+
} catch {
|
|
1043
|
+
return void 0;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
if (name === "go.mod") {
|
|
1047
|
+
const module = /^\s*module\s+(\S+)/m.exec(source)?.[1];
|
|
1048
|
+
return module === void 0 ? void 0 : `Go module ${module.split("/").slice(-2).join("/")}`;
|
|
1049
|
+
}
|
|
1050
|
+
const toml = /^\s*description\s*=\s*["'](.+?)["']\s*$/m.exec(source)?.[1];
|
|
1051
|
+
const xml = /<description>([^<]+)<\/description>/.exec(source)?.[1];
|
|
1052
|
+
const found = (toml ?? xml)?.trim();
|
|
1053
|
+
return found === void 0 || found === "" ? void 0 : found;
|
|
1054
|
+
};
|
|
1055
|
+
var describeReadme = (dir) => {
|
|
1056
|
+
const source = readIfPresent(join(dir, "README.md")) ?? readIfPresent(join(dir, "readme.md"));
|
|
1057
|
+
if (source === void 0) {
|
|
1058
|
+
return void 0;
|
|
1059
|
+
}
|
|
1060
|
+
const body = source.replace(/^---[\s\S]*?\n---\s*/, "");
|
|
1061
|
+
for (const raw of body.split("\n")) {
|
|
1062
|
+
const line = raw.trim();
|
|
1063
|
+
if (line === "" || line.startsWith("#") || line.startsWith("<") || line.startsWith(">") || line.startsWith("|")) {
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
const prose = line.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_`]/g, "").trim();
|
|
1067
|
+
if (prose === "" || prose.startsWith("-") || prose.startsWith("*")) {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
return prose;
|
|
1071
|
+
}
|
|
1072
|
+
return void 0;
|
|
1073
|
+
};
|
|
1074
|
+
var clip = (text, max) => {
|
|
1075
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
1076
|
+
if (oneLine.length <= max) {
|
|
1077
|
+
return oneLine;
|
|
1078
|
+
}
|
|
1079
|
+
const head = oneLine.slice(0, max);
|
|
1080
|
+
const lastSpace = head.lastIndexOf(" ");
|
|
1081
|
+
return `${(lastSpace > max / 2 ? head.slice(0, lastSpace) : head).replace(/[.,;:]$/, "")}\u2026`;
|
|
1082
|
+
};
|
|
1083
|
+
var purposeOf = (dir) => {
|
|
1084
|
+
for (const name of MANIFESTS) {
|
|
1085
|
+
const described = describeManifest(dir, name);
|
|
1086
|
+
if (described !== void 0) {
|
|
1087
|
+
return clip(described, MAX_PURPOSE_CHARS);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const readme = describeReadme(dir);
|
|
1091
|
+
return readme === void 0 ? "" : clip(readme, MAX_PURPOSE_CHARS);
|
|
1092
|
+
};
|
|
1093
|
+
var measure = (dir, root) => {
|
|
1094
|
+
const extensions = /* @__PURE__ */ new Map();
|
|
1095
|
+
let files = 0;
|
|
1096
|
+
let visited = 0;
|
|
1097
|
+
const walk = (current, depth) => {
|
|
1098
|
+
if (depth > MAX_AREA_DEPTH || visited >= MAX_AREA_ENTRIES) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
let entries;
|
|
1102
|
+
try {
|
|
1103
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
1104
|
+
} catch {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
for (const entry of entries) {
|
|
1108
|
+
if (visited >= MAX_AREA_ENTRIES) {
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
visited += 1;
|
|
1112
|
+
const child = join(current, entry.name);
|
|
1113
|
+
if (entry.isDirectory()) {
|
|
1114
|
+
if (isBrowsable(entry.name, posix(relative(root, child)))) {
|
|
1115
|
+
walk(child, depth + 1);
|
|
1116
|
+
}
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (!entry.isFile() || entry.name.startsWith(".")) {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
files += 1;
|
|
1123
|
+
const dot = entry.name.lastIndexOf(".");
|
|
1124
|
+
if (dot > 0) {
|
|
1125
|
+
const ext = entry.name.slice(dot + 1).toLowerCase();
|
|
1126
|
+
if (ext.length <= 5) {
|
|
1127
|
+
extensions.set(ext, (extensions.get(ext) ?? 0) + 1);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
walk(dir, 0);
|
|
1133
|
+
const kinds = [...extensions.entries()].toSorted((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, 2).map(([ext]) => ext);
|
|
1134
|
+
return { files, kinds };
|
|
1135
|
+
};
|
|
1136
|
+
var projectRootOf = (root, cwd) => {
|
|
1137
|
+
let current = cwd;
|
|
1138
|
+
for (; ; ) {
|
|
1139
|
+
try {
|
|
1140
|
+
statSync2(join(current, ".git"));
|
|
1141
|
+
return current;
|
|
1142
|
+
} catch {
|
|
1143
|
+
}
|
|
1144
|
+
if (current === root || current === dirname(current)) {
|
|
1145
|
+
return root;
|
|
1146
|
+
}
|
|
1147
|
+
const parent = dirname(current);
|
|
1148
|
+
if (relative(root, parent).startsWith("..")) {
|
|
1149
|
+
return root;
|
|
1150
|
+
}
|
|
1151
|
+
current = parent;
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
var packagesIn = (dir, root) => {
|
|
1155
|
+
if (hasManifest(dir)) {
|
|
1156
|
+
return [];
|
|
1157
|
+
}
|
|
1158
|
+
const children = childDirs(dir, root);
|
|
1159
|
+
const bearing = children.filter((child) => hasManifest(join(dir, child)));
|
|
1160
|
+
return children.length >= CONTAINER_MIN_CHILDREN && bearing.length >= CONTAINER_MIN_CHILDREN ? bearing.map((child) => ({ name: child, dir: join(dir, child) })) : [];
|
|
1161
|
+
};
|
|
1162
|
+
var areaDirsOf = (projectRoot, root) => {
|
|
1163
|
+
const names = childDirs(projectRoot, root);
|
|
1164
|
+
const areas = names.map((name) => {
|
|
1165
|
+
const dir = join(projectRoot, name);
|
|
1166
|
+
return { name, dir, packages: packagesIn(dir, root) };
|
|
1167
|
+
});
|
|
1168
|
+
const shelf = areas.length <= 2 ? areas.find((area) => area.packages.length > 0) : void 0;
|
|
1169
|
+
return shelf === void 0 ? areas : shelf.packages.map((entry) => ({ name: `${shelf.name}/${entry.name}`, dir: entry.dir, packages: [] }));
|
|
1170
|
+
};
|
|
1171
|
+
var workspaceMapOf = ({ root, cwd }) => {
|
|
1172
|
+
const projectRoot = projectRootOf(root, cwd);
|
|
1173
|
+
const found = areaDirsOf(projectRoot, root);
|
|
1174
|
+
if (found.length < 2) {
|
|
1175
|
+
return void 0;
|
|
1176
|
+
}
|
|
1177
|
+
const cwdRel = posix(relative(projectRoot, cwd));
|
|
1178
|
+
const isHere = (name) => cwdRel === name || cwdRel.startsWith(`${name}/`);
|
|
1179
|
+
const asArea = (name, dir, packages, children) => {
|
|
1180
|
+
const { files, kinds } = measure(dir, root);
|
|
1181
|
+
return { name, files, kinds, purpose: purposeOf(dir), here: isHere(name), packages, children, childrenOmitted: 0 };
|
|
1182
|
+
};
|
|
1183
|
+
const expand = (parent, entries) => {
|
|
1184
|
+
const ranked2 = entries.map((entry) => asArea(`${parent}/${entry.name}`, entry.dir, packagesIn(entry.dir, root).length, [])).toSorted((left, right) => right.files - left.files || left.name.localeCompare(right.name));
|
|
1185
|
+
return { children: ranked2.slice(0, MAX_EXPANDED), childrenOmitted: Math.max(0, ranked2.length - MAX_EXPANDED) };
|
|
1186
|
+
};
|
|
1187
|
+
const base = found.map((area) => ({ ...area, measured: asArea(area.name, area.dir, area.packages.length, []) }));
|
|
1188
|
+
const total = base.reduce((sum, entry) => sum + entry.measured.files, 0);
|
|
1189
|
+
const biggest = base.toSorted((left, right) => right.measured.files - left.measured.files)[0];
|
|
1190
|
+
const dominant = cwdRel === "" && biggest !== void 0 && biggest.measured.files >= total * DOMINANT_SHARE ? biggest.name : void 0;
|
|
1191
|
+
const measured = base.map(({ name, dir, packages, measured: area }) => {
|
|
1192
|
+
if (isHere(name)) {
|
|
1193
|
+
return { ...area, ...expand(name, packages) };
|
|
1194
|
+
}
|
|
1195
|
+
return name === dominant ? { ...area, ...expand(name, areaDirsOf(dir, root)) } : area;
|
|
1196
|
+
});
|
|
1197
|
+
const ranked = measured.toSorted((left, right) => right.files - left.files || left.name.localeCompare(right.name));
|
|
1198
|
+
const kept = ranked.slice(0, MAX_AREAS);
|
|
1199
|
+
const here = ranked.slice(MAX_AREAS).filter((area) => area.here);
|
|
1200
|
+
const areas = [...kept, ...here];
|
|
1201
|
+
return {
|
|
1202
|
+
project: posix(relative(root, projectRoot)),
|
|
1203
|
+
cwd: posix(relative(root, cwd)),
|
|
1204
|
+
areas,
|
|
1205
|
+
omitted: ranked.length - areas.length,
|
|
1206
|
+
// Only meaningful when the project isn't the whole workspace: names what else is there, nothing more.
|
|
1207
|
+
siblings: projectRoot === root ? [] : childDirs(root, root).filter((name) => name !== basename(projectRoot))
|
|
1208
|
+
};
|
|
1209
|
+
};
|
|
1210
|
+
var sizeOf = (area) => `${area.files} file${area.files === 1 ? "" : "s"}${area.kinds.length > 0 ? ` \xB7 ${area.kinds.join(", ")}` : ""}${area.packages > 0 ? ` \xB7 ${area.packages} packages` : ""}`;
|
|
1211
|
+
var areaLine = (area, width, indent) => `${indent}${area.name.padEnd(width - indent.length)} ${sizeOf(area)}${area.here && area.children.length === 0 ? " \u2190 you are here" : ""}`;
|
|
1212
|
+
var areaBlock = (area, width, silent) => [
|
|
1213
|
+
areaLine(area, width, " "),
|
|
1214
|
+
...area.purpose === "" || silent.has(area.name) ? [] : [` ${" ".repeat(width - 2)} ${area.purpose}`],
|
|
1215
|
+
...area.children.flatMap((child) => [
|
|
1216
|
+
areaLine(child, width, " "),
|
|
1217
|
+
...child.purpose === "" || silent.has(child.name) ? [] : [` ${" ".repeat(width - 6)} ${child.purpose}`]
|
|
1218
|
+
]),
|
|
1219
|
+
// What opening this area left out; unsaid, it reads as if that were the whole list.
|
|
1220
|
+
...area.childrenOmitted > 0 ? [` \u2026 and ${area.childrenOmitted} more in ${area.name}`] : []
|
|
1221
|
+
];
|
|
1222
|
+
var render = (map, exactPaths) => {
|
|
1223
|
+
const project = map.project === "" ? "the workspace" : `\`${map.project}\``;
|
|
1224
|
+
const all = map.areas.flatMap((area) => [area, ...area.children]);
|
|
1225
|
+
const width = Math.min(
|
|
1226
|
+
Math.max(...map.areas.flatMap((area) => [area.name.length + 2, ...area.children.map((child) => child.name.length + 6)])) + 2,
|
|
1227
|
+
34
|
|
1228
|
+
);
|
|
1229
|
+
const head = [
|
|
1230
|
+
WORKSPACE_MAP_NOTE_HEADER,
|
|
1231
|
+
"",
|
|
1232
|
+
`This is current filesystem context. It maps project areas, not file locations; use ${exactPaths} for exact paths.`,
|
|
1233
|
+
"Do not `ls` or `tree` the workspace root to orient yourself \u2014 the map above is that orientation.",
|
|
1234
|
+
"",
|
|
1235
|
+
map.cwd === map.project ? `You are at the top of ${project}.` : `You are here: \`${map.cwd}\``,
|
|
1236
|
+
"",
|
|
1237
|
+
`${project}, ${map.areas.length} area${map.areas.length === 1 ? "" : "s"}${map.omitted > 0 ? `, plus ${map.omitted} smaller ones not listed` : ""}`
|
|
1238
|
+
];
|
|
1239
|
+
const tail = map.siblings.length > 0 ? ["", `Also under the workspace root: ${map.siblings.map((name) => `${name}/`).join(", ")}`] : [];
|
|
1240
|
+
const byFilesAsc = all.toSorted((left, right) => left.files - right.files).map((area) => area.name);
|
|
1241
|
+
for (let muted = 0; muted <= byFilesAsc.length; muted += 1) {
|
|
1242
|
+
const silent2 = new Set(byFilesAsc.slice(0, muted));
|
|
1243
|
+
const note = [...head, ...map.areas.flatMap((area) => areaBlock(area, width, silent2)), ...tail].join("\n");
|
|
1244
|
+
if (note.length <= MAX_NOTE_CHARS) {
|
|
1245
|
+
return note;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
const silent = new Set(byFilesAsc);
|
|
1249
|
+
const lines = [];
|
|
1250
|
+
let used = [...head, ...tail].join("\n").length;
|
|
1251
|
+
let dropped = 0;
|
|
1252
|
+
for (const area of map.areas) {
|
|
1253
|
+
const block = areaBlock(area, width, silent);
|
|
1254
|
+
const cost = block.reduce((sum, line) => sum + line.length + 1, 0);
|
|
1255
|
+
if (used + cost > MAX_NOTE_CHARS) {
|
|
1256
|
+
dropped += 1;
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
used += cost;
|
|
1260
|
+
lines.push(...block);
|
|
1261
|
+
}
|
|
1262
|
+
const total = map.omitted + dropped;
|
|
1263
|
+
return [...head, ...lines, ...total > 0 ? [` \u2026 and ${total} more`] : [], ...tail].join("\n");
|
|
1264
|
+
};
|
|
1265
|
+
var TTL_MS = 6e4;
|
|
1266
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1267
|
+
var workspaceMapNote = (input) => {
|
|
1268
|
+
const exactPaths = input.exactPaths ?? "`iq files` or Read";
|
|
1269
|
+
const key = `${input.root}\0${input.cwd}\0${exactPaths}`;
|
|
1270
|
+
const hit = cache.get(key);
|
|
1271
|
+
if (hit !== void 0 && Date.now() - hit.at < TTL_MS) {
|
|
1272
|
+
return hit.note;
|
|
1273
|
+
}
|
|
1274
|
+
let note;
|
|
1275
|
+
try {
|
|
1276
|
+
const map = workspaceMapOf(input);
|
|
1277
|
+
note = map === void 0 ? void 0 : render(map, exactPaths);
|
|
1278
|
+
} catch {
|
|
1279
|
+
note = void 0;
|
|
1280
|
+
}
|
|
1281
|
+
cache.set(key, { at: Date.now(), note });
|
|
1282
|
+
return note;
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
// src/features.ts
|
|
1286
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1287
|
+
import { join as join3 } from "node:path";
|
|
1288
|
+
|
|
1289
|
+
// src/runtime.ts
|
|
1290
|
+
import { spawn } from "node:child_process";
|
|
1291
|
+
import { accessSync, closeSync, constants, mkdirSync, openSync, readFileSync as readFileSync3, statSync as statSync3, writeFileSync } from "node:fs";
|
|
1292
|
+
import { delimiter, dirname as dirname2, join as join2 } from "node:path";
|
|
1293
|
+
var pluginRoot = (env = process.env) => env["CLAUDE_PLUGIN_ROOT"] ?? dirname2(import.meta.dirname);
|
|
1294
|
+
var isMissing = (error) => {
|
|
1295
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
1296
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
1297
|
+
};
|
|
1298
|
+
var pluginFile = (parts, env = process.env) => {
|
|
1299
|
+
try {
|
|
1300
|
+
return readFileSync3(join2(pluginRoot(env), ...parts), "utf8");
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
if (isMissing(error)) {
|
|
1303
|
+
return void 0;
|
|
1304
|
+
}
|
|
1305
|
+
throw error;
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
var onPath = (name, env = process.env) => {
|
|
1309
|
+
const extensions = process.platform === "win32" ? (env["PATHEXT"] ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
1310
|
+
for (const dir of (env["PATH"] ?? "").split(delimiter)) {
|
|
1311
|
+
for (const extension of extensions) {
|
|
1312
|
+
try {
|
|
1313
|
+
accessSync(join2(dir, `${name}${extension}`), constants.X_OK);
|
|
1314
|
+
return true;
|
|
1315
|
+
} catch {
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return false;
|
|
1320
|
+
};
|
|
1321
|
+
var startDetached = (command, args, options) => {
|
|
1322
|
+
mkdirSync(dirname2(options.log), { recursive: true });
|
|
1323
|
+
const out = openSync(options.log, "w");
|
|
1324
|
+
try {
|
|
1325
|
+
const child = spawn(command, args, { cwd: options.cwd, detached: true, stdio: ["ignore", out, out], env: { ...options.env ?? process.env } });
|
|
1326
|
+
child.on("error", () => void 0);
|
|
1327
|
+
child.unref();
|
|
1328
|
+
} finally {
|
|
1329
|
+
closeSync(out);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
var claimIfDue = (stamp, everyMs, now = Date.now()) => {
|
|
1333
|
+
try {
|
|
1334
|
+
if (now - statSync3(stamp).mtimeMs < everyMs) {
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
} catch {
|
|
1338
|
+
}
|
|
1339
|
+
try {
|
|
1340
|
+
mkdirSync(dirname2(stamp), { recursive: true });
|
|
1341
|
+
writeFileSync(stamp, `${now}
|
|
1342
|
+
`);
|
|
1343
|
+
return true;
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
process.stderr.write(`intentic: ${stamp} cannot be written (${error instanceof Error ? error.message : String(error)}), so what it paces is skipped
|
|
1346
|
+
`);
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
var projectSlug = (project) => project.replace(/[^a-zA-Z0-9]/g, "-");
|
|
1351
|
+
|
|
1352
|
+
// src/features.ts
|
|
1353
|
+
var OPTIONS = {
|
|
1354
|
+
output_cleaners: {
|
|
1355
|
+
type: "boolean",
|
|
1356
|
+
title: "Trim Bash output",
|
|
1357
|
+
description: "Clean what a successful command printed before Claude reads it (progress bars, install chatter, repeated lines, the middle of huge logs), keep the full text retrievable with `retrieve-output`, and ledger what each cleaner saved.",
|
|
1358
|
+
default: true
|
|
1359
|
+
},
|
|
1360
|
+
cleaners: {
|
|
1361
|
+
type: "string",
|
|
1362
|
+
title: "Cleaners",
|
|
1363
|
+
description: "Which cleaners run, as the sandbox spells it: empty for all, `-cap,-wide` to switch some off, `git,pnpm` to allow only those. `/intentic:stats` lists them by what they saved.",
|
|
1364
|
+
default: ""
|
|
1365
|
+
},
|
|
1366
|
+
output_holdout: {
|
|
1367
|
+
type: "number",
|
|
1368
|
+
title: "Untrimmed share",
|
|
1369
|
+
description: "Share of commands left untrimmed, so the saving can be measured against a real comparison and not only computed. 0 turns the comparison off; the exact per-command saving is reported either way.",
|
|
1370
|
+
default: 0,
|
|
1371
|
+
min: 0,
|
|
1372
|
+
max: 0.5
|
|
1373
|
+
},
|
|
1374
|
+
project_map: {
|
|
1375
|
+
type: "boolean",
|
|
1376
|
+
title: "Project map",
|
|
1377
|
+
description: "Open each session with a map of the project it starts in: its areas, what each is for, and where Claude is standing. Worked out from the tree every time, never stored, so it cannot go stale.",
|
|
1378
|
+
default: true
|
|
1379
|
+
},
|
|
1380
|
+
field_notes: {
|
|
1381
|
+
type: "boolean",
|
|
1382
|
+
title: "Field notes",
|
|
1383
|
+
description: "Open each session with this project's field notes: the traps past sessions hit and the commands that really verify. Written by `/intentic:field-notes` from your own sessions here; nothing is sent until you run it once.",
|
|
1384
|
+
default: true
|
|
1385
|
+
},
|
|
1386
|
+
iq: {
|
|
1387
|
+
type: "boolean",
|
|
1388
|
+
title: "iq search",
|
|
1389
|
+
description: "Teach Claude the `iq` code search CLI and recall past sessions on a matching prompt. Needs `npm i -g @intentic/iq`; without it this switch does nothing and says so once.",
|
|
1390
|
+
default: true
|
|
1391
|
+
},
|
|
1392
|
+
fileq: {
|
|
1393
|
+
type: "boolean",
|
|
1394
|
+
title: "fileq",
|
|
1395
|
+
description: "Let Claude read documents, PDFs, spreadsheets, slides, notebooks, images, audio and archives as markdown with the bundled `fileq` CLI. Off makes the command refuse; its one-line skill description stays listed, since a plugin cannot hide its own skills.",
|
|
1396
|
+
default: true
|
|
1397
|
+
},
|
|
1398
|
+
shadows: {
|
|
1399
|
+
type: "boolean",
|
|
1400
|
+
title: "Document shadows",
|
|
1401
|
+
description: "Keep a markdown rendering of every document in the project up to date in the background (a sweep at session start and every ten minutes, and each binary file Claude writes), so a read finds one ready. Costs background CPU on a document-heavy project.",
|
|
1402
|
+
default: false
|
|
1403
|
+
},
|
|
1404
|
+
holdout: {
|
|
1405
|
+
type: "number",
|
|
1406
|
+
title: "Measured share",
|
|
1407
|
+
description: "Share of sessions opened without the map, the field notes and the iq teaching, so `/intentic:stats` can show what each changed. 0 sends them to every session and measures nothing.",
|
|
1408
|
+
default: 0.1,
|
|
1409
|
+
min: 0,
|
|
1410
|
+
max: 0.5
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
var asBoolean = (raw, fallback) => {
|
|
1414
|
+
const value = raw?.trim().toLowerCase();
|
|
1415
|
+
if (value === "true" || value === "1" || value === "yes" || value === "on") {
|
|
1416
|
+
return true;
|
|
1417
|
+
}
|
|
1418
|
+
if (value === "false" || value === "0" || value === "no" || value === "off") {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
return fallback;
|
|
1422
|
+
};
|
|
1423
|
+
var asShare = (raw, option) => {
|
|
1424
|
+
const value = raw === void 0 || raw.trim() === "" ? Number.NaN : Number(raw);
|
|
1425
|
+
return Number.isFinite(value) ? Math.min(option.max, Math.max(option.min, value)) : option.default;
|
|
1426
|
+
};
|
|
1427
|
+
var optionEnvName = (key) => `CLAUDE_PLUGIN_OPTION_${key.toUpperCase()}`;
|
|
1428
|
+
var readOptions = (env = process.env) => {
|
|
1429
|
+
const entries = Object.keys(OPTIONS).map((key) => {
|
|
1430
|
+
const option = OPTIONS[key];
|
|
1431
|
+
const raw = env[optionEnvName(key)];
|
|
1432
|
+
if (option.type === "boolean") {
|
|
1433
|
+
return [key, asBoolean(raw, option.default)];
|
|
1434
|
+
}
|
|
1435
|
+
if (option.type === "number") {
|
|
1436
|
+
return [key, asShare(raw, option)];
|
|
1437
|
+
}
|
|
1438
|
+
return [key, raw ?? option.default];
|
|
1439
|
+
});
|
|
1440
|
+
return Object.fromEntries(entries);
|
|
1441
|
+
};
|
|
1442
|
+
var optionsFile = (data) => join3(data, "options.json");
|
|
1443
|
+
var saveOptions = (data, options) => {
|
|
1444
|
+
mkdirSync2(data, { recursive: true });
|
|
1445
|
+
writeFileSync2(optionsFile(data), `${JSON.stringify(options)}
|
|
1446
|
+
`);
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1449
|
+
// src/hook-io.ts
|
|
1450
|
+
import { homedir } from "node:os";
|
|
1451
|
+
import { join as join4 } from "node:path";
|
|
1452
|
+
var readStdin = async () => {
|
|
1453
|
+
const chunks = [];
|
|
1454
|
+
for await (const chunk of process.stdin) {
|
|
1455
|
+
chunks.push(chunk);
|
|
1456
|
+
}
|
|
1457
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1458
|
+
};
|
|
1459
|
+
var parseHookInput = (text) => {
|
|
1460
|
+
try {
|
|
1461
|
+
const value = JSON.parse(text);
|
|
1462
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1463
|
+
} catch {
|
|
1464
|
+
return void 0;
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
var runHook = async (handle) => {
|
|
1468
|
+
try {
|
|
1469
|
+
const raw = await readStdin();
|
|
1470
|
+
const input = parseHookInput(raw);
|
|
1471
|
+
if (input === void 0) {
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
const answer = await handle(input, raw);
|
|
1475
|
+
if (typeof answer === "string") {
|
|
1476
|
+
process.stdout.write(answer);
|
|
1477
|
+
} else if (answer !== void 0) {
|
|
1478
|
+
process.stdout.write(`${JSON.stringify(answer)}
|
|
1479
|
+
`);
|
|
1480
|
+
}
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
process.stderr.write(`intentic: ${error instanceof Error ? error.message : String(error)}
|
|
1483
|
+
`);
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
var dataDir = (env = process.env) => env["CLAUDE_PLUGIN_DATA"] ?? join4(env["CLAUDE_CONFIG_DIR"] ?? join4(homedir(), ".claude"), "plugins", "data", "intentic-intentic");
|
|
1487
|
+
var projectDir = (input, env = process.env) => env["CLAUDE_PROJECT_DIR"] ?? input.cwd ?? process.cwd();
|
|
1488
|
+
var fieldNotesFile = (project) => join4(project, ".claude", "intentic", "field-notes.toon");
|
|
1489
|
+
|
|
1490
|
+
// src/sessions.ts
|
|
1491
|
+
import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
1492
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
1493
|
+
|
|
1494
|
+
// ../agent-context/src/experiments.ts
|
|
1495
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1496
|
+
var experimentArm = (experiment, id, holdout) => {
|
|
1497
|
+
if (id === void 0) {
|
|
1498
|
+
return Math.random() >= holdout;
|
|
1499
|
+
}
|
|
1500
|
+
const bucket = createHash2("sha256").update(`${experiment}:${id}`).digest().readUInt32BE(0) / 4294967296;
|
|
1501
|
+
return bucket >= holdout;
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// src/sessions.ts
|
|
1505
|
+
var SALTS = { map: "workspace-map", notes: "field-notes", iq: "iq-search" };
|
|
1506
|
+
var MECHANISM_ON = {
|
|
1507
|
+
map: (options) => options.project_map,
|
|
1508
|
+
notes: (options) => options.field_notes,
|
|
1509
|
+
iq: (options) => options.iq
|
|
1510
|
+
};
|
|
1511
|
+
var armsOf = (options, session) => {
|
|
1512
|
+
const arms = {};
|
|
1513
|
+
for (const mechanism of Object.keys(SALTS)) {
|
|
1514
|
+
if (MECHANISM_ON[mechanism](options) && options.holdout > 0 && session !== void 0) {
|
|
1515
|
+
arms[mechanism] = experimentArm(SALTS[mechanism], session, options.holdout);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return arms;
|
|
1519
|
+
};
|
|
1520
|
+
var sends = (options, arms, mechanism) => MECHANISM_ON[mechanism](options) && arms[mechanism] !== false;
|
|
1521
|
+
var measuredArms = (arms, offered) => Object.fromEntries(Object.entries(arms).filter(([mechanism]) => offered[mechanism] === true));
|
|
1522
|
+
var sessionsFile = (data) => join5(data, "sessions.jsonl");
|
|
1523
|
+
var recordSession = (data, row) => {
|
|
1524
|
+
const file = sessionsFile(data);
|
|
1525
|
+
mkdirSync3(dirname3(file), { recursive: true });
|
|
1526
|
+
appendFileSync(file, `${JSON.stringify(row)}
|
|
1527
|
+
`);
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
// src/shadows.ts
|
|
1531
|
+
import { relative as relative2, isAbsolute, join as join6 } from "node:path";
|
|
1532
|
+
var SWEEP_EVERY_MS = 10 * 6e4;
|
|
1533
|
+
var fileq = (env) => join6(pluginRoot(env), "dist", "fileq.mjs");
|
|
1534
|
+
var logOf = (data, project, what) => join6(data, "shadows", `${projectSlug(project)}.${what}.log`);
|
|
1535
|
+
var fileqEnv = (env, project) => ({ ...env, WORKSPACE_ROOT: project });
|
|
1536
|
+
var sweepIfDue = (data, project, env = process.env, now = Date.now()) => {
|
|
1537
|
+
if (!claimIfDue(join6(data, "shadows", `${projectSlug(project)}.stamp`), SWEEP_EVERY_MS, now)) {
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
startDetached(process.execPath, [fileq(env), "sweep", "--json"], { cwd: project, log: logOf(data, project, "sweep"), env: fileqEnv(env, project) });
|
|
1541
|
+
return true;
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
// src/session-start.ts
|
|
1545
|
+
var NOTES_BUDGET = 4e3;
|
|
1546
|
+
var NOTICE_EVERY_MS = 24 * 60 * 6e4;
|
|
1547
|
+
var TOLD_ON = /* @__PURE__ */ new Set(["startup", "clear", "compact"]);
|
|
1548
|
+
var COUNTED_ON = /* @__PURE__ */ new Set(["startup", "clear"]);
|
|
1549
|
+
var compose = (options, input, project, arms, data, env) => {
|
|
1550
|
+
const context = [];
|
|
1551
|
+
const notices = [];
|
|
1552
|
+
const notice = (id, text) => {
|
|
1553
|
+
if (claimIfDue(join7(data, "notices", `${id}.stamp`), NOTICE_EVERY_MS)) {
|
|
1554
|
+
notices.push(text);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
const iq = options.iq && onPath("iq", env);
|
|
1558
|
+
const sent = {};
|
|
1559
|
+
const offered = {};
|
|
1560
|
+
let notesRevision;
|
|
1561
|
+
if (options.project_map) {
|
|
1562
|
+
const note = workspaceMapNote({ root: project, cwd: input.cwd ?? project, exactPaths: iq ? "`iq files` or Read" : "Glob or Read" });
|
|
1563
|
+
offered.map = note !== void 0;
|
|
1564
|
+
if (note !== void 0 && sends(options, arms, "map")) {
|
|
1565
|
+
context.push(note);
|
|
1566
|
+
sent.map = note.length;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (options.field_notes) {
|
|
1570
|
+
const brief = fieldNotes({
|
|
1571
|
+
file: fieldNotesFile(project),
|
|
1572
|
+
title: "Field notes for this project",
|
|
1573
|
+
budget: NOTES_BUDGET,
|
|
1574
|
+
onUnreadable: (why) => notice(`notes-${projectSlug(project)}`, `intentic: this project's field notes were not sent: ${why}. Run /intentic:field-notes to rewrite them.`)
|
|
1575
|
+
});
|
|
1576
|
+
notesRevision = brief?.revision;
|
|
1577
|
+
offered.notes = brief !== void 0;
|
|
1578
|
+
if (brief !== void 0 && sends(options, arms, "notes")) {
|
|
1579
|
+
context.push(brief.text);
|
|
1580
|
+
sent.notes = brief.chars;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
if (options.iq) {
|
|
1584
|
+
if (iq) {
|
|
1585
|
+
const nudge = pluginFile(["generated", "iq-nudge.txt"], env);
|
|
1586
|
+
offered.iq = nudge !== void 0;
|
|
1587
|
+
if (nudge !== void 0 && sends(options, arms, "iq")) {
|
|
1588
|
+
context.push(`## iq workspace search
|
|
1589
|
+
|
|
1590
|
+
${nudge.trim()}`);
|
|
1591
|
+
sent.iq = true;
|
|
1592
|
+
}
|
|
1593
|
+
startDetached("iq", ["sessions", "ingest"], { cwd: project, log: join7(data, "iq", `${projectSlug(project)}.ingest.log`), env });
|
|
1594
|
+
} else {
|
|
1595
|
+
notice("iq-missing", "intentic: iq search is on but the `iq` CLI is not installed (`npm i -g @intentic/iq`), so nothing was taught.");
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return { context, row: { arms: measuredArms(arms, offered), sent, ...notesRevision === void 0 ? {} : { notesRevision } }, notices };
|
|
1599
|
+
};
|
|
1600
|
+
var exportEnv = (options, project, env) => {
|
|
1601
|
+
const file = env["CLAUDE_ENV_FILE"];
|
|
1602
|
+
if (file === void 0 || file === "") {
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
const quoted = `'${project.replaceAll("'", `'\\''`)}'`;
|
|
1606
|
+
appendFileSync2(file, `export INTENTIC_PROJECT_DIR=${quoted}
|
|
1607
|
+
export INTENTIC_FILEQ=${options.fileq ? "1" : "0"}
|
|
1608
|
+
`);
|
|
1609
|
+
};
|
|
1610
|
+
var record = (what, write) => {
|
|
1611
|
+
try {
|
|
1612
|
+
write();
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
process.stderr.write(`intentic: ${what} not recorded: ${error instanceof Error ? error.message : String(error)}
|
|
1615
|
+
`);
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
var sessionStart = (input, env = process.env) => {
|
|
1619
|
+
const options = readOptions(env);
|
|
1620
|
+
const data = dataDir(env);
|
|
1621
|
+
const project = projectDir(input, env);
|
|
1622
|
+
const source = input.source ?? "startup";
|
|
1623
|
+
record("the switches", () => saveOptions(data, options));
|
|
1624
|
+
exportEnv(options, project, env);
|
|
1625
|
+
if (options.shadows && existsSync(project)) {
|
|
1626
|
+
sweepIfDue(data, project, env);
|
|
1627
|
+
}
|
|
1628
|
+
if (!TOLD_ON.has(source)) {
|
|
1629
|
+
return void 0;
|
|
1630
|
+
}
|
|
1631
|
+
const { context, row, notices } = compose(options, input, project, armsOf(options, input.session_id), data, env);
|
|
1632
|
+
const session = input.session_id;
|
|
1633
|
+
if (COUNTED_ON.has(source) && session !== void 0) {
|
|
1634
|
+
record(
|
|
1635
|
+
"the session",
|
|
1636
|
+
() => recordSession(data, {
|
|
1637
|
+
ts: Date.now(),
|
|
1638
|
+
session,
|
|
1639
|
+
project,
|
|
1640
|
+
...input.transcript_path === void 0 ? {} : { transcript: input.transcript_path },
|
|
1641
|
+
...row
|
|
1642
|
+
})
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
if (context.length === 0 && notices.length === 0) {
|
|
1646
|
+
return void 0;
|
|
1647
|
+
}
|
|
1648
|
+
return {
|
|
1649
|
+
...notices.length === 0 ? {} : { systemMessage: notices.join("\n") },
|
|
1650
|
+
hookSpecificOutput: { hookEventName: "SessionStart", ...context.length === 0 ? {} : { additionalContext: context.join("\n\n") } }
|
|
1651
|
+
};
|
|
1652
|
+
};
|
|
1653
|
+
if (process.argv[1]?.endsWith("session-start.mjs")) {
|
|
1654
|
+
await runHook((input) => sessionStart(input));
|
|
1655
|
+
}
|
|
1656
|
+
export {
|
|
1657
|
+
sessionStart
|
|
1658
|
+
};
|