brainfile 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/__tests__/helpers/mcp-wire.d.ts +2 -0
- package/dist/__tests__/helpers/mcp-wire.d.ts.map +1 -1
- package/dist/__tests__/helpers/mcp-wire.js +4 -2
- package/dist/__tests__/helpers/mcp-wire.js.map +1 -1
- package/dist/chunk-BNE7R5GK.mjs +5489 -0
- package/dist/chunk-L4WSDMK3.mjs +1973 -0
- package/dist/{chunk-MUV7XTFA.mjs → chunk-XFDTTLSW.mjs} +3277 -4340
- package/dist/cli.js +7 -1
- package/dist/cli.mjs +8078 -5357
- package/dist/commands/add.d.ts +4 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +68 -72
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/boards.d.ts +18 -0
- package/dist/commands/boards.d.ts.map +1 -0
- package/dist/commands/boards.js +166 -0
- package/dist/commands/boards.js.map +1 -0
- package/dist/commands/brief.d.ts +17 -11
- package/dist/commands/brief.d.ts.map +1 -1
- package/dist/commands/brief.js +38 -35
- package/dist/commands/brief.js.map +1 -1
- package/dist/commands/check.d.ts +47 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +134 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/columns.d.ts +48 -0
- package/dist/commands/columns.d.ts.map +1 -0
- package/dist/commands/columns.js +132 -0
- package/dist/commands/columns.js.map +1 -0
- package/dist/commands/complete.js +9 -4
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/decide.d.ts +72 -0
- package/dist/commands/decide.d.ts.map +1 -0
- package/dist/commands/decide.js +264 -0
- package/dist/commands/decide.js.map +1 -0
- package/dist/commands/hooks.d.ts +5 -1
- package/dist/commands/hooks.d.ts.map +1 -1
- package/dist/commands/hooks.js +10 -11
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +27 -0
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +127 -20
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +216 -7
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/log.d.ts +51 -0
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +113 -84
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/mcp.d.ts +5 -2
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +44 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/memory-index.d.ts +34 -0
- package/dist/commands/memory-index.d.ts.map +1 -0
- package/dist/commands/memory-index.js +135 -0
- package/dist/commands/memory-index.js.map +1 -0
- package/dist/commands/merge-driver.d.ts +1 -15
- package/dist/commands/merge-driver.d.ts.map +1 -1
- package/dist/commands/merge-driver.js +8 -134
- package/dist/commands/merge-driver.js.map +1 -1
- package/dist/commands/migrate-tracked.d.ts +1 -1
- package/dist/commands/migrate-tracked.d.ts.map +1 -1
- package/dist/commands/migrate-tracked.js +21 -10
- package/dist/commands/migrate-tracked.js.map +1 -1
- package/dist/commands/patch.d.ts +23 -4
- package/dist/commands/patch.d.ts.map +1 -1
- package/dist/commands/patch.js +149 -91
- package/dist/commands/patch.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +11 -5
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/schema.d.ts +12 -0
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +28 -10
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/search.d.ts +46 -12
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +89 -44
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +175 -10
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/template.d.ts +2 -0
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +2 -1
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/types.d.ts +31 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +122 -22
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/where.d.ts +20 -1
- package/dist/commands/where.d.ts.map +1 -1
- package/dist/commands/where.js +56 -6
- package/dist/commands/where.js.map +1 -1
- package/dist/hook.mjs +15 -0
- package/dist/hooks/session-start.d.ts +39 -0
- package/dist/hooks/session-start.d.ts.map +1 -0
- package/dist/hooks/session-start.js +181 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/lib/contractRunner.d.ts.map +1 -1
- package/dist/lib/contractRunner.js +8 -0
- package/dist/lib/contractRunner.js.map +1 -1
- package/dist/mcp/helpers.d.ts +29 -1
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +57 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/instructions.d.ts +5 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +57 -0
- package/dist/mcp/instructions.js.map +1 -0
- package/dist/mcp/resources.d.ts +9 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +29 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/schemas.d.ts +600 -9
- package/dist/mcp/schemas.d.ts.map +1 -1
- package/dist/mcp/schemas.js +374 -12
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/tools/adr_promote_tool.d.ts +7 -0
- package/dist/mcp/tools/adr_promote_tool.d.ts.map +1 -0
- package/dist/mcp/tools/adr_promote_tool.js +37 -0
- package/dist/mcp/tools/adr_promote_tool.js.map +1 -0
- package/dist/mcp/tools/board_tool.d.ts +7 -0
- package/dist/mcp/tools/board_tool.d.ts.map +1 -0
- package/dist/mcp/tools/board_tool.js +61 -0
- package/dist/mcp/tools/board_tool.js.map +1 -0
- package/dist/mcp/tools/brief_tool.d.ts.map +1 -1
- package/dist/mcp/tools/brief_tool.js +13 -5
- package/dist/mcp/tools/brief_tool.js.map +1 -1
- package/dist/mcp/tools/check_tool.d.ts +6 -0
- package/dist/mcp/tools/check_tool.d.ts.map +1 -0
- package/dist/mcp/tools/check_tool.js +54 -0
- package/dist/mcp/tools/check_tool.js.map +1 -0
- package/dist/mcp/tools/columns_tool.d.ts +7 -0
- package/dist/mcp/tools/columns_tool.d.ts.map +1 -0
- package/dist/mcp/tools/columns_tool.js +94 -0
- package/dist/mcp/tools/columns_tool.js.map +1 -0
- package/dist/mcp/tools/decide_tool.d.ts +91 -0
- package/dist/mcp/tools/decide_tool.d.ts.map +1 -0
- package/dist/mcp/tools/decide_tool.js +141 -0
- package/dist/mcp/tools/decide_tool.js.map +1 -0
- package/dist/mcp/tools/get_task_tool.d.ts.map +1 -1
- package/dist/mcp/tools/get_task_tool.js +62 -4
- package/dist/mcp/tools/get_task_tool.js.map +1 -1
- package/dist/mcp/tools/lint_tool.d.ts +7 -0
- package/dist/mcp/tools/lint_tool.d.ts.map +1 -0
- package/dist/mcp/tools/lint_tool.js +48 -0
- package/dist/mcp/tools/lint_tool.js.map +1 -0
- package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -1
- package/dist/mcp/tools/list_tasks_tool.js +132 -15
- package/dist/mcp/tools/list_tasks_tool.js.map +1 -1
- package/dist/mcp/tools/log_tool.d.ts +4 -0
- package/dist/mcp/tools/log_tool.d.ts.map +1 -0
- package/dist/mcp/tools/log_tool.js +52 -0
- package/dist/mcp/tools/log_tool.js.map +1 -0
- package/dist/mcp/tools/note_tool.d.ts +7 -0
- package/dist/mcp/tools/note_tool.d.ts.map +1 -0
- package/dist/mcp/tools/note_tool.js +39 -0
- package/dist/mcp/tools/note_tool.js.map +1 -0
- package/dist/mcp/tools/retire_tool.d.ts +4 -0
- package/dist/mcp/tools/retire_tool.d.ts.map +1 -0
- package/dist/mcp/tools/retire_tool.js +81 -0
- package/dist/mcp/tools/retire_tool.js.map +1 -0
- package/dist/mcp/tools/search_tool.d.ts.map +1 -1
- package/dist/mcp/tools/search_tool.js +36 -19
- package/dist/mcp/tools/search_tool.js.map +1 -1
- package/dist/mcp/tools/supersede_tool.d.ts +4 -0
- package/dist/mcp/tools/supersede_tool.d.ts.map +1 -0
- package/dist/mcp/tools/supersede_tool.js +72 -0
- package/dist/mcp/tools/supersede_tool.js.map +1 -0
- package/dist/mcp/tools/sync_tool.d.ts +9 -0
- package/dist/mcp/tools/sync_tool.d.ts.map +1 -0
- package/dist/mcp/tools/sync_tool.js +87 -0
- package/dist/mcp/tools/sync_tool.js.map +1 -0
- package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_add_tool.js +39 -17
- package/dist/mcp/tools/task_add_tool.js.map +1 -1
- package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_complete_tool.js +3 -2
- package/dist/mcp/tools/task_complete_tool.js.map +1 -1
- package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_patch_tool.js +30 -5
- package/dist/mcp/tools/task_patch_tool.js.map +1 -1
- package/dist/mcp/tools/template_tool.d.ts +4 -0
- package/dist/mcp/tools/template_tool.d.ts.map +1 -0
- package/dist/mcp/tools/template_tool.js +70 -0
- package/dist/mcp/tools/template_tool.js.map +1 -0
- package/dist/mcp/tools/types_tool.d.ts +4 -0
- package/dist/mcp/tools/types_tool.d.ts.map +1 -0
- package/dist/mcp/tools/types_tool.js +61 -0
- package/dist/mcp/tools/types_tool.js.map +1 -0
- package/dist/mcp/tools/where_tool.d.ts +4 -0
- package/dist/mcp/tools/where_tool.d.ts.map +1 -0
- package/dist/mcp/tools/where_tool.js +73 -0
- package/dist/mcp/tools/where_tool.js.map +1 -0
- package/dist/schemas/board.json +29 -8
- package/dist/{tui-5TFQR53I.mjs → tui-IR3JKS4Y.mjs} +994 -315
- package/dist/utils/board-autosync.d.ts +1 -10
- package/dist/utils/board-autosync.d.ts.map +1 -1
- package/dist/utils/board-autosync.js +11 -29
- package/dist/utils/board-autosync.js.map +1 -1
- package/dist/utils/board-commit-hooks.d.ts +8 -0
- package/dist/utils/board-commit-hooks.d.ts.map +1 -1
- package/dist/utils/board-commit-hooks.js +45 -1
- package/dist/utils/board-commit-hooks.js.map +1 -1
- package/dist/utils/board-repo.d.ts +1 -119
- package/dist/utils/board-repo.d.ts.map +1 -1
- package/dist/utils/board-repo.js +43 -571
- package/dist/utils/board-repo.js.map +1 -1
- package/dist/utils/board-sync.d.ts +3 -123
- package/dist/utils/board-sync.d.ts.map +1 -1
- package/dist/utils/board-sync.js +34 -551
- package/dist/utils/board-sync.js.map +1 -1
- package/dist/utils/graph.d.ts +20 -0
- package/dist/utils/graph.d.ts.map +1 -0
- package/dist/utils/graph.js +61 -0
- package/dist/utils/graph.js.map +1 -0
- package/dist/utils/related.d.ts +21 -0
- package/dist/utils/related.d.ts.map +1 -0
- package/dist/utils/related.js +36 -0
- package/dist/utils/related.js.map +1 -0
- package/package.json +2 -2
- package/dist/tui/theme.d.ts +0 -98
- package/dist/tui/theme.d.ts.map +0 -1
- package/dist/tui/theme.js +0 -141
- package/dist/tui/theme.js.map +0 -1
|
@@ -0,0 +1,1973 @@
|
|
|
1
|
+
import { createRequire as __brainfileCreateRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath as __brainfileFileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __brainfileDirname } from 'node:path';
|
|
4
|
+
const require = __brainfileCreateRequire(import.meta.url);
|
|
5
|
+
const __filename = __brainfileFileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = __brainfileDirname(__filename);
|
|
7
|
+
import {
|
|
8
|
+
require_boardFile,
|
|
9
|
+
require_brief,
|
|
10
|
+
require_briefState,
|
|
11
|
+
require_decisionOperations,
|
|
12
|
+
require_decisions,
|
|
13
|
+
require_dist,
|
|
14
|
+
require_docGraph,
|
|
15
|
+
require_docTypes,
|
|
16
|
+
require_files,
|
|
17
|
+
require_frontmatter,
|
|
18
|
+
require_search,
|
|
19
|
+
require_taskFile,
|
|
20
|
+
require_workspace
|
|
21
|
+
} from "./chunk-XFDTTLSW.mjs";
|
|
22
|
+
import {
|
|
23
|
+
__commonJS,
|
|
24
|
+
__require,
|
|
25
|
+
__toESM
|
|
26
|
+
} from "./chunk-I5SJHUZK.mjs";
|
|
27
|
+
|
|
28
|
+
// ../core/dist/memory/sqlite.js
|
|
29
|
+
var require_sqlite = __commonJS({
|
|
30
|
+
"../core/dist/memory/sqlite.js"(exports) {
|
|
31
|
+
"use strict";
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.indexDisabledByEnv = indexDisabledByEnv;
|
|
34
|
+
exports.loadSqlite = loadSqlite;
|
|
35
|
+
exports.sqliteAvailable = sqliteAvailable;
|
|
36
|
+
exports.setSqliteLoaderForTesting = setSqliteLoaderForTesting;
|
|
37
|
+
var override;
|
|
38
|
+
var cached;
|
|
39
|
+
function indexDisabledByEnv(env = process.env) {
|
|
40
|
+
const value = (env.BRAINFILE_INDEX ?? "").trim().toLowerCase();
|
|
41
|
+
return value === "off" || value === "0" || value === "false" || value === "no";
|
|
42
|
+
}
|
|
43
|
+
function isSqliteExperimentalWarning(warning, rest) {
|
|
44
|
+
const message = typeof warning === "string" ? warning : warning?.message;
|
|
45
|
+
const first = rest[0];
|
|
46
|
+
const type = typeof first === "string" ? first : first?.type ?? warning?.name;
|
|
47
|
+
return /sqlite/i.test(String(message ?? "")) && String(type ?? "") === "ExperimentalWarning";
|
|
48
|
+
}
|
|
49
|
+
function requireSqlite() {
|
|
50
|
+
const getBuiltin = process.getBuiltinModule;
|
|
51
|
+
if (typeof getBuiltin !== "function")
|
|
52
|
+
return null;
|
|
53
|
+
const original = process.emitWarning;
|
|
54
|
+
process.emitWarning = function patched(warning, ...rest) {
|
|
55
|
+
if (isSqliteExperimentalWarning(warning, rest))
|
|
56
|
+
return;
|
|
57
|
+
return original.call(process, warning, ...rest);
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
const mod = getBuiltin.call(process, "node:sqlite");
|
|
61
|
+
if (!mod || typeof mod.DatabaseSync !== "function")
|
|
62
|
+
return null;
|
|
63
|
+
const probe = new mod.DatabaseSync(":memory:");
|
|
64
|
+
try {
|
|
65
|
+
probe.exec("CREATE VIRTUAL TABLE probe USING fts5(x)");
|
|
66
|
+
} finally {
|
|
67
|
+
probe.close();
|
|
68
|
+
}
|
|
69
|
+
return mod;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
} finally {
|
|
73
|
+
process.emitWarning = original;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function loadSqlite() {
|
|
77
|
+
if (override)
|
|
78
|
+
return override();
|
|
79
|
+
if (indexDisabledByEnv())
|
|
80
|
+
return null;
|
|
81
|
+
if (cached === void 0)
|
|
82
|
+
cached = requireSqlite();
|
|
83
|
+
return cached;
|
|
84
|
+
}
|
|
85
|
+
function sqliteAvailable() {
|
|
86
|
+
return loadSqlite() !== null;
|
|
87
|
+
}
|
|
88
|
+
function setSqliteLoaderForTesting(loader) {
|
|
89
|
+
override = loader;
|
|
90
|
+
cached = void 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ../core/dist/memory/chunk.js
|
|
96
|
+
var require_chunk = __commonJS({
|
|
97
|
+
"../core/dist/memory/chunk.js"(exports) {
|
|
98
|
+
"use strict";
|
|
99
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
100
|
+
exports.DEFAULT_MAX_CHUNK_CHARS = void 0;
|
|
101
|
+
exports.chunkText = chunkText;
|
|
102
|
+
exports.firstHeading = firstHeading;
|
|
103
|
+
exports.DEFAULT_MAX_CHUNK_CHARS = 3e3;
|
|
104
|
+
var HEADING = /^(#{1,6})\s+(\S.*?)\s*#*\s*$/;
|
|
105
|
+
var FENCE = /^\s*(```|~~~)/;
|
|
106
|
+
function sectionsOf(lines, markdown) {
|
|
107
|
+
const sections = [];
|
|
108
|
+
let current = { start: 0, lines: [] };
|
|
109
|
+
let fence = null;
|
|
110
|
+
lines.forEach((line, index) => {
|
|
111
|
+
const fenceMatch = FENCE.exec(line);
|
|
112
|
+
const heading = markdown && fence === null && !fenceMatch ? HEADING.exec(line) : null;
|
|
113
|
+
if (fenceMatch) {
|
|
114
|
+
if (fence === null)
|
|
115
|
+
fence = fenceMatch[1];
|
|
116
|
+
else if (fenceMatch[1] === fence)
|
|
117
|
+
fence = null;
|
|
118
|
+
}
|
|
119
|
+
if (heading) {
|
|
120
|
+
if (current.lines.length > 0)
|
|
121
|
+
sections.push(current);
|
|
122
|
+
current = { start: index, lines: [], heading: heading[2] };
|
|
123
|
+
}
|
|
124
|
+
current.lines.push(line);
|
|
125
|
+
});
|
|
126
|
+
if (current.lines.length > 0)
|
|
127
|
+
sections.push(current);
|
|
128
|
+
return sections;
|
|
129
|
+
}
|
|
130
|
+
function chunkText(text, options = {}) {
|
|
131
|
+
const maxChars = options.maxChars && options.maxChars > 0 ? options.maxChars : exports.DEFAULT_MAX_CHUNK_CHARS;
|
|
132
|
+
const offset = options.lineOffset ?? 0;
|
|
133
|
+
const normalized = text.replace(/\r\n?/g, "\n");
|
|
134
|
+
if (!normalized.trim())
|
|
135
|
+
return [];
|
|
136
|
+
const lines = normalized.split("\n");
|
|
137
|
+
if (lines[lines.length - 1] === "")
|
|
138
|
+
lines.pop();
|
|
139
|
+
const chunks = [];
|
|
140
|
+
let buffer = [];
|
|
141
|
+
let bufferStart = 0;
|
|
142
|
+
let bufferLength = 0;
|
|
143
|
+
let bufferHeading;
|
|
144
|
+
const flush = () => {
|
|
145
|
+
if (buffer.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
let first = 0;
|
|
148
|
+
let last = buffer.length - 1;
|
|
149
|
+
while (first <= last && !buffer[first].trim())
|
|
150
|
+
first++;
|
|
151
|
+
while (last >= first && !buffer[last].trim())
|
|
152
|
+
last--;
|
|
153
|
+
if (first <= last) {
|
|
154
|
+
chunks.push({
|
|
155
|
+
text: buffer.slice(first, last + 1).join("\n"),
|
|
156
|
+
startLine: offset + bufferStart + first + 1,
|
|
157
|
+
endLine: offset + bufferStart + last + 1,
|
|
158
|
+
...bufferHeading ? { heading: bufferHeading } : {}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
buffer = [];
|
|
162
|
+
bufferLength = 0;
|
|
163
|
+
bufferHeading = void 0;
|
|
164
|
+
};
|
|
165
|
+
const push = (line, index, heading) => {
|
|
166
|
+
if (buffer.length === 0) {
|
|
167
|
+
bufferStart = index;
|
|
168
|
+
bufferHeading = heading;
|
|
169
|
+
}
|
|
170
|
+
buffer.push(line);
|
|
171
|
+
bufferLength += line.length + 1;
|
|
172
|
+
};
|
|
173
|
+
for (const section of sectionsOf(lines, options.markdown !== false)) {
|
|
174
|
+
const size = section.lines.reduce((n, l) => n + l.length + 1, 0);
|
|
175
|
+
if (buffer.length > 0 && bufferLength + size > maxChars)
|
|
176
|
+
flush();
|
|
177
|
+
if (size <= maxChars) {
|
|
178
|
+
section.lines.forEach((line, i) => push(line, section.start + i, section.heading));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
section.lines.forEach((line, i) => {
|
|
182
|
+
if (buffer.length > 0 && bufferLength + line.length + 1 > maxChars)
|
|
183
|
+
flush();
|
|
184
|
+
push(line, section.start + i, section.heading);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
flush();
|
|
188
|
+
return chunks;
|
|
189
|
+
}
|
|
190
|
+
function firstHeading(text) {
|
|
191
|
+
let fence = false;
|
|
192
|
+
for (const line of text.split(/\r?\n/)) {
|
|
193
|
+
if (FENCE.test(line)) {
|
|
194
|
+
fence = !fence;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (fence)
|
|
198
|
+
continue;
|
|
199
|
+
const match = HEADING.exec(line);
|
|
200
|
+
if (match)
|
|
201
|
+
return match[2];
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ../core/dist/memory/mine.js
|
|
209
|
+
var require_mine = __commonJS({
|
|
210
|
+
"../core/dist/memory/mine.js"(exports) {
|
|
211
|
+
"use strict";
|
|
212
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
213
|
+
if (k2 === void 0) k2 = k;
|
|
214
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
215
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
216
|
+
desc = { enumerable: true, get: function() {
|
|
217
|
+
return m[k];
|
|
218
|
+
} };
|
|
219
|
+
}
|
|
220
|
+
Object.defineProperty(o, k2, desc);
|
|
221
|
+
}) : (function(o, m, k, k2) {
|
|
222
|
+
if (k2 === void 0) k2 = k;
|
|
223
|
+
o[k2] = m[k];
|
|
224
|
+
}));
|
|
225
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
226
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
227
|
+
}) : function(o, v) {
|
|
228
|
+
o["default"] = v;
|
|
229
|
+
});
|
|
230
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
231
|
+
var ownKeys = function(o) {
|
|
232
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
233
|
+
var ar = [];
|
|
234
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
235
|
+
return ar;
|
|
236
|
+
};
|
|
237
|
+
return ownKeys(o);
|
|
238
|
+
};
|
|
239
|
+
return function(mod) {
|
|
240
|
+
if (mod && mod.__esModule) return mod;
|
|
241
|
+
var result = {};
|
|
242
|
+
if (mod != null) {
|
|
243
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
244
|
+
}
|
|
245
|
+
__setModuleDefault(result, mod);
|
|
246
|
+
return result;
|
|
247
|
+
};
|
|
248
|
+
})();
|
|
249
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
250
|
+
exports.ALWAYS_INCLUDED_ROOT_DOCS = exports.SKIP_DIRS = exports.MAX_DOC_FILES = exports.MAX_FILE_BYTES = void 0;
|
|
251
|
+
exports.isSupportedTextPath = isSupportedTextPath;
|
|
252
|
+
exports.isSkippedPath = isSkippedPath;
|
|
253
|
+
exports.isUnminableRoot = isUnminableRoot;
|
|
254
|
+
exports.listProjectDocs = listProjectDocs;
|
|
255
|
+
var child_process_1 = __require("child_process");
|
|
256
|
+
var fs = __importStar(__require("fs"));
|
|
257
|
+
var os = __importStar(__require("os"));
|
|
258
|
+
var path2 = __importStar(__require("path"));
|
|
259
|
+
exports.MAX_FILE_BYTES = 256 * 1024;
|
|
260
|
+
exports.MAX_DOC_FILES = 5e3;
|
|
261
|
+
exports.SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
262
|
+
".brainfile",
|
|
263
|
+
".git",
|
|
264
|
+
".hg",
|
|
265
|
+
".svn",
|
|
266
|
+
".recoil",
|
|
267
|
+
"node_modules",
|
|
268
|
+
"vendor",
|
|
269
|
+
"dist",
|
|
270
|
+
"build",
|
|
271
|
+
"out",
|
|
272
|
+
"target",
|
|
273
|
+
"coverage",
|
|
274
|
+
".next",
|
|
275
|
+
".cache",
|
|
276
|
+
"tmp",
|
|
277
|
+
"temp"
|
|
278
|
+
]);
|
|
279
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".mdown", ".txt", ".text", ".rst", ".adoc", ".asciidoc", ".org"]);
|
|
280
|
+
var OPERATIONAL_NAMES = /* @__PURE__ */ new Set([
|
|
281
|
+
"agents",
|
|
282
|
+
"claude",
|
|
283
|
+
"readme",
|
|
284
|
+
"security",
|
|
285
|
+
"security_contacts",
|
|
286
|
+
"security_policy",
|
|
287
|
+
"contributing",
|
|
288
|
+
"contribute",
|
|
289
|
+
"developers",
|
|
290
|
+
"developing",
|
|
291
|
+
"developer"
|
|
292
|
+
]);
|
|
293
|
+
exports.ALWAYS_INCLUDED_ROOT_DOCS = ["AGENTS.md", "CLAUDE.md"];
|
|
294
|
+
function isOperationalHiddenDir(rel) {
|
|
295
|
+
return rel === ".github" || rel === ".well-known" || rel.endsWith("/.well-known");
|
|
296
|
+
}
|
|
297
|
+
function isSupportedTextPath(rel) {
|
|
298
|
+
const base = path2.posix.basename(rel).toLowerCase();
|
|
299
|
+
const ext = path2.posix.extname(base);
|
|
300
|
+
if (TEXT_EXTENSIONS.has(ext))
|
|
301
|
+
return true;
|
|
302
|
+
return ext === "" && OPERATIONAL_NAMES.has(base);
|
|
303
|
+
}
|
|
304
|
+
function isSkippedPath(rel) {
|
|
305
|
+
const parts = rel.split("/").filter(Boolean);
|
|
306
|
+
for (let i = 0; i < parts.length; i++) {
|
|
307
|
+
const part = parts[i];
|
|
308
|
+
const isDir = i < parts.length - 1;
|
|
309
|
+
if (isDir && exports.SKIP_DIRS.has(part))
|
|
310
|
+
return true;
|
|
311
|
+
if (part.startsWith(".")) {
|
|
312
|
+
const prefix = parts.slice(0, i + 1).join("/");
|
|
313
|
+
if (isDir && isOperationalHiddenDir(prefix))
|
|
314
|
+
continue;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
function isUnminableRoot(root) {
|
|
321
|
+
const resolved = path2.resolve(root);
|
|
322
|
+
return resolved === path2.parse(resolved).root || resolved === path2.resolve(os.homedir());
|
|
323
|
+
}
|
|
324
|
+
function statFile(root, rel) {
|
|
325
|
+
const abs = path2.join(root, rel);
|
|
326
|
+
try {
|
|
327
|
+
const stat = fs.statSync(abs);
|
|
328
|
+
if (!stat.isFile() || stat.size > exports.MAX_FILE_BYTES)
|
|
329
|
+
return null;
|
|
330
|
+
return { rel, abs, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function gitListing(root) {
|
|
336
|
+
const result = (0, child_process_1.spawnSync)("git", ["ls-files", "-co", "--exclude-standard", "-z"], {
|
|
337
|
+
cwd: root,
|
|
338
|
+
encoding: "utf-8",
|
|
339
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
340
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
341
|
+
timeout: 1e4
|
|
342
|
+
});
|
|
343
|
+
if (result.status !== 0 || typeof result.stdout !== "string")
|
|
344
|
+
return null;
|
|
345
|
+
return result.stdout.split("\0").filter(Boolean);
|
|
346
|
+
}
|
|
347
|
+
function walkListing(root, limit) {
|
|
348
|
+
const out = [];
|
|
349
|
+
const visit = (dir, rel, depth) => {
|
|
350
|
+
if (out.length >= limit || depth > 12)
|
|
351
|
+
return;
|
|
352
|
+
let entries;
|
|
353
|
+
try {
|
|
354
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
355
|
+
} catch {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
359
|
+
for (const entry of entries) {
|
|
360
|
+
if (out.length >= limit)
|
|
361
|
+
return;
|
|
362
|
+
const childRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
363
|
+
if (entry.isDirectory()) {
|
|
364
|
+
if (exports.SKIP_DIRS.has(entry.name))
|
|
365
|
+
continue;
|
|
366
|
+
if (entry.name.startsWith(".") && !isOperationalHiddenDir(childRel))
|
|
367
|
+
continue;
|
|
368
|
+
visit(path2.join(dir, entry.name), childRel, depth + 1);
|
|
369
|
+
} else if (entry.isFile() && isSupportedTextPath(childRel) && !isSkippedPath(childRel)) {
|
|
370
|
+
out.push(childRel);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
visit(root, "", 0);
|
|
375
|
+
return out;
|
|
376
|
+
}
|
|
377
|
+
function listProjectDocs(root, options = {}) {
|
|
378
|
+
const limit = options.maxFiles ?? exports.MAX_DOC_FILES;
|
|
379
|
+
if (isUnminableRoot(root))
|
|
380
|
+
return { files: [], via: "none" };
|
|
381
|
+
try {
|
|
382
|
+
if (!fs.statSync(root).isDirectory())
|
|
383
|
+
return { files: [], via: "none" };
|
|
384
|
+
} catch {
|
|
385
|
+
return { files: [], via: "none" };
|
|
386
|
+
}
|
|
387
|
+
const listed = gitListing(root);
|
|
388
|
+
const via = listed ? "git" : "walk";
|
|
389
|
+
const candidates = (listed ?? walkListing(root, limit * 4)).map((rel) => rel.split(path2.sep).join("/")).filter((rel) => isSupportedTextPath(rel) && !isSkippedPath(rel));
|
|
390
|
+
for (const name of exports.ALWAYS_INCLUDED_ROOT_DOCS) {
|
|
391
|
+
if (!candidates.includes(name) && fs.existsSync(path2.join(root, name)))
|
|
392
|
+
candidates.push(name);
|
|
393
|
+
}
|
|
394
|
+
const files = [];
|
|
395
|
+
for (const rel of [...new Set(candidates)].sort()) {
|
|
396
|
+
if (files.length >= limit)
|
|
397
|
+
break;
|
|
398
|
+
const file = statFile(root, rel);
|
|
399
|
+
if (file)
|
|
400
|
+
files.push(file);
|
|
401
|
+
}
|
|
402
|
+
return { files, via };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// ../core/dist/memory/memoryIndex.js
|
|
408
|
+
var require_memoryIndex = __commonJS({
|
|
409
|
+
"../core/dist/memory/memoryIndex.js"(exports) {
|
|
410
|
+
"use strict";
|
|
411
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
412
|
+
if (k2 === void 0) k2 = k;
|
|
413
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
414
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
415
|
+
desc = { enumerable: true, get: function() {
|
|
416
|
+
return m[k];
|
|
417
|
+
} };
|
|
418
|
+
}
|
|
419
|
+
Object.defineProperty(o, k2, desc);
|
|
420
|
+
}) : (function(o, m, k, k2) {
|
|
421
|
+
if (k2 === void 0) k2 = k;
|
|
422
|
+
o[k2] = m[k];
|
|
423
|
+
}));
|
|
424
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
425
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
426
|
+
}) : function(o, v) {
|
|
427
|
+
o["default"] = v;
|
|
428
|
+
});
|
|
429
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
430
|
+
var ownKeys = function(o) {
|
|
431
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
432
|
+
var ar = [];
|
|
433
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
434
|
+
return ar;
|
|
435
|
+
};
|
|
436
|
+
return ownKeys(o);
|
|
437
|
+
};
|
|
438
|
+
return function(mod) {
|
|
439
|
+
if (mod && mod.__esModule) return mod;
|
|
440
|
+
var result = {};
|
|
441
|
+
if (mod != null) {
|
|
442
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
443
|
+
}
|
|
444
|
+
__setModuleDefault(result, mod);
|
|
445
|
+
return result;
|
|
446
|
+
};
|
|
447
|
+
})();
|
|
448
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
449
|
+
exports.MemoryIndex = exports.INDEX_SOURCES = exports.INDEX_FILENAME = exports.INDEX_SCHEMA_VERSION = void 0;
|
|
450
|
+
exports.getIndexPath = getIndexPath;
|
|
451
|
+
exports.ftsTerms = ftsTerms;
|
|
452
|
+
var crypto = __importStar(__require("crypto"));
|
|
453
|
+
var fs = __importStar(__require("fs"));
|
|
454
|
+
var path2 = __importStar(__require("path"));
|
|
455
|
+
var frontmatter_1 = require_frontmatter();
|
|
456
|
+
var boardFile_1 = require_boardFile();
|
|
457
|
+
var workspace_1 = require_workspace();
|
|
458
|
+
var briefState_1 = require_briefState();
|
|
459
|
+
var files_1 = require_files();
|
|
460
|
+
var docTypes_1 = require_docTypes();
|
|
461
|
+
var docGraph_1 = require_docGraph();
|
|
462
|
+
var search_1 = require_search();
|
|
463
|
+
var decisions_1 = require_decisions();
|
|
464
|
+
var chunk_1 = require_chunk();
|
|
465
|
+
var mine_1 = require_mine();
|
|
466
|
+
var sqlite_1 = require_sqlite();
|
|
467
|
+
exports.INDEX_SCHEMA_VERSION = "2";
|
|
468
|
+
exports.INDEX_FILENAME = "index.db";
|
|
469
|
+
exports.INDEX_SOURCES = ["board", "logs", "ledger", "plans", "repo"];
|
|
470
|
+
function getIndexPath(brainfilePath) {
|
|
471
|
+
return path2.join((0, briefState_1.getBriefStateDir)(brainfilePath), exports.INDEX_FILENAME);
|
|
472
|
+
}
|
|
473
|
+
var SCHEMA = `
|
|
474
|
+
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
475
|
+
CREATE TABLE IF NOT EXISTS sources (
|
|
476
|
+
path TEXT PRIMARY KEY,
|
|
477
|
+
source TEXT NOT NULL,
|
|
478
|
+
size INTEGER NOT NULL,
|
|
479
|
+
mtime_ms REAL NOT NULL,
|
|
480
|
+
hash TEXT NOT NULL,
|
|
481
|
+
indexed_at TEXT NOT NULL
|
|
482
|
+
);
|
|
483
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
484
|
+
id INTEGER PRIMARY KEY,
|
|
485
|
+
path TEXT NOT NULL,
|
|
486
|
+
source TEXT NOT NULL,
|
|
487
|
+
doc_id TEXT,
|
|
488
|
+
title TEXT NOT NULL,
|
|
489
|
+
body TEXT NOT NULL,
|
|
490
|
+
ident TEXT NOT NULL,
|
|
491
|
+
heading TEXT,
|
|
492
|
+
start_line INTEGER NOT NULL,
|
|
493
|
+
end_line INTEGER NOT NULL,
|
|
494
|
+
lane TEXT NOT NULL,
|
|
495
|
+
type TEXT,
|
|
496
|
+
role TEXT,
|
|
497
|
+
status TEXT,
|
|
498
|
+
column_id TEXT,
|
|
499
|
+
meta TEXT
|
|
500
|
+
);
|
|
501
|
+
CREATE INDEX IF NOT EXISTS chunks_path ON chunks(path);
|
|
502
|
+
CREATE INDEX IF NOT EXISTS chunks_doc ON chunks(doc_id);
|
|
503
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
504
|
+
title, body, ident,
|
|
505
|
+
content='chunks', content_rowid='id',
|
|
506
|
+
tokenize='porter unicode61'
|
|
507
|
+
);
|
|
508
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
|
|
509
|
+
INSERT INTO chunks_fts(rowid, title, body, ident) VALUES (new.id, new.title, new.body, new.ident);
|
|
510
|
+
END;
|
|
511
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
|
|
512
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, title, body, ident) VALUES ('delete', old.id, old.title, old.body, old.ident);
|
|
513
|
+
END;
|
|
514
|
+
CREATE TRIGGER IF NOT EXISTS chunks_au AFTER UPDATE ON chunks BEGIN
|
|
515
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, title, body, ident) VALUES ('delete', old.id, old.title, old.body, old.ident);
|
|
516
|
+
INSERT INTO chunks_fts(rowid, title, body, ident) VALUES (new.id, new.title, new.body, new.ident);
|
|
517
|
+
END;
|
|
518
|
+
`;
|
|
519
|
+
var DROP = `
|
|
520
|
+
DROP TRIGGER IF EXISTS chunks_ai;
|
|
521
|
+
DROP TRIGGER IF EXISTS chunks_ad;
|
|
522
|
+
DROP TRIGGER IF EXISTS chunks_au;
|
|
523
|
+
DROP TABLE IF EXISTS chunks_fts;
|
|
524
|
+
DROP TABLE IF EXISTS chunks;
|
|
525
|
+
DROP TABLE IF EXISTS sources;
|
|
526
|
+
DROP TABLE IF EXISTS meta;
|
|
527
|
+
`;
|
|
528
|
+
var BM25_WEIGHTS = "4.0, 1.0, 2.0";
|
|
529
|
+
var MARKDOWN_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".mdown", ""]);
|
|
530
|
+
function sha256(data) {
|
|
531
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
532
|
+
}
|
|
533
|
+
function posixRelative(root, abs) {
|
|
534
|
+
return path2.relative(root, abs).split(path2.sep).join("/");
|
|
535
|
+
}
|
|
536
|
+
function decodeUtf8(data) {
|
|
537
|
+
if (data.includes(0))
|
|
538
|
+
return null;
|
|
539
|
+
try {
|
|
540
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function listMarkdown(dir, recursive) {
|
|
546
|
+
const out = [];
|
|
547
|
+
const visit = (current, depth) => {
|
|
548
|
+
let entries;
|
|
549
|
+
try {
|
|
550
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
551
|
+
} catch {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
for (const entry of entries) {
|
|
555
|
+
const abs = path2.join(current, entry.name);
|
|
556
|
+
if (entry.isDirectory()) {
|
|
557
|
+
if (recursive && depth < 6 && !entry.name.startsWith("."))
|
|
558
|
+
visit(abs, depth + 1);
|
|
559
|
+
} else if (entry.isFile() && /\.(md|markdown|txt)$/i.test(entry.name)) {
|
|
560
|
+
out.push(abs);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
visit(dir, 0);
|
|
565
|
+
return out.sort();
|
|
566
|
+
}
|
|
567
|
+
function frontmatterLines(text2) {
|
|
568
|
+
const lines = text2.split("\n");
|
|
569
|
+
if (lines[0]?.trim() !== "---")
|
|
570
|
+
return 0;
|
|
571
|
+
for (let i = 1; i < lines.length; i++) {
|
|
572
|
+
if (lines[i].trimEnd() === "---")
|
|
573
|
+
return i + 1 + (lines[i + 1] === "" ? 1 : 0);
|
|
574
|
+
}
|
|
575
|
+
return 0;
|
|
576
|
+
}
|
|
577
|
+
function ftsTerms(query, ignore) {
|
|
578
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
579
|
+
const terms = [];
|
|
580
|
+
const seen = /* @__PURE__ */ new Set();
|
|
581
|
+
const keep = (tokens) => {
|
|
582
|
+
const key = tokens.join(" ");
|
|
583
|
+
if (!key || seen.has(key) || terms.length >= 32)
|
|
584
|
+
return;
|
|
585
|
+
if (tokens.length === 1 && key.length < 2)
|
|
586
|
+
return;
|
|
587
|
+
seen.add(key);
|
|
588
|
+
const last = tokens[tokens.length - 1];
|
|
589
|
+
terms.push(`"${key}"${tokens.length === 1 || last.length >= 3 ? "*" : ""}`);
|
|
590
|
+
};
|
|
591
|
+
const significant = new Set((0, decisions_1.significantTokens)(query, ignore));
|
|
592
|
+
for (const word of words) {
|
|
593
|
+
const tokens = word.match(/[a-z0-9_]+/g) ?? [];
|
|
594
|
+
if (tokens.length === 0)
|
|
595
|
+
continue;
|
|
596
|
+
if (tokens.length === 1) {
|
|
597
|
+
if (significant.has(tokens[0]))
|
|
598
|
+
keep(tokens);
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (tokens.some((t) => significant.has(t)))
|
|
602
|
+
keep(tokens);
|
|
603
|
+
}
|
|
604
|
+
return terms;
|
|
605
|
+
}
|
|
606
|
+
function normalizeRank(rank) {
|
|
607
|
+
const abs = Math.abs(rank);
|
|
608
|
+
return abs / (1 + abs);
|
|
609
|
+
}
|
|
610
|
+
function pathPrior(source, rel) {
|
|
611
|
+
if (source !== "repo")
|
|
612
|
+
return 0;
|
|
613
|
+
const lower = rel.toLowerCase();
|
|
614
|
+
const base = path2.posix.basename(lower);
|
|
615
|
+
const parts = lower.split("/");
|
|
616
|
+
if (base === "agents.md" || base === "claude.md")
|
|
617
|
+
return 0.06;
|
|
618
|
+
if (parts.length === 1 && base.startsWith("readme"))
|
|
619
|
+
return 0.04;
|
|
620
|
+
if (base.includes("changelog") || base.includes("release-notes"))
|
|
621
|
+
return -0.05;
|
|
622
|
+
if (parts.some((p) => ["fixtures", "fixture", "testdata", "__tests__", "examples", "example", "public", "static"].includes(p)))
|
|
623
|
+
return -0.1;
|
|
624
|
+
if (parts.some((p) => p === "docs" || p === "doc"))
|
|
625
|
+
return 0.02;
|
|
626
|
+
return 0;
|
|
627
|
+
}
|
|
628
|
+
function parseMeta(raw) {
|
|
629
|
+
if (typeof raw !== "string" || !raw)
|
|
630
|
+
return {};
|
|
631
|
+
try {
|
|
632
|
+
const value = JSON.parse(raw);
|
|
633
|
+
return value && typeof value === "object" ? value : {};
|
|
634
|
+
} catch {
|
|
635
|
+
return {};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function text(value) {
|
|
639
|
+
if (value instanceof Date)
|
|
640
|
+
return value.toISOString();
|
|
641
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
642
|
+
}
|
|
643
|
+
var MemoryIndex = class _MemoryIndex {
|
|
644
|
+
constructor(db, dbPath, brainfilePath, projectRoot) {
|
|
645
|
+
this.statements = /* @__PURE__ */ new Map();
|
|
646
|
+
this.db = db;
|
|
647
|
+
this.path = dbPath;
|
|
648
|
+
this.brainfilePath = brainfilePath;
|
|
649
|
+
this.projectRoot = projectRoot;
|
|
650
|
+
this.dirs = (0, workspace_1.getV2Dirs)(brainfilePath);
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Open (or create) the index of a board. Null when `node:sqlite` is not
|
|
654
|
+
* available, the index is turned off, `create: false` and there is none, or
|
|
655
|
+
* the database cannot be opened even after a rebuild.
|
|
656
|
+
*/
|
|
657
|
+
static open(brainfilePath, options = {}) {
|
|
658
|
+
const sqlite = (0, sqlite_1.loadSqlite)();
|
|
659
|
+
if (!sqlite)
|
|
660
|
+
return null;
|
|
661
|
+
const resolved = path2.resolve(brainfilePath);
|
|
662
|
+
const dbPath = getIndexPath(resolved);
|
|
663
|
+
const exists = fs.existsSync(dbPath);
|
|
664
|
+
if (!exists && options.create === false)
|
|
665
|
+
return null;
|
|
666
|
+
const projectRoot = path2.resolve(options.projectRoot ?? path2.dirname(path2.dirname(resolved)));
|
|
667
|
+
const attempt = () => {
|
|
668
|
+
if (!exists) {
|
|
669
|
+
(0, files_1.ensureDotBrainfileGitignore)(resolved);
|
|
670
|
+
fs.mkdirSync(path2.dirname(dbPath), { recursive: true });
|
|
671
|
+
}
|
|
672
|
+
const db = new sqlite.DatabaseSync(dbPath);
|
|
673
|
+
try {
|
|
674
|
+
db.exec("PRAGMA busy_timeout = 3000");
|
|
675
|
+
try {
|
|
676
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
677
|
+
} catch {
|
|
678
|
+
}
|
|
679
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
680
|
+
const index = new _MemoryIndex(db, dbPath, resolved, projectRoot);
|
|
681
|
+
index.ensureSchema();
|
|
682
|
+
return index;
|
|
683
|
+
} catch (err) {
|
|
684
|
+
db.close();
|
|
685
|
+
throw err;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
try {
|
|
689
|
+
return attempt();
|
|
690
|
+
} catch {
|
|
691
|
+
try {
|
|
692
|
+
for (const suffix of ["", "-wal", "-shm"])
|
|
693
|
+
fs.rmSync(`${dbPath}${suffix}`, { force: true });
|
|
694
|
+
return attempt();
|
|
695
|
+
} catch {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
close() {
|
|
701
|
+
this.statements.clear();
|
|
702
|
+
try {
|
|
703
|
+
this.db.close();
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
stmt(sql) {
|
|
708
|
+
let statement = this.statements.get(sql);
|
|
709
|
+
if (!statement) {
|
|
710
|
+
statement = this.db.prepare(sql);
|
|
711
|
+
this.statements.set(sql, statement);
|
|
712
|
+
}
|
|
713
|
+
return statement;
|
|
714
|
+
}
|
|
715
|
+
getMeta(key) {
|
|
716
|
+
const row = this.stmt("SELECT value FROM meta WHERE key = ?").get(key);
|
|
717
|
+
return row ? String(row.value) : void 0;
|
|
718
|
+
}
|
|
719
|
+
setMeta(key, value) {
|
|
720
|
+
this.stmt("INSERT INTO meta(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
721
|
+
}
|
|
722
|
+
ensureSchema() {
|
|
723
|
+
let version;
|
|
724
|
+
try {
|
|
725
|
+
version = this.getMeta("schema_version");
|
|
726
|
+
} catch {
|
|
727
|
+
version = void 0;
|
|
728
|
+
}
|
|
729
|
+
if (version === exports.INDEX_SCHEMA_VERSION)
|
|
730
|
+
return;
|
|
731
|
+
this.statements.clear();
|
|
732
|
+
this.db.exec(DROP);
|
|
733
|
+
this.db.exec(SCHEMA);
|
|
734
|
+
this.setMeta("schema_version", exports.INDEX_SCHEMA_VERSION);
|
|
735
|
+
}
|
|
736
|
+
/** Drop everything and index from scratch. */
|
|
737
|
+
rebuild() {
|
|
738
|
+
this.statements.clear();
|
|
739
|
+
this.db.exec(DROP);
|
|
740
|
+
this.db.exec(SCHEMA);
|
|
741
|
+
this.setMeta("schema_version", exports.INDEX_SCHEMA_VERSION);
|
|
742
|
+
return this.refresh();
|
|
743
|
+
}
|
|
744
|
+
boardConfig() {
|
|
745
|
+
return (0, boardFile_1.readBoardConfig)(this.dirs.brainfilePath)?.config ?? null;
|
|
746
|
+
}
|
|
747
|
+
collectFiles() {
|
|
748
|
+
const files = [];
|
|
749
|
+
const add = (abs, source) => {
|
|
750
|
+
try {
|
|
751
|
+
const stat = fs.statSync(abs);
|
|
752
|
+
if (!stat.isFile())
|
|
753
|
+
return;
|
|
754
|
+
files.push({ path: posixRelative(this.projectRoot, abs), abs, source, size: stat.size, mtimeMs: stat.mtimeMs });
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
for (const abs of listMarkdown(this.dirs.boardDir, false))
|
|
759
|
+
if (abs.endsWith(".md"))
|
|
760
|
+
add(abs, "board");
|
|
761
|
+
for (const abs of listMarkdown(this.dirs.logsDir, false))
|
|
762
|
+
if (abs.endsWith(".md"))
|
|
763
|
+
add(abs, "logs");
|
|
764
|
+
add(path2.join(this.dirs.logsDir, "ledger.jsonl"), "ledger");
|
|
765
|
+
for (const abs of listMarkdown(path2.join(this.dirs.dotDir, "plans"), true))
|
|
766
|
+
add(abs, "plans");
|
|
767
|
+
const mined = (0, mine_1.listProjectDocs)(this.projectRoot);
|
|
768
|
+
const dotPrefix = posixRelative(this.projectRoot, this.dirs.dotDir);
|
|
769
|
+
for (const file of mined.files) {
|
|
770
|
+
if (dotPrefix && (file.rel === dotPrefix || file.rel.startsWith(`${dotPrefix}/`)))
|
|
771
|
+
continue;
|
|
772
|
+
files.push({ path: file.rel, abs: file.abs, source: "repo", size: file.size, mtimeMs: file.mtimeMs });
|
|
773
|
+
}
|
|
774
|
+
return { files, via: mined.via };
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Bring the index up to date with the files: unchanged files (same size and
|
|
778
|
+
* mtime, or same hash) are left alone, changed ones replace their chunks,
|
|
779
|
+
* deleted ones drop out. With a `deadline` (epoch ms), files not reached in
|
|
780
|
+
* time are left for the next refresh. When another process is writing the
|
|
781
|
+
* index, nothing is done and `busy` is set.
|
|
782
|
+
*/
|
|
783
|
+
refresh(options = {}) {
|
|
784
|
+
const started = Date.now();
|
|
785
|
+
const stats = { added: 0, updated: 0, unchanged: 0, removed: 0, skipped: 0, incomplete: false, busy: false, via: "none", ms: 0 };
|
|
786
|
+
const config = this.boardConfig();
|
|
787
|
+
const typesKey = sha256(JSON.stringify(config?.types ?? {}));
|
|
788
|
+
const typesChanged = this.getMeta("types_key") !== typesKey;
|
|
789
|
+
const { files, via } = this.collectFiles();
|
|
790
|
+
stats.via = via;
|
|
791
|
+
const known = /* @__PURE__ */ new Map();
|
|
792
|
+
for (const row of this.stmt("SELECT path, source, size, mtime_ms, hash FROM sources").all()) {
|
|
793
|
+
known.set(String(row.path), { size: Number(row.size), mtime: Number(row.mtime_ms), hash: String(row.hash), source: String(row.source) });
|
|
794
|
+
}
|
|
795
|
+
try {
|
|
796
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
797
|
+
} catch {
|
|
798
|
+
stats.busy = true;
|
|
799
|
+
stats.ms = Date.now() - started;
|
|
800
|
+
return stats;
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
const seen = /* @__PURE__ */ new Set();
|
|
804
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
805
|
+
for (const file of files) {
|
|
806
|
+
if (seen.has(file.path))
|
|
807
|
+
continue;
|
|
808
|
+
seen.add(file.path);
|
|
809
|
+
const prev = known.get(file.path);
|
|
810
|
+
const forced = typesChanged && (file.source === "board" || file.source === "logs");
|
|
811
|
+
if (prev && !forced && prev.source === file.source && prev.size === file.size && prev.mtime === file.mtimeMs) {
|
|
812
|
+
stats.unchanged++;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (options.deadline !== void 0 && Date.now() > options.deadline) {
|
|
816
|
+
stats.incomplete = true;
|
|
817
|
+
stats.skipped++;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
let data;
|
|
821
|
+
try {
|
|
822
|
+
data = fs.readFileSync(file.abs);
|
|
823
|
+
} catch {
|
|
824
|
+
stats.skipped++;
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
const hash = sha256(data);
|
|
828
|
+
if (prev && !forced && prev.source === file.source && prev.hash === hash) {
|
|
829
|
+
this.stmt("UPDATE sources SET size = ?, mtime_ms = ? WHERE path = ?").run(file.size, file.mtimeMs, file.path);
|
|
830
|
+
stats.unchanged++;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const content = decodeUtf8(data);
|
|
834
|
+
const rows = content === null ? [] : this.chunksFor(file, content, config?.types);
|
|
835
|
+
this.stmt("DELETE FROM chunks WHERE path = ?").run(file.path);
|
|
836
|
+
const insert = this.stmt(`INSERT INTO chunks
|
|
837
|
+
(path, source, doc_id, title, body, ident, heading, start_line, end_line, lane, type, role, status, column_id, meta)
|
|
838
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
839
|
+
for (const row of rows) {
|
|
840
|
+
insert.run(row.path, row.source, row.docId, row.title, row.body, row.ident, row.heading, row.startLine, row.endLine, row.lane, row.type, row.role, row.status, row.column, row.meta);
|
|
841
|
+
}
|
|
842
|
+
this.stmt(`INSERT INTO sources(path, source, size, mtime_ms, hash, indexed_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
843
|
+
ON CONFLICT(path) DO UPDATE SET source = excluded.source, size = excluded.size, mtime_ms = excluded.mtime_ms,
|
|
844
|
+
hash = excluded.hash, indexed_at = excluded.indexed_at`).run(file.path, file.source, file.size, file.mtimeMs, hash, now);
|
|
845
|
+
if (prev)
|
|
846
|
+
stats.updated++;
|
|
847
|
+
else
|
|
848
|
+
stats.added++;
|
|
849
|
+
}
|
|
850
|
+
for (const key of known.keys()) {
|
|
851
|
+
if (seen.has(key))
|
|
852
|
+
continue;
|
|
853
|
+
this.stmt("DELETE FROM chunks WHERE path = ?").run(key);
|
|
854
|
+
this.stmt("DELETE FROM sources WHERE path = ?").run(key);
|
|
855
|
+
stats.removed++;
|
|
856
|
+
}
|
|
857
|
+
if (!stats.incomplete)
|
|
858
|
+
this.setMeta("types_key", typesKey);
|
|
859
|
+
this.setMeta("last_refresh", now);
|
|
860
|
+
this.db.exec("COMMIT");
|
|
861
|
+
} catch (err) {
|
|
862
|
+
try {
|
|
863
|
+
this.db.exec("ROLLBACK");
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
throw err;
|
|
867
|
+
}
|
|
868
|
+
stats.ms = Date.now() - started;
|
|
869
|
+
return stats;
|
|
870
|
+
}
|
|
871
|
+
chunksFor(file, content, types) {
|
|
872
|
+
if (file.source === "ledger")
|
|
873
|
+
return this.ledgerChunks(file, content);
|
|
874
|
+
if (file.source === "board" || file.source === "logs") {
|
|
875
|
+
const rows = this.boardChunks(file, content, types);
|
|
876
|
+
if (rows)
|
|
877
|
+
return rows;
|
|
878
|
+
}
|
|
879
|
+
return this.fileChunks(file, content);
|
|
880
|
+
}
|
|
881
|
+
boardChunks(file, content, types) {
|
|
882
|
+
const parsed = (0, frontmatter_1.parseFrontmatter)(content);
|
|
883
|
+
if (!parsed || !parsed.data || typeof parsed.data.id !== "string" || typeof parsed.data.title !== "string")
|
|
884
|
+
return null;
|
|
885
|
+
const task = parsed.data;
|
|
886
|
+
const resolved = (0, docTypes_1.resolveDocType)(task.type, types);
|
|
887
|
+
const isDecision = resolved.role === "decision";
|
|
888
|
+
const status = isDecision ? (0, docGraph_1.decisionStatus)(task) : file.source === "logs" || task.completedAt ? "completed" : "open";
|
|
889
|
+
const lane = isDecision ? (0, decisions_1.isHistoricalStatus)(status) ? "history" : "current" : status === "completed" ? "history" : "current";
|
|
890
|
+
const record = task;
|
|
891
|
+
const claimKey = (0, decisions_1.claimKeyOf)(task);
|
|
892
|
+
const tags = Array.isArray(task.tags) ? task.tags.filter((t) => typeof t === "string") : [];
|
|
893
|
+
const ident = [
|
|
894
|
+
claimKey ? claimKey.replace(/[.-]/g, " ") : "",
|
|
895
|
+
...tags,
|
|
896
|
+
text(record.subject) ?? "",
|
|
897
|
+
text(record.kind) ?? "",
|
|
898
|
+
text(record.stance) ?? ""
|
|
899
|
+
].filter(Boolean).join(" ");
|
|
900
|
+
const meta = JSON.stringify({
|
|
901
|
+
...task.priority ? { priority: task.priority } : {},
|
|
902
|
+
...tags.length ? { tags } : {},
|
|
903
|
+
...text(task.assignee) ? { assignee: text(task.assignee) } : {},
|
|
904
|
+
...text(task.dueDate) ? { dueDate: text(task.dueDate) } : {},
|
|
905
|
+
...task.contract?.status ? { contract: task.contract.status } : {},
|
|
906
|
+
...text(task.completedAt) ? { completedAt: text(task.completedAt) } : {},
|
|
907
|
+
...claimKey ? { claimKey } : {},
|
|
908
|
+
...text(task.parentId) ? { parentId: text(task.parentId) } : {},
|
|
909
|
+
...text(record.supersededBy) ? { supersededBy: text(record.supersededBy) } : {}
|
|
910
|
+
});
|
|
911
|
+
const description = text(task.description);
|
|
912
|
+
const offset = frontmatterLines(content);
|
|
913
|
+
const chunks = (0, chunk_1.chunkText)(parsed.body, { lineOffset: offset });
|
|
914
|
+
if (chunks.length === 0)
|
|
915
|
+
chunks.push({ text: "", startLine: 1, endLine: Math.max(1, offset) });
|
|
916
|
+
if (description)
|
|
917
|
+
chunks[0] = { ...chunks[0], text: `${description}
|
|
918
|
+
|
|
919
|
+
${chunks[0].text}`.trim() };
|
|
920
|
+
return chunks.map((chunk) => ({
|
|
921
|
+
path: file.path,
|
|
922
|
+
source: file.source,
|
|
923
|
+
docId: task.id,
|
|
924
|
+
title: task.title,
|
|
925
|
+
body: chunk.text,
|
|
926
|
+
ident,
|
|
927
|
+
heading: chunk.heading ?? null,
|
|
928
|
+
startLine: chunk.startLine,
|
|
929
|
+
endLine: chunk.endLine,
|
|
930
|
+
lane,
|
|
931
|
+
type: resolved.name === "task" ? null : resolved.name,
|
|
932
|
+
role: resolved.role,
|
|
933
|
+
status,
|
|
934
|
+
column: isDecision || file.source === "logs" ? null : text(task.column) ?? null,
|
|
935
|
+
meta
|
|
936
|
+
}));
|
|
937
|
+
}
|
|
938
|
+
ledgerChunks(file, content) {
|
|
939
|
+
const rows = [];
|
|
940
|
+
content.split("\n").forEach((line, index) => {
|
|
941
|
+
if (!line.trim())
|
|
942
|
+
return;
|
|
943
|
+
let record;
|
|
944
|
+
try {
|
|
945
|
+
record = JSON.parse(line);
|
|
946
|
+
} catch {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
if (!record || typeof record.id !== "string")
|
|
950
|
+
return;
|
|
951
|
+
const files = Array.isArray(record.filesChanged) ? record.filesChanged.join(" ") : "";
|
|
952
|
+
const tags = Array.isArray(record.tags) ? record.tags.filter((t) => typeof t === "string") : [];
|
|
953
|
+
rows.push({
|
|
954
|
+
path: file.path,
|
|
955
|
+
source: "ledger",
|
|
956
|
+
docId: record.id,
|
|
957
|
+
title: typeof record.title === "string" ? record.title : record.id,
|
|
958
|
+
body: [typeof record.summary === "string" ? record.summary : "", files ? `Files: ${files}` : ""].filter(Boolean).join("\n"),
|
|
959
|
+
ident: tags.join(" "),
|
|
960
|
+
heading: null,
|
|
961
|
+
startLine: index + 1,
|
|
962
|
+
endLine: index + 1,
|
|
963
|
+
lane: "history",
|
|
964
|
+
type: typeof record.type === "string" && record.type !== "task" ? record.type : null,
|
|
965
|
+
role: record.type === "adr" ? "decision" : "task",
|
|
966
|
+
status: "completed",
|
|
967
|
+
column: null,
|
|
968
|
+
meta: JSON.stringify({
|
|
969
|
+
...record.priority ? { priority: record.priority } : {},
|
|
970
|
+
...tags.length ? { tags } : {},
|
|
971
|
+
...text(record.assignee) ? { assignee: text(record.assignee) } : {},
|
|
972
|
+
...text(record.completedAt) ? { completedAt: text(record.completedAt) } : {},
|
|
973
|
+
...record.contractStatus ? { contract: record.contractStatus } : {}
|
|
974
|
+
})
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
return rows;
|
|
978
|
+
}
|
|
979
|
+
fileChunks(file, content) {
|
|
980
|
+
const ext = path2.posix.extname(file.path).toLowerCase();
|
|
981
|
+
const markdown = MARKDOWN_EXTENSIONS.has(ext);
|
|
982
|
+
const docTitle = (markdown ? (0, chunk_1.firstHeading)(content) : void 0) ?? path2.posix.basename(file.path);
|
|
983
|
+
return (0, chunk_1.chunkText)(content, { markdown }).map((chunk) => ({
|
|
984
|
+
path: file.path,
|
|
985
|
+
source: file.source,
|
|
986
|
+
docId: null,
|
|
987
|
+
title: chunk.heading && chunk.heading !== docTitle ? `${docTitle} \u203A ${chunk.heading}` : docTitle,
|
|
988
|
+
body: chunk.text,
|
|
989
|
+
ident: file.path.replace(/[/._-]+/g, " "),
|
|
990
|
+
heading: chunk.heading ?? null,
|
|
991
|
+
startLine: chunk.startLine,
|
|
992
|
+
endLine: chunk.endLine,
|
|
993
|
+
lane: "current",
|
|
994
|
+
type: null,
|
|
995
|
+
role: null,
|
|
996
|
+
status: null,
|
|
997
|
+
column: null,
|
|
998
|
+
meta: null
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
/** Counts by source, size on disk and the last refresh. */
|
|
1002
|
+
status() {
|
|
1003
|
+
const empty = () => ({ board: 0, logs: 0, ledger: 0, plans: 0, repo: 0 });
|
|
1004
|
+
const files = empty();
|
|
1005
|
+
const chunks = empty();
|
|
1006
|
+
for (const row of this.stmt("SELECT source, COUNT(*) AS n FROM sources GROUP BY source").all()) {
|
|
1007
|
+
files[String(row.source)] = Number(row.n);
|
|
1008
|
+
}
|
|
1009
|
+
for (const row of this.stmt("SELECT source, COUNT(*) AS n FROM chunks GROUP BY source").all()) {
|
|
1010
|
+
chunks[String(row.source)] = Number(row.n);
|
|
1011
|
+
}
|
|
1012
|
+
let sizeBytes = 0;
|
|
1013
|
+
for (const suffix of ["", "-wal"]) {
|
|
1014
|
+
try {
|
|
1015
|
+
sizeBytes += fs.statSync(`${this.path}${suffix}`).size;
|
|
1016
|
+
} catch {
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
const lastRefresh = this.getMeta("last_refresh");
|
|
1020
|
+
return {
|
|
1021
|
+
path: this.path,
|
|
1022
|
+
projectRoot: this.projectRoot,
|
|
1023
|
+
schemaVersion: exports.INDEX_SCHEMA_VERSION,
|
|
1024
|
+
sizeBytes,
|
|
1025
|
+
...lastRefresh ? { lastRefresh } : {},
|
|
1026
|
+
files,
|
|
1027
|
+
chunks,
|
|
1028
|
+
totalFiles: Object.values(files).reduce((a, b) => a + b, 0),
|
|
1029
|
+
totalChunks: Object.values(chunks).reduce((a, b) => a + b, 0)
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Search the index. Null when the query has no words to search on (a
|
|
1034
|
+
* filter-only query such as `p:high`): the caller falls back to the scan.
|
|
1035
|
+
*/
|
|
1036
|
+
search(query, options = {}) {
|
|
1037
|
+
const limit = options.limit && options.limit > 0 ? options.limit : 20;
|
|
1038
|
+
const parsed = (0, search_1.parseSearchQuery)(query);
|
|
1039
|
+
const filters = {};
|
|
1040
|
+
for (const [key, value] of Object.entries({ ...parsed, ...definedOnly(options.filters) })) {
|
|
1041
|
+
if (key !== "text" && value !== void 0 && value !== "")
|
|
1042
|
+
filters[key] = value;
|
|
1043
|
+
}
|
|
1044
|
+
const terms = ftsTerms(parsed.text, options.ignoreWords);
|
|
1045
|
+
if (terms.length === 0)
|
|
1046
|
+
return null;
|
|
1047
|
+
const exactId = parsed.text.trim();
|
|
1048
|
+
const structural = Object.keys(filters).length > 0;
|
|
1049
|
+
let sources = (options.sources ?? exports.INDEX_SOURCES).filter((s) => exports.INDEX_SOURCES.includes(s));
|
|
1050
|
+
if (structural)
|
|
1051
|
+
sources = sources.filter((s) => s === "board" || s === "logs" || s === "ledger");
|
|
1052
|
+
if (filters.column)
|
|
1053
|
+
sources = sources.filter((s) => s === "board");
|
|
1054
|
+
if (sources.length === 0)
|
|
1055
|
+
return { current: [], history: [] };
|
|
1056
|
+
const where = [`chunks.source IN (${sources.map(() => "?").join(", ")})`];
|
|
1057
|
+
const params = [...sources];
|
|
1058
|
+
if (options.role === "decision")
|
|
1059
|
+
where.push(`chunks.role = 'decision'`);
|
|
1060
|
+
if (options.role === "work")
|
|
1061
|
+
where.push(`(chunks.role IS NULL OR chunks.role != 'decision')`);
|
|
1062
|
+
const sql = `SELECT chunks.*, bm25(chunks_fts, ${BM25_WEIGHTS}) AS rank,
|
|
1063
|
+
snippet(chunks_fts, 1, '', '', '\u2026', 16) AS snippet
|
|
1064
|
+
FROM chunks_fts JOIN chunks ON chunks.id = chunks_fts.rowid
|
|
1065
|
+
WHERE chunks_fts MATCH ? AND ${where.join(" AND ")}
|
|
1066
|
+
ORDER BY rank LIMIT ?`;
|
|
1067
|
+
const sqlLimit = Math.max(limit * 6, 60);
|
|
1068
|
+
const best = /* @__PURE__ */ new Map();
|
|
1069
|
+
const coverageTokens = options.minCoverage ? (0, decisions_1.significantTokens)(parsed.text, options.ignoreWords) : [];
|
|
1070
|
+
const consider = (row, strict) => {
|
|
1071
|
+
if (!strict && options.minCoverage && (0, decisions_1.tokenCoverage)(coverageTokens, `${row.title}
|
|
1072
|
+
${row.body}
|
|
1073
|
+
${row.ident}`) < options.minCoverage)
|
|
1074
|
+
return;
|
|
1075
|
+
const hit = this.hitFromRow(row);
|
|
1076
|
+
if (structural && !this.rowMatchesFilters(hit, row, filters))
|
|
1077
|
+
return;
|
|
1078
|
+
hit.score = normalizeRank(Number(row.rank)) + (strict ? 1 : 0) + pathPrior(hit.source, hit.path) + (hit.role === "decision" && hit.lane === "current" ? 0.05 : 0) + (exactId && hit.id.toLowerCase() === exactId ? 10 : 0);
|
|
1079
|
+
hit.score = Math.round(hit.score * 1e4) / 1e4;
|
|
1080
|
+
const key = hit.source === "board" || hit.source === "logs" || hit.source === "ledger" ? `doc:${hit.id}` : `file:${hit.path}`;
|
|
1081
|
+
const existing = best.get(key);
|
|
1082
|
+
if (!existing || existing.source === "ledger" && hit.source !== "ledger" || hit.score > existing.score && !(hit.source === "ledger" && existing.source !== "ledger")) {
|
|
1083
|
+
best.set(key, hit);
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
const run = (match, strict) => {
|
|
1087
|
+
for (const row of this.stmt(sql).all(match, ...params, sqlLimit))
|
|
1088
|
+
consider(row, strict);
|
|
1089
|
+
};
|
|
1090
|
+
run(terms.join(" AND "), true);
|
|
1091
|
+
if (best.size < limit && terms.length > 1)
|
|
1092
|
+
run(terms.join(" OR "), false);
|
|
1093
|
+
if (exactId && /^[a-z0-9][\w-]*-\d+$/i.test(exactId) && !best.has(`doc:${exactId}`)) {
|
|
1094
|
+
const row = this.db.prepare(`SELECT chunks.*, 0 AS rank, NULL AS snippet FROM chunks
|
|
1095
|
+
WHERE doc_id = ? AND source IN (${sources.map(() => "?").join(", ")}) ORDER BY start_line LIMIT 1`).get(exactId, ...sources);
|
|
1096
|
+
if (row)
|
|
1097
|
+
consider(row, true);
|
|
1098
|
+
}
|
|
1099
|
+
const hits = [...best.values()].sort((a, b) => b.score - a.score);
|
|
1100
|
+
return {
|
|
1101
|
+
current: hits.filter((h) => h.lane === "current").slice(0, limit),
|
|
1102
|
+
history: hits.filter((h) => h.lane === "history").slice(0, limit)
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
hitFromRow(row) {
|
|
1106
|
+
const meta = parseMeta(row.meta);
|
|
1107
|
+
const source = String(row.source);
|
|
1108
|
+
const rel = String(row.path);
|
|
1109
|
+
const start = Number(row.start_line);
|
|
1110
|
+
const end = Number(row.end_line);
|
|
1111
|
+
const docId = typeof row.doc_id === "string" && row.doc_id ? row.doc_id : null;
|
|
1112
|
+
const snippet = typeof row.snippet === "string" && row.snippet.trim() ? row.snippet.replace(/\s+/g, " ").trim() : void 0;
|
|
1113
|
+
return {
|
|
1114
|
+
id: docId ?? `${rel}:${start}-${end}`,
|
|
1115
|
+
title: String(row.title),
|
|
1116
|
+
source,
|
|
1117
|
+
lane: String(row.lane) === "history" ? "history" : "current",
|
|
1118
|
+
path: rel,
|
|
1119
|
+
startLine: start,
|
|
1120
|
+
endLine: end,
|
|
1121
|
+
score: 0,
|
|
1122
|
+
...snippet ? { snippet } : {},
|
|
1123
|
+
...typeof row.type === "string" && row.type ? { type: row.type } : {},
|
|
1124
|
+
...typeof row.role === "string" && row.role ? { role: row.role } : {},
|
|
1125
|
+
...typeof row.status === "string" && row.status ? { status: row.status } : {},
|
|
1126
|
+
...typeof row.column_id === "string" && row.column_id ? { column: row.column_id } : {},
|
|
1127
|
+
...typeof meta.completedAt === "string" ? { completedAt: meta.completedAt } : {},
|
|
1128
|
+
...typeof meta.claimKey === "string" ? { claimKey: meta.claimKey } : {},
|
|
1129
|
+
...typeof meta.priority === "string" ? { priority: meta.priority } : {},
|
|
1130
|
+
...Array.isArray(meta.tags) ? { tags: meta.tags } : {},
|
|
1131
|
+
...typeof meta.assignee === "string" ? { assignee: meta.assignee } : {},
|
|
1132
|
+
isLog: source === "logs" || source === "ledger"
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
rowMatchesFilters(hit, row, filters) {
|
|
1136
|
+
if (hit.source === "repo" || hit.source === "plans")
|
|
1137
|
+
return false;
|
|
1138
|
+
const meta = parseMeta(row.meta);
|
|
1139
|
+
const pseudo = {
|
|
1140
|
+
id: hit.id,
|
|
1141
|
+
title: hit.title,
|
|
1142
|
+
...hit.type ? { type: hit.type } : {},
|
|
1143
|
+
...hit.column ? { column: hit.column } : {},
|
|
1144
|
+
...hit.priority ? { priority: hit.priority } : {},
|
|
1145
|
+
...hit.tags ? { tags: hit.tags } : {},
|
|
1146
|
+
...hit.assignee ? { assignee: hit.assignee } : {},
|
|
1147
|
+
...typeof meta.dueDate === "string" ? { dueDate: meta.dueDate } : {},
|
|
1148
|
+
...typeof meta.contract === "string" ? { contract: { status: meta.contract } } : {}
|
|
1149
|
+
};
|
|
1150
|
+
return (0, search_1.taskMatchesFilters)(pseudo, filters);
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Documents that share a text's most distinctive words: the query's terms
|
|
1154
|
+
* that occur in `sources`, rarest first; the three rarest ANDed, then two,
|
|
1155
|
+
* then one, until something matches. How the brief finds the docs that
|
|
1156
|
+
* bear on a task from its title, where an OR over every word would match
|
|
1157
|
+
* anything that says "board".
|
|
1158
|
+
*/
|
|
1159
|
+
relatedDocs(text2, options = {}) {
|
|
1160
|
+
const limit = options.limit ?? 2;
|
|
1161
|
+
const sources = (options.sources ?? ["repo", "plans"]).filter((s) => exports.INDEX_SOURCES.includes(s));
|
|
1162
|
+
if (sources.length === 0)
|
|
1163
|
+
return [];
|
|
1164
|
+
const placeholders = sources.map(() => "?").join(", ");
|
|
1165
|
+
const countSql = `SELECT count(*) AS n FROM chunks_fts JOIN chunks ON chunks.id = chunks_fts.rowid
|
|
1166
|
+
WHERE chunks_fts MATCH ? AND chunks.source IN (${placeholders})`;
|
|
1167
|
+
const ranked = ftsTerms(text2).map((term) => ({ term, n: Number(this.stmt(countSql).get(term, ...sources)?.n ?? 0) })).filter((t) => t.n > 0).sort((a, b) => a.n - b.n);
|
|
1168
|
+
const sql = `SELECT chunks.*, bm25(chunks_fts, ${BM25_WEIGHTS}) AS rank,
|
|
1169
|
+
snippet(chunks_fts, 1, '', '', '\u2026', 16) AS snippet
|
|
1170
|
+
FROM chunks_fts JOIN chunks ON chunks.id = chunks_fts.rowid
|
|
1171
|
+
WHERE chunks_fts MATCH ? AND chunks.source IN (${placeholders})
|
|
1172
|
+
ORDER BY rank LIMIT ?`;
|
|
1173
|
+
for (let k = Math.min(3, ranked.length); k >= 1; k--) {
|
|
1174
|
+
const match = ranked.slice(0, k).map((t) => t.term).join(" AND ");
|
|
1175
|
+
const best = /* @__PURE__ */ new Map();
|
|
1176
|
+
for (const row of this.stmt(sql).all(match, ...sources, limit * 4)) {
|
|
1177
|
+
const hit = this.hitFromRow(row);
|
|
1178
|
+
hit.score = Math.round((normalizeRank(Number(row.rank)) + k + pathPrior(hit.source, hit.path)) * 1e4) / 1e4;
|
|
1179
|
+
const existing = best.get(hit.path);
|
|
1180
|
+
if (!existing || hit.score > existing.score)
|
|
1181
|
+
best.set(hit.path, hit);
|
|
1182
|
+
}
|
|
1183
|
+
if (best.size > 0)
|
|
1184
|
+
return [...best.values()].sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1185
|
+
}
|
|
1186
|
+
return [];
|
|
1187
|
+
}
|
|
1188
|
+
/** The decisions whose words best match a text, best first (any status): how `check` finds a claim family. */
|
|
1189
|
+
decisionCandidates(query, options = {}) {
|
|
1190
|
+
const result = this.search(query, {
|
|
1191
|
+
limit: options.limit ?? 8,
|
|
1192
|
+
sources: ["board", "logs"],
|
|
1193
|
+
role: "decision",
|
|
1194
|
+
...options.ignoreWords ? { ignoreWords: options.ignoreWords } : {}
|
|
1195
|
+
});
|
|
1196
|
+
if (!result)
|
|
1197
|
+
return [];
|
|
1198
|
+
return [...result.current, ...result.history].sort((a, b) => b.score - a.score).map((hit) => hit.id);
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
exports.MemoryIndex = MemoryIndex;
|
|
1202
|
+
function definedOnly(value) {
|
|
1203
|
+
const out = {};
|
|
1204
|
+
for (const [key, v] of Object.entries(value ?? {})) {
|
|
1205
|
+
if (v !== void 0)
|
|
1206
|
+
out[key] = v;
|
|
1207
|
+
}
|
|
1208
|
+
return out;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
// ../core/dist/memory/search.js
|
|
1214
|
+
var require_search2 = __commonJS({
|
|
1215
|
+
"../core/dist/memory/search.js"(exports) {
|
|
1216
|
+
"use strict";
|
|
1217
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1218
|
+
if (k2 === void 0) k2 = k;
|
|
1219
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1220
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1221
|
+
desc = { enumerable: true, get: function() {
|
|
1222
|
+
return m[k];
|
|
1223
|
+
} };
|
|
1224
|
+
}
|
|
1225
|
+
Object.defineProperty(o, k2, desc);
|
|
1226
|
+
}) : (function(o, m, k, k2) {
|
|
1227
|
+
if (k2 === void 0) k2 = k;
|
|
1228
|
+
o[k2] = m[k];
|
|
1229
|
+
}));
|
|
1230
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1231
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1232
|
+
}) : function(o, v) {
|
|
1233
|
+
o["default"] = v;
|
|
1234
|
+
});
|
|
1235
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
1236
|
+
var ownKeys = function(o) {
|
|
1237
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1238
|
+
var ar = [];
|
|
1239
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1240
|
+
return ar;
|
|
1241
|
+
};
|
|
1242
|
+
return ownKeys(o);
|
|
1243
|
+
};
|
|
1244
|
+
return function(mod) {
|
|
1245
|
+
if (mod && mod.__esModule) return mod;
|
|
1246
|
+
var result = {};
|
|
1247
|
+
if (mod != null) {
|
|
1248
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1249
|
+
}
|
|
1250
|
+
__setModuleDefault(result, mod);
|
|
1251
|
+
return result;
|
|
1252
|
+
};
|
|
1253
|
+
})();
|
|
1254
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1255
|
+
exports.hitFromDocument = hitFromDocument;
|
|
1256
|
+
exports.scanSearch = scanSearch;
|
|
1257
|
+
exports.searchMemory = searchMemory;
|
|
1258
|
+
var fs = __importStar(__require("fs"));
|
|
1259
|
+
var path2 = __importStar(__require("path"));
|
|
1260
|
+
var boardFile_1 = require_boardFile();
|
|
1261
|
+
var taskFile_1 = require_taskFile();
|
|
1262
|
+
var workspace_1 = require_workspace();
|
|
1263
|
+
var docTypes_1 = require_docTypes();
|
|
1264
|
+
var docGraph_1 = require_docGraph();
|
|
1265
|
+
var decisions_1 = require_decisions();
|
|
1266
|
+
var search_1 = require_search();
|
|
1267
|
+
var memoryIndex_1 = require_memoryIndex();
|
|
1268
|
+
function lineCount(filePath) {
|
|
1269
|
+
if (!filePath)
|
|
1270
|
+
return 1;
|
|
1271
|
+
try {
|
|
1272
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1273
|
+
const lines = content.split("\n");
|
|
1274
|
+
return Math.max(1, lines[lines.length - 1] === "" ? lines.length - 1 : lines.length);
|
|
1275
|
+
} catch {
|
|
1276
|
+
return 1;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
function hitFromDocument(doc, source, score, projectRoot, types) {
|
|
1280
|
+
const task = doc.task;
|
|
1281
|
+
const resolved = (0, docTypes_1.resolveDocType)(task.type, types);
|
|
1282
|
+
const isDecision = resolved.role === "decision";
|
|
1283
|
+
const status = isDecision ? (0, docGraph_1.decisionStatus)(task) : source === "logs" || task.completedAt ? "completed" : "open";
|
|
1284
|
+
const lane = isDecision ? (0, decisions_1.isHistoricalStatus)(status) ? "history" : "current" : status === "completed" ? "history" : "current";
|
|
1285
|
+
const rel = doc.filePath ? path2.relative(projectRoot, doc.filePath).split(path2.sep).join("/") : `${source}/${doc.task.id}.md`;
|
|
1286
|
+
const claimKey = typeof task.claimKey === "string" && task.claimKey.trim() ? task.claimKey.trim().toLowerCase() : void 0;
|
|
1287
|
+
return {
|
|
1288
|
+
id: task.id,
|
|
1289
|
+
title: task.title,
|
|
1290
|
+
source,
|
|
1291
|
+
lane,
|
|
1292
|
+
path: rel,
|
|
1293
|
+
startLine: 1,
|
|
1294
|
+
endLine: lineCount(doc.filePath),
|
|
1295
|
+
score,
|
|
1296
|
+
...resolved.name !== "task" ? { type: resolved.name } : {},
|
|
1297
|
+
role: resolved.role,
|
|
1298
|
+
status,
|
|
1299
|
+
...!isDecision && source === "board" && task.column ? { column: task.column } : {},
|
|
1300
|
+
...typeof task.completedAt === "string" ? { completedAt: task.completedAt } : {},
|
|
1301
|
+
...claimKey ? { claimKey } : {},
|
|
1302
|
+
...task.priority ? { priority: task.priority } : {},
|
|
1303
|
+
...Array.isArray(task.tags) && task.tags.length ? { tags: task.tags } : {},
|
|
1304
|
+
...typeof task.assignee === "string" && task.assignee ? { assignee: task.assignee } : {},
|
|
1305
|
+
isLog: source === "logs"
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
function scanSearch(brainfilePath, query, options = {}) {
|
|
1309
|
+
const dirs = (0, workspace_1.getV2Dirs)(brainfilePath);
|
|
1310
|
+
const projectRoot = path2.dirname(dirs.dotDir);
|
|
1311
|
+
const types = (0, boardFile_1.readBoardConfig)(dirs.brainfilePath)?.config.types;
|
|
1312
|
+
const limit = options.limit && options.limit > 0 ? options.limit : 20;
|
|
1313
|
+
const filters = options.filters ?? {};
|
|
1314
|
+
const wants = (source) => !options.sources || options.sources.includes(source);
|
|
1315
|
+
const hits = [];
|
|
1316
|
+
if (wants("board")) {
|
|
1317
|
+
for (const { doc, score } of (0, search_1.searchTasksRanked)((0, taskFile_1.readTasksDir)(dirs.boardDir), query, filters)) {
|
|
1318
|
+
hits.push(hitFromDocument(doc, "board", score, projectRoot, types));
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (wants("logs") && !filters.column) {
|
|
1322
|
+
const { column: _column, ...rest } = filters;
|
|
1323
|
+
for (const { doc, score } of (0, search_1.searchTasksRanked)((0, taskFile_1.readTasksDir)(dirs.logsDir), query, rest)) {
|
|
1324
|
+
hits.push(hitFromDocument(doc, "logs", score, projectRoot, types));
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const sorted = hits.map((hit, order) => ({ hit, order })).sort((a, b) => b.hit.score - a.hit.score || a.order - b.order).map(({ hit }) => hit);
|
|
1328
|
+
const current = sorted.filter((h) => h.lane === "current").slice(0, limit);
|
|
1329
|
+
const history = sorted.filter((h) => h.lane === "history").slice(0, limit);
|
|
1330
|
+
const results = [...current, ...history];
|
|
1331
|
+
return { mode: "scan", query, current, history, results, count: results.length };
|
|
1332
|
+
}
|
|
1333
|
+
function searchMemory(brainfilePath, query, options = {}) {
|
|
1334
|
+
if (!options.scan) {
|
|
1335
|
+
const index = memoryIndex_1.MemoryIndex.open(brainfilePath);
|
|
1336
|
+
if (index) {
|
|
1337
|
+
try {
|
|
1338
|
+
const refresh = options.refresh === false ? void 0 : index.refresh();
|
|
1339
|
+
const found = index.search(query, options);
|
|
1340
|
+
if (found) {
|
|
1341
|
+
const results = [...found.current, ...found.history];
|
|
1342
|
+
return {
|
|
1343
|
+
mode: "index",
|
|
1344
|
+
query,
|
|
1345
|
+
current: found.current,
|
|
1346
|
+
history: found.history,
|
|
1347
|
+
results,
|
|
1348
|
+
count: results.length,
|
|
1349
|
+
...refresh ? { refresh } : {}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
} catch {
|
|
1353
|
+
} finally {
|
|
1354
|
+
index.close();
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
return scanSearch(brainfilePath, query, options);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
// ../core/dist/memory/brief.js
|
|
1364
|
+
var require_brief2 = __commonJS({
|
|
1365
|
+
"../core/dist/memory/brief.js"(exports) {
|
|
1366
|
+
"use strict";
|
|
1367
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1368
|
+
if (k2 === void 0) k2 = k;
|
|
1369
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1370
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1371
|
+
desc = { enumerable: true, get: function() {
|
|
1372
|
+
return m[k];
|
|
1373
|
+
} };
|
|
1374
|
+
}
|
|
1375
|
+
Object.defineProperty(o, k2, desc);
|
|
1376
|
+
}) : (function(o, m, k, k2) {
|
|
1377
|
+
if (k2 === void 0) k2 = k;
|
|
1378
|
+
o[k2] = m[k];
|
|
1379
|
+
}));
|
|
1380
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1381
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1382
|
+
}) : function(o, v) {
|
|
1383
|
+
o["default"] = v;
|
|
1384
|
+
});
|
|
1385
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
1386
|
+
var ownKeys = function(o) {
|
|
1387
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1388
|
+
var ar = [];
|
|
1389
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1390
|
+
return ar;
|
|
1391
|
+
};
|
|
1392
|
+
return ownKeys(o);
|
|
1393
|
+
};
|
|
1394
|
+
return function(mod) {
|
|
1395
|
+
if (mod && mod.__esModule) return mod;
|
|
1396
|
+
var result = {};
|
|
1397
|
+
if (mod != null) {
|
|
1398
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1399
|
+
}
|
|
1400
|
+
__setModuleDefault(result, mod);
|
|
1401
|
+
return result;
|
|
1402
|
+
};
|
|
1403
|
+
})();
|
|
1404
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1405
|
+
exports.DEFAULT_BRIEF_MAX_CHARS = void 0;
|
|
1406
|
+
exports.composeBrief = composeBrief2;
|
|
1407
|
+
exports.briefItemLine = briefItemLine;
|
|
1408
|
+
exports.renderBriefText = renderBriefText;
|
|
1409
|
+
var path2 = __importStar(__require("path"));
|
|
1410
|
+
var brief_1 = require_brief();
|
|
1411
|
+
var boardFile_1 = require_boardFile();
|
|
1412
|
+
var taskFile_1 = require_taskFile();
|
|
1413
|
+
var docTypes_1 = require_docTypes();
|
|
1414
|
+
var docGraph_1 = require_docGraph();
|
|
1415
|
+
var decisions_1 = require_decisions();
|
|
1416
|
+
var decisionOperations_1 = require_decisionOperations();
|
|
1417
|
+
var memoryIndex_1 = require_memoryIndex();
|
|
1418
|
+
exports.DEFAULT_BRIEF_MAX_CHARS = 1600;
|
|
1419
|
+
var MAX_LINE = 180;
|
|
1420
|
+
function parseMs(value) {
|
|
1421
|
+
if (typeof value !== "string")
|
|
1422
|
+
return null;
|
|
1423
|
+
const ms = Date.parse(value);
|
|
1424
|
+
return Number.isNaN(ms) ? null : ms;
|
|
1425
|
+
}
|
|
1426
|
+
function isAssignedTo(task, agent) {
|
|
1427
|
+
return typeof task.assignee === "string" && task.assignee.trim().toLowerCase() === agent.trim().toLowerCase();
|
|
1428
|
+
}
|
|
1429
|
+
var PRIORITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
1430
|
+
var IN_FLIGHT = /progress|doing|review|active|wip|working/i;
|
|
1431
|
+
function workWhy(task, config) {
|
|
1432
|
+
const column = config?.columns?.find((c) => c.id === task.column);
|
|
1433
|
+
const parts = [column?.title ?? task.column ?? ""];
|
|
1434
|
+
if (task.priority)
|
|
1435
|
+
parts.push(task.priority);
|
|
1436
|
+
if (task.contract?.status)
|
|
1437
|
+
parts.push(`contract:${task.contract.status}`);
|
|
1438
|
+
return parts.filter(Boolean).join(" \xB7 ");
|
|
1439
|
+
}
|
|
1440
|
+
function sortWork(tasks, config) {
|
|
1441
|
+
const order = (config?.columns ?? []).map((c) => c.id);
|
|
1442
|
+
const rank = (t) => {
|
|
1443
|
+
const i = order.indexOf(t.column ?? "");
|
|
1444
|
+
return i === -1 ? -1 : i;
|
|
1445
|
+
};
|
|
1446
|
+
return [...tasks].sort((a, b) => rank(b) - rank(a) || (PRIORITY_RANK[a.priority ?? ""] ?? 4) - (PRIORITY_RANK[b.priority ?? ""] ?? 4) || (parseMs(b.updatedAt ?? b.createdAt) ?? 0) - (parseMs(a.updatedAt ?? a.createdAt) ?? 0));
|
|
1447
|
+
}
|
|
1448
|
+
function inFlight(work, config) {
|
|
1449
|
+
const columns = config?.columns ?? [];
|
|
1450
|
+
let ids = columns.filter((c) => IN_FLIGHT.test(c.id) || IN_FLIGHT.test(c.title ?? "")).map((c) => c.id);
|
|
1451
|
+
if (ids.length === 0)
|
|
1452
|
+
ids = columns.slice(1).map((c) => c.id);
|
|
1453
|
+
return work.filter((t) => ids.includes(t.column ?? ""));
|
|
1454
|
+
}
|
|
1455
|
+
function decisionWhy(task) {
|
|
1456
|
+
const record = task;
|
|
1457
|
+
const parts = [];
|
|
1458
|
+
if (typeof record.kind === "string" && record.kind !== "decision")
|
|
1459
|
+
parts.push(record.kind);
|
|
1460
|
+
if (typeof record.stance === "string" && typeof record.subject === "string")
|
|
1461
|
+
parts.push(`${record.stance} ${record.subject}`);
|
|
1462
|
+
if (typeof record.holdsWhile === "string" && record.holdsWhile.trim())
|
|
1463
|
+
parts.push(`while ${record.holdsWhile.trim()}`);
|
|
1464
|
+
if ((task.type ?? "").toLowerCase() === "adr")
|
|
1465
|
+
parts.push("adr");
|
|
1466
|
+
return parts.join(" \xB7 ");
|
|
1467
|
+
}
|
|
1468
|
+
function agoPhrase(from, to) {
|
|
1469
|
+
const rel = (0, brief_1.relativeTime)(from, to);
|
|
1470
|
+
return rel === "just now" || rel === "unknown" ? rel : `${rel} ago`;
|
|
1471
|
+
}
|
|
1472
|
+
function composeBrief2(dirs, agent, options) {
|
|
1473
|
+
const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1474
|
+
const maxChars = options.maxChars === void 0 ? exports.DEFAULT_BRIEF_MAX_CHARS : Math.max(0, Math.floor(options.maxChars));
|
|
1475
|
+
const base = (0, brief_1.buildBrief)(dirs, agent, { lastBriefAt: options.lastBriefAt, now });
|
|
1476
|
+
const config = (0, boardFile_1.readBoardConfig)(dirs.brainfilePath)?.config ?? null;
|
|
1477
|
+
const types = config?.types;
|
|
1478
|
+
const projectRoot = path2.dirname(dirs.dotDir);
|
|
1479
|
+
const boardDocs = (0, taskFile_1.readTasksDir)(dirs.boardDir);
|
|
1480
|
+
const logDocs = (0, taskFile_1.readTasksDir)(dirs.logsDir);
|
|
1481
|
+
const graph = (0, docGraph_1.buildDocGraph)([...boardDocs.map((d) => d.task), ...logDocs.map((d) => d.task)], { types });
|
|
1482
|
+
const isWork = (task) => (0, docTypes_1.resolveDocType)(task.type, types).role !== "decision";
|
|
1483
|
+
const openWork = boardDocs.map((d) => d.task).filter((t) => isWork(t) && !t.completedAt);
|
|
1484
|
+
const decisions = [...boardDocs, ...logDocs].filter((d) => (0, decisions_1.isDecisionDoc)(d.task, types));
|
|
1485
|
+
const current = decisions.filter((d) => (0, docGraph_1.decisionStatus)(d.task) === "current").sort((a, b) => ((0, decisions_1.claimKeyOf)(a.task) ?? `~${a.task.id}`).localeCompare((0, decisions_1.claimKeyOf)(b.task) ?? `~${b.task.id}`, void 0, { numeric: true }));
|
|
1486
|
+
const mine = sortWork(openWork.filter((t) => isAssignedTo(t, agent)), config);
|
|
1487
|
+
const taskList = mine.length > 0 ? mine : sortWork(inFlight(openWork, config), config);
|
|
1488
|
+
const tasksLane = {
|
|
1489
|
+
id: "tasks",
|
|
1490
|
+
label: mine.length > 0 ? "Your tasks" : "In flight",
|
|
1491
|
+
items: taskList.map((task) => ({ taskId: task.id, text: task.title, why: workWhy(task, config), ...task.updatedAt ? { at: task.updatedAt } : {} }))
|
|
1492
|
+
};
|
|
1493
|
+
const warnings = [];
|
|
1494
|
+
const resting = graph.openWorkOnSupersededDecisions();
|
|
1495
|
+
resting.sort((a, b) => Number(isAssignedTo(b.task, agent)) - Number(isAssignedTo(a.task, agent)));
|
|
1496
|
+
for (const { task, decisions: links } of resting) {
|
|
1497
|
+
const named = links.map((l) => l.supersededBy ? `${l.id} (now ${l.supersededBy})` : l.id).join(", ");
|
|
1498
|
+
warnings.push({ taskId: task.id, text: `${task.title}: rests on superseded ${named}`, why: "rests on a superseded decision" });
|
|
1499
|
+
}
|
|
1500
|
+
const nowMs = parseMs(now) ?? Date.now();
|
|
1501
|
+
for (const doc of current) {
|
|
1502
|
+
const report = (0, decisionOperations_1.evaluateDecisionPredicates)(doc.task, { projectRoot, now: nowMs });
|
|
1503
|
+
if (report.status !== "broken")
|
|
1504
|
+
continue;
|
|
1505
|
+
const broken = report.checks.find((c) => c.status === "broken");
|
|
1506
|
+
const recheck = typeof doc.task.recheck === "string" && doc.task.recheck.trim() ? ` \xB7 recheck: ${doc.task.recheck.trim()}` : "";
|
|
1507
|
+
warnings.push({ taskId: doc.task.id, text: `${doc.task.title}: ${(broken?.reason ?? "predicate broken").replace(/_/g, " ")}${recheck}`, why: "decision needs a look" });
|
|
1508
|
+
}
|
|
1509
|
+
const decisionsLane = {
|
|
1510
|
+
id: "decisions",
|
|
1511
|
+
label: "Current decisions",
|
|
1512
|
+
items: current.map((doc) => {
|
|
1513
|
+
const key = (0, decisions_1.claimKeyOf)(doc.task);
|
|
1514
|
+
return { taskId: doc.task.id, text: key ? `${key}: ${doc.task.title}` : doc.task.title, why: decisionWhy(doc.task) };
|
|
1515
|
+
})
|
|
1516
|
+
};
|
|
1517
|
+
const changes = [];
|
|
1518
|
+
const laneItems = (id) => base.lanes.find((l) => l.id === id)?.items ?? [];
|
|
1519
|
+
if (base.mode === "delta") {
|
|
1520
|
+
const since = parseMs(base.lastBriefAt) ?? 0;
|
|
1521
|
+
for (const doc of decisions) {
|
|
1522
|
+
const created = parseMs(doc.task.createdAt);
|
|
1523
|
+
const updated = parseMs(doc.task.updatedAt);
|
|
1524
|
+
const status = (0, docGraph_1.decisionStatus)(doc.task);
|
|
1525
|
+
if (status === "current" && created !== null && created > since) {
|
|
1526
|
+
const supersedes = doc.task.supersedes ? ` (supersedes ${[doc.task.supersedes].flat().join(", ")})` : "";
|
|
1527
|
+
changes.push({ taskId: doc.task.id, text: `decided: ${doc.task.title}${supersedes}`, why: agoPhrase(doc.task.createdAt, now), at: doc.task.createdAt });
|
|
1528
|
+
} else if (status !== "current" && updated !== null && updated > since) {
|
|
1529
|
+
const by = typeof doc.task.supersededBy === "string" ? ` by ${doc.task.supersededBy}` : "";
|
|
1530
|
+
changes.push({ taskId: doc.task.id, text: `${status}${by}: ${doc.task.title}`, why: agoPhrase(doc.task.updatedAt, now), at: doc.task.updatedAt });
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
for (const item of laneItems("notes"))
|
|
1534
|
+
changes.push({ ...item, text: `note: ${item.text}` });
|
|
1535
|
+
for (const item of laneItems("changes"))
|
|
1536
|
+
changes.push(item);
|
|
1537
|
+
for (const item of laneItems("completions"))
|
|
1538
|
+
changes.push({ ...item, text: `completed: ${item.text}` });
|
|
1539
|
+
for (const item of laneItems("config"))
|
|
1540
|
+
changes.push(item);
|
|
1541
|
+
if (changes.length === 0)
|
|
1542
|
+
changes.push({ text: "Nothing new since your last brief.", why: "" });
|
|
1543
|
+
} else {
|
|
1544
|
+
for (const item of laneItems("notes"))
|
|
1545
|
+
changes.push({ ...item, text: `note: ${item.text}` });
|
|
1546
|
+
for (const item of laneItems("completions"))
|
|
1547
|
+
changes.push({ ...item, text: `completed: ${item.text}` });
|
|
1548
|
+
}
|
|
1549
|
+
changes.sort((a, b) => (parseMs(b.at) ?? 0) - (parseMs(a.at) ?? 0));
|
|
1550
|
+
const changesLane = {
|
|
1551
|
+
id: "changes",
|
|
1552
|
+
label: base.mode === "delta" ? "Since your last brief" : "Lately",
|
|
1553
|
+
items: changes
|
|
1554
|
+
};
|
|
1555
|
+
let docsMode = "none";
|
|
1556
|
+
const docItems = [];
|
|
1557
|
+
if (options.docs !== false && taskList.length > 0) {
|
|
1558
|
+
const index = memoryIndex_1.MemoryIndex.open(dirs.brainfilePath);
|
|
1559
|
+
if (index) {
|
|
1560
|
+
try {
|
|
1561
|
+
const budget2 = options.refreshBudgetMs;
|
|
1562
|
+
index.refresh(budget2 === void 0 ? {} : { deadline: Date.now() + budget2 });
|
|
1563
|
+
const best = /* @__PURE__ */ new Map();
|
|
1564
|
+
for (const task of taskList.slice(0, 5)) {
|
|
1565
|
+
for (const hit of index.relatedDocs(task.title, { limit: 2, sources: ["repo", "plans"] })) {
|
|
1566
|
+
const existing = best.get(hit.path);
|
|
1567
|
+
if (!existing || hit.score > existing.score)
|
|
1568
|
+
best.set(hit.path, hit);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
for (const hit of [...best.values()].sort((a, b) => b.score - a.score).slice(0, 4)) {
|
|
1572
|
+
docItems.push({ text: hit.title, why: `${hit.path}:${hit.startLine}-${hit.endLine}` });
|
|
1573
|
+
}
|
|
1574
|
+
docsMode = "index";
|
|
1575
|
+
} catch {
|
|
1576
|
+
} finally {
|
|
1577
|
+
index.close();
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
const sections = [
|
|
1582
|
+
{ id: "warnings", label: "Heads up", items: warnings },
|
|
1583
|
+
decisionsLane,
|
|
1584
|
+
tasksLane,
|
|
1585
|
+
changesLane,
|
|
1586
|
+
{ id: "docs", label: "Docs that bear on them", items: docItems }
|
|
1587
|
+
];
|
|
1588
|
+
const title = config?.title ? ` \xB7 ${config.title}` : "";
|
|
1589
|
+
const when = base.mode === "full" ? "first brief" : `since ${agoPhrase(base.lastBriefAt, now)}`;
|
|
1590
|
+
const header = `Brainfile brief for ${agent}${title} (${when})`;
|
|
1591
|
+
const rendered = renderBriefText(header, sections, maxChars, agent);
|
|
1592
|
+
return { ...base, sections, text: rendered.text, maxChars, truncated: rendered.truncated, docsMode };
|
|
1593
|
+
}
|
|
1594
|
+
function clip(line, max) {
|
|
1595
|
+
if (line.length <= max)
|
|
1596
|
+
return line;
|
|
1597
|
+
return `${line.slice(0, Math.max(1, max - 1)).trimEnd()}\u2026`;
|
|
1598
|
+
}
|
|
1599
|
+
function briefItemLine(laneId, item) {
|
|
1600
|
+
const id = item.taskId ? `${item.taskId} ` : "";
|
|
1601
|
+
switch (laneId) {
|
|
1602
|
+
case "warnings":
|
|
1603
|
+
return `! ${id}${item.text}`;
|
|
1604
|
+
case "docs":
|
|
1605
|
+
return `- ${item.why} ${item.text}`;
|
|
1606
|
+
case "tasks":
|
|
1607
|
+
return `- ${id}${item.text}${item.why ? ` \xB7 ${item.why}` : ""}`;
|
|
1608
|
+
default:
|
|
1609
|
+
return `- ${id}${item.text}${item.why ? ` (${item.why})` : ""}`;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
function renderBriefText(header, sections, maxChars, agent = "") {
|
|
1613
|
+
const lanes = sections.filter((lane) => lane.items.length > 0);
|
|
1614
|
+
if (lanes.length === 0) {
|
|
1615
|
+
const text2 = `${header}
|
|
1616
|
+
Nothing on the board for you yet.`;
|
|
1617
|
+
return maxChars > 0 && text2.length > maxChars ? { text: clip(text2, maxChars), truncated: true } : { text: text2, truncated: false };
|
|
1618
|
+
}
|
|
1619
|
+
const lineMax = maxChars > 0 ? Math.min(MAX_LINE, Math.max(40, maxChars)) : MAX_LINE;
|
|
1620
|
+
const heading = (lane) => lane.id === "warnings" ? "" : `## ${lane.label}`;
|
|
1621
|
+
const lines = lanes.map((lane) => lane.items.map((item) => clip(briefItemLine(lane.id, item), lineMax)));
|
|
1622
|
+
const render = (counts2) => {
|
|
1623
|
+
const out = [header];
|
|
1624
|
+
lanes.forEach((lane, i) => {
|
|
1625
|
+
if (counts2[i] === 0)
|
|
1626
|
+
return;
|
|
1627
|
+
const h = heading(lane);
|
|
1628
|
+
if (h)
|
|
1629
|
+
out.push(h);
|
|
1630
|
+
out.push(...lines[i].slice(0, counts2[i]));
|
|
1631
|
+
});
|
|
1632
|
+
return out.join("\n");
|
|
1633
|
+
};
|
|
1634
|
+
const total = lines.reduce((n, l) => n + l.length, 0);
|
|
1635
|
+
if (maxChars <= 0)
|
|
1636
|
+
return { text: render(lines.map((l) => l.length)), truncated: false };
|
|
1637
|
+
const more = (hidden) => `(+${hidden} more: brainfile brief${agent ? ` --agent ${agent}` : ""} --max-chars 0)`;
|
|
1638
|
+
const fits = (counts2) => {
|
|
1639
|
+
const shown2 = counts2.reduce((a, b) => a + b, 0);
|
|
1640
|
+
const text2 = render(counts2);
|
|
1641
|
+
return (shown2 < total ? `${text2}
|
|
1642
|
+
${more(total - shown2)}` : text2).length <= maxChars;
|
|
1643
|
+
};
|
|
1644
|
+
const counts = lines.map(() => 1);
|
|
1645
|
+
const full = lines.map((l) => l[0]);
|
|
1646
|
+
if (!fits(counts)) {
|
|
1647
|
+
const MIN_LINE = 16;
|
|
1648
|
+
for (let i = 0; i < lanes.length; i++) {
|
|
1649
|
+
counts[i] = 0;
|
|
1650
|
+
lines[i][0] = full[i].length > MIN_LINE ? clip(full[i], MIN_LINE) : full[i];
|
|
1651
|
+
}
|
|
1652
|
+
for (let i = 0; i < lanes.length; i++) {
|
|
1653
|
+
counts[i] = 1;
|
|
1654
|
+
if (!fits(counts))
|
|
1655
|
+
counts[i] = 0;
|
|
1656
|
+
}
|
|
1657
|
+
for (let grew = true; grew; ) {
|
|
1658
|
+
grew = false;
|
|
1659
|
+
for (let i = 0; i < lanes.length; i++) {
|
|
1660
|
+
if (counts[i] === 0 || lines[i][0] === full[i])
|
|
1661
|
+
continue;
|
|
1662
|
+
const before = lines[i][0];
|
|
1663
|
+
const next = before.length + 10;
|
|
1664
|
+
lines[i][0] = next >= full[i].length ? full[i] : clip(full[i], next);
|
|
1665
|
+
if (fits(counts))
|
|
1666
|
+
grew = true;
|
|
1667
|
+
else
|
|
1668
|
+
lines[i][0] = before;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
for (let i = 0; i < lanes.length; i++) {
|
|
1673
|
+
while (counts[i] > 0 && counts[i] < lines[i].length) {
|
|
1674
|
+
counts[i]++;
|
|
1675
|
+
if (!fits(counts)) {
|
|
1676
|
+
counts[i]--;
|
|
1677
|
+
break;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
const shown = counts.reduce((a, b) => a + b, 0);
|
|
1682
|
+
let text = render(counts);
|
|
1683
|
+
if (shown < total)
|
|
1684
|
+
text = `${text}
|
|
1685
|
+
${more(total - shown)}`;
|
|
1686
|
+
if (text.length > maxChars)
|
|
1687
|
+
text = clip(text, maxChars);
|
|
1688
|
+
return { text, truncated: shown < total };
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
// ../core/dist/memory/check.js
|
|
1694
|
+
var require_check = __commonJS({
|
|
1695
|
+
"../core/dist/memory/check.js"(exports) {
|
|
1696
|
+
"use strict";
|
|
1697
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1698
|
+
if (k2 === void 0) k2 = k;
|
|
1699
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1700
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1701
|
+
desc = { enumerable: true, get: function() {
|
|
1702
|
+
return m[k];
|
|
1703
|
+
} };
|
|
1704
|
+
}
|
|
1705
|
+
Object.defineProperty(o, k2, desc);
|
|
1706
|
+
}) : (function(o, m, k, k2) {
|
|
1707
|
+
if (k2 === void 0) k2 = k;
|
|
1708
|
+
o[k2] = m[k];
|
|
1709
|
+
}));
|
|
1710
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
1711
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1712
|
+
}) : function(o, v) {
|
|
1713
|
+
o["default"] = v;
|
|
1714
|
+
});
|
|
1715
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
1716
|
+
var ownKeys = function(o) {
|
|
1717
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1718
|
+
var ar = [];
|
|
1719
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1720
|
+
return ar;
|
|
1721
|
+
};
|
|
1722
|
+
return ownKeys(o);
|
|
1723
|
+
};
|
|
1724
|
+
return function(mod) {
|
|
1725
|
+
if (mod && mod.__esModule) return mod;
|
|
1726
|
+
var result = {};
|
|
1727
|
+
if (mod != null) {
|
|
1728
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1729
|
+
}
|
|
1730
|
+
__setModuleDefault(result, mod);
|
|
1731
|
+
return result;
|
|
1732
|
+
};
|
|
1733
|
+
})();
|
|
1734
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1735
|
+
exports.checkExitCode = checkExitCode;
|
|
1736
|
+
exports.checkDecision = checkDecision;
|
|
1737
|
+
exports.checkClaimKeyPrefix = checkClaimKeyPrefix;
|
|
1738
|
+
var path2 = __importStar(__require("path"));
|
|
1739
|
+
var boardFile_1 = require_boardFile();
|
|
1740
|
+
var workspace_1 = require_workspace();
|
|
1741
|
+
var decisions_1 = require_decisions();
|
|
1742
|
+
var decisionOperations_1 = require_decisionOperations();
|
|
1743
|
+
var memoryIndex_1 = require_memoryIndex();
|
|
1744
|
+
function checkExitCode(verdict) {
|
|
1745
|
+
if (verdict === "review")
|
|
1746
|
+
return 2;
|
|
1747
|
+
if (verdict === "use_replacement")
|
|
1748
|
+
return 3;
|
|
1749
|
+
return 0;
|
|
1750
|
+
}
|
|
1751
|
+
function load(brainfilePath, options) {
|
|
1752
|
+
const dirs = (0, workspace_1.getV2Dirs)(brainfilePath);
|
|
1753
|
+
const types = (0, boardFile_1.readBoardConfig)(dirs.brainfilePath)?.config.types;
|
|
1754
|
+
const docs = (0, decisionOperations_1.readDecisionDocs)(dirs, types);
|
|
1755
|
+
return { docs, tasks: docs.map((d) => d.task), types, projectRoot: options.projectRoot ?? path2.dirname(dirs.dotDir) };
|
|
1756
|
+
}
|
|
1757
|
+
function familyTasks(loaded, key) {
|
|
1758
|
+
return loaded.tasks.filter((task) => (0, decisions_1.decisionFamilyKey)(task) === key);
|
|
1759
|
+
}
|
|
1760
|
+
function assess(loaded, options, key, targetKind, matched) {
|
|
1761
|
+
const now = options.now ?? Date.now();
|
|
1762
|
+
return (0, decisions_1.assessDecisionFamily)({
|
|
1763
|
+
...options.query ? { query: options.query } : {},
|
|
1764
|
+
claimKey: key,
|
|
1765
|
+
targetKind,
|
|
1766
|
+
...matched ? { matched } : {},
|
|
1767
|
+
family: familyTasks(loaded, key),
|
|
1768
|
+
evaluate: (task) => (0, decisionOperations_1.evaluateDecisionPredicates)(task, { projectRoot: loaded.projectRoot, now })
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
function scanCandidates(loaded, query) {
|
|
1772
|
+
const tokens = (0, decisions_1.significantTokens)(query, decisions_1.ACTION_CUE_WORDS);
|
|
1773
|
+
if (tokens.length === 0)
|
|
1774
|
+
return [];
|
|
1775
|
+
return loaded.docs.map((doc) => ({ task: doc.task, coverage: (0, decisions_1.tokenCoverage)(tokens, (0, decisions_1.decisionSearchText)(doc.task, doc.body)) })).filter((c) => c.coverage > 0).sort((a, b) => b.coverage - a.coverage).map((c) => c.task);
|
|
1776
|
+
}
|
|
1777
|
+
function checkDecision(brainfilePath, options) {
|
|
1778
|
+
const loaded = load(brainfilePath, options);
|
|
1779
|
+
const query = options.query?.trim() ?? "";
|
|
1780
|
+
if (options.claimKey?.trim()) {
|
|
1781
|
+
const key = (0, decisions_1.normalizeClaimKey)(options.claimKey);
|
|
1782
|
+
const byId = loaded.tasks.find((t) => t.id === options.claimKey.trim());
|
|
1783
|
+
const familyKey = familyTasks(loaded, key).length > 0 ? key : byId ? (0, decisions_1.decisionFamilyKey)(byId) : key;
|
|
1784
|
+
return { matchedBy: "claim_key", result: assess(loaded, { ...options, query }, familyKey, "claim_key") };
|
|
1785
|
+
}
|
|
1786
|
+
if (!query)
|
|
1787
|
+
return { matchedBy: "none", result: (0, decisions_1.noDecisionResult)() };
|
|
1788
|
+
if (!/\s/.test(query)) {
|
|
1789
|
+
const named = loaded.tasks.find((t) => t.id === query);
|
|
1790
|
+
if (named)
|
|
1791
|
+
return { matchedBy: "decision", result: assess(loaded, options, (0, decisions_1.decisionFamilyKey)(named), "decision", named) };
|
|
1792
|
+
const key = (0, decisions_1.normalizeClaimKey)(query);
|
|
1793
|
+
if (loaded.tasks.some((t) => (0, decisions_1.claimKeyOf)(t) === key)) {
|
|
1794
|
+
return { matchedBy: "claim_key", result: assess(loaded, { ...options, query: "" }, key, "claim_key") };
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
let candidates = [];
|
|
1798
|
+
let matchedBy = "none";
|
|
1799
|
+
if (!options.scan) {
|
|
1800
|
+
const index = memoryIndex_1.MemoryIndex.open(brainfilePath);
|
|
1801
|
+
if (index) {
|
|
1802
|
+
try {
|
|
1803
|
+
index.refresh();
|
|
1804
|
+
const ids = index.decisionCandidates(query, { ignoreWords: decisions_1.ACTION_CUE_WORDS });
|
|
1805
|
+
candidates = ids.map((id) => loaded.tasks.find((t) => t.id === id)).filter((t) => Boolean(t));
|
|
1806
|
+
matchedBy = "index";
|
|
1807
|
+
} catch {
|
|
1808
|
+
candidates = [];
|
|
1809
|
+
} finally {
|
|
1810
|
+
index.close();
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
if (matchedBy === "none") {
|
|
1815
|
+
candidates = scanCandidates(loaded, query);
|
|
1816
|
+
matchedBy = "scan";
|
|
1817
|
+
}
|
|
1818
|
+
const matched = candidates[0];
|
|
1819
|
+
if (!matched)
|
|
1820
|
+
return { matchedBy, result: (0, decisions_1.noDecisionResult)(query) };
|
|
1821
|
+
return { matchedBy, result: assess(loaded, options, (0, decisions_1.decisionFamilyKey)(matched), "query", matched) };
|
|
1822
|
+
}
|
|
1823
|
+
function checkClaimKeyPrefix(brainfilePath, prefix, options = {}) {
|
|
1824
|
+
const loaded = load(brainfilePath, options);
|
|
1825
|
+
const families = (0, decisions_1.groupDecisionFamilies)(loaded.tasks, loaded.types).filter((family) => family.claimKey && (0, decisions_1.claimKeyMatchesPrefix)(family.claimKey, prefix));
|
|
1826
|
+
const results = families.map((family) => assess(loaded, { ...options, query: options.query ?? "" }, family.key, "claim_key"));
|
|
1827
|
+
const worst = (0, decisions_1.worstCheckResult)(results);
|
|
1828
|
+
return {
|
|
1829
|
+
claimKeyPrefix: (0, decisions_1.normalizeClaimKey)(prefix),
|
|
1830
|
+
verdict: worst?.verdict ?? "no_decision",
|
|
1831
|
+
recommendation: worst?.recommendation ?? "proceed_without_memory",
|
|
1832
|
+
reason: worst?.reason ?? "no_claim_key_match",
|
|
1833
|
+
...worst?.claimKey ? { decidingClaimKey: worst.claimKey } : {},
|
|
1834
|
+
familyCount: results.length,
|
|
1835
|
+
families: results
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1841
|
+
// ../core/dist/memory/index.js
|
|
1842
|
+
var require_memory = __commonJS({
|
|
1843
|
+
"../core/dist/memory/index.js"(exports) {
|
|
1844
|
+
"use strict";
|
|
1845
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1846
|
+
if (k2 === void 0) k2 = k;
|
|
1847
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1848
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1849
|
+
desc = { enumerable: true, get: function() {
|
|
1850
|
+
return m[k];
|
|
1851
|
+
} };
|
|
1852
|
+
}
|
|
1853
|
+
Object.defineProperty(o, k2, desc);
|
|
1854
|
+
}) : (function(o, m, k, k2) {
|
|
1855
|
+
if (k2 === void 0) k2 = k;
|
|
1856
|
+
o[k2] = m[k];
|
|
1857
|
+
}));
|
|
1858
|
+
var __exportStar = exports && exports.__exportStar || function(m, exports2) {
|
|
1859
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
|
|
1860
|
+
};
|
|
1861
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1862
|
+
__exportStar(require_sqlite(), exports);
|
|
1863
|
+
__exportStar(require_chunk(), exports);
|
|
1864
|
+
__exportStar(require_mine(), exports);
|
|
1865
|
+
__exportStar(require_memoryIndex(), exports);
|
|
1866
|
+
__exportStar(require_search2(), exports);
|
|
1867
|
+
__exportStar(require_brief2(), exports);
|
|
1868
|
+
__exportStar(require_check(), exports);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1872
|
+
// src/hooks/session-start.ts
|
|
1873
|
+
var import_core = __toESM(require_dist());
|
|
1874
|
+
var import_memory = __toESM(require_memory());
|
|
1875
|
+
import * as path from "path";
|
|
1876
|
+
var HOOK_REFRESH_BUDGET_MS = 120;
|
|
1877
|
+
function hookCaller(input) {
|
|
1878
|
+
if (!input) return "unknown";
|
|
1879
|
+
if (input.clineVersion && input.hookName) return "cline";
|
|
1880
|
+
const event = typeof input.hook_event_name === "string" ? input.hook_event_name : "";
|
|
1881
|
+
if (event === "SessionStart") return "claude-code";
|
|
1882
|
+
if (event === "stop") return "cursor";
|
|
1883
|
+
return "unknown";
|
|
1884
|
+
}
|
|
1885
|
+
function clean(name) {
|
|
1886
|
+
return (name ?? "").replace(/[[\]<>\r\n]/g, "").trim();
|
|
1887
|
+
}
|
|
1888
|
+
function hookAgent(caller, env = process.env) {
|
|
1889
|
+
const named = clean(env.BRAINFILE_AGENT);
|
|
1890
|
+
if (named) return named;
|
|
1891
|
+
if (caller === "claude-code") return "claude";
|
|
1892
|
+
if (caller === "cline") return "cline";
|
|
1893
|
+
if (caller === "cursor") return "cursor";
|
|
1894
|
+
if (env.CODEX_THREAD_ID || env.CODEX_SESSION_ID) return "codex";
|
|
1895
|
+
if (env.GEMINI_CLI === "1") return "gemini";
|
|
1896
|
+
if (env.CLAUDECODE === "1") return "claude";
|
|
1897
|
+
return "agent";
|
|
1898
|
+
}
|
|
1899
|
+
function budget(env) {
|
|
1900
|
+
const n = Number.parseInt(env.BRAINFILE_BRIEF_MAX_CHARS ?? "", 10);
|
|
1901
|
+
return Number.isFinite(n) && n >= 0 ? n : import_memory.DEFAULT_BRIEF_MAX_CHARS;
|
|
1902
|
+
}
|
|
1903
|
+
function sessionStartOutput(input, env = process.env) {
|
|
1904
|
+
try {
|
|
1905
|
+
const cwd = typeof input?.cwd === "string" && input.cwd ? input.cwd : process.cwd();
|
|
1906
|
+
const found = (0, import_core.findBrainfile)(cwd, { stopAtGitRoot: true });
|
|
1907
|
+
if (!found || found.kind !== "dotdir") return null;
|
|
1908
|
+
const caller = hookCaller(input);
|
|
1909
|
+
if (caller === "cursor") {
|
|
1910
|
+
return `\u2705 Brainfile detected: @${path.basename(found.absolutePath)}
|
|
1911
|
+
Remember to update task status as you work.`;
|
|
1912
|
+
}
|
|
1913
|
+
if (!(0, import_core.isV2)(found.absolutePath)) return null;
|
|
1914
|
+
const agent = hookAgent(caller, env);
|
|
1915
|
+
const state = (0, import_core.readBriefState)(found.absolutePath, agent);
|
|
1916
|
+
const brief = (0, import_memory.composeBrief)((0, import_core.getV2Dirs)(found.absolutePath), agent, {
|
|
1917
|
+
lastBriefAt: state.lastBriefAt,
|
|
1918
|
+
maxChars: budget(env),
|
|
1919
|
+
refreshBudgetMs: HOOK_REFRESH_BUDGET_MS
|
|
1920
|
+
});
|
|
1921
|
+
try {
|
|
1922
|
+
(0, import_core.writeBriefState)(found.absolutePath, agent, brief.generatedAt);
|
|
1923
|
+
} catch {
|
|
1924
|
+
}
|
|
1925
|
+
if (caller === "claude-code") {
|
|
1926
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: brief.text } });
|
|
1927
|
+
}
|
|
1928
|
+
return brief.text;
|
|
1929
|
+
} catch {
|
|
1930
|
+
return null;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
function readHookInput(stdin = process.stdin, timeoutMs = 1500) {
|
|
1934
|
+
if (stdin.isTTY) return Promise.resolve({});
|
|
1935
|
+
return new Promise((resolve) => {
|
|
1936
|
+
let data = "";
|
|
1937
|
+
let done = false;
|
|
1938
|
+
const finish = () => {
|
|
1939
|
+
if (done) return;
|
|
1940
|
+
done = true;
|
|
1941
|
+
clearTimeout(timer);
|
|
1942
|
+
try {
|
|
1943
|
+
const parsed = JSON.parse(data);
|
|
1944
|
+
resolve(parsed && typeof parsed === "object" ? parsed : {});
|
|
1945
|
+
} catch {
|
|
1946
|
+
resolve({});
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1949
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
1950
|
+
stdin.setEncoding?.("utf-8");
|
|
1951
|
+
stdin.on("data", (chunk) => {
|
|
1952
|
+
data += String(chunk);
|
|
1953
|
+
});
|
|
1954
|
+
stdin.on("end", finish);
|
|
1955
|
+
stdin.on("error", finish);
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
async function runSessionStartHook(stdin = process.stdin, write = (text) => process.stdout.write(text), env = process.env) {
|
|
1959
|
+
const input = await readHookInput(stdin);
|
|
1960
|
+
const out = sessionStartOutput(input, env);
|
|
1961
|
+
if (out) write(`${out}
|
|
1962
|
+
`);
|
|
1963
|
+
if (stdin === process.stdin) {
|
|
1964
|
+
process.stdin.pause();
|
|
1965
|
+
process.stdin.unref?.();
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
export {
|
|
1970
|
+
require_memory,
|
|
1971
|
+
sessionStartOutput,
|
|
1972
|
+
runSessionStartHook
|
|
1973
|
+
};
|