@nexrall/code-core 1.4.76 → 1.4.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/goal.d.ts +55 -0
- package/dist/agent/goal.d.ts.map +1 -0
- package/dist/agent/goal.js +120 -0
- package/dist/agent/handoffBrief.d.ts +82 -0
- package/dist/agent/handoffBrief.d.ts.map +1 -0
- package/dist/agent/handoffBrief.js +121 -0
- package/dist/agent/modelCatalogue.d.ts +23 -0
- package/dist/agent/modelCatalogue.d.ts.map +1 -1
- package/dist/agent/modelCatalogue.js +28 -0
- package/dist/agent/outputStyles.d.ts +27 -0
- package/dist/agent/outputStyles.d.ts.map +1 -0
- package/dist/agent/outputStyles.js +174 -0
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +7 -0
- package/dist/api/billingLines.d.ts +11 -0
- package/dist/api/billingLines.d.ts.map +1 -0
- package/dist/api/billingLines.js +56 -0
- package/dist/api/client.d.ts +2 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/scheduleLines.d.ts +53 -0
- package/dist/api/scheduleLines.d.ts.map +1 -0
- package/dist/api/scheduleLines.js +150 -0
- package/dist/context/contextFiles.d.ts +81 -0
- package/dist/context/contextFiles.d.ts.map +1 -0
- package/dist/context/contextFiles.js +310 -0
- package/dist/diff/collect.d.ts +25 -0
- package/dist/diff/collect.d.ts.map +1 -0
- package/dist/diff/collect.js +169 -0
- package/dist/diff/diffView.d.ts +57 -0
- package/dist/diff/diffView.d.ts.map +1 -0
- package/dist/diff/diffView.js +231 -0
- package/dist/diff/reviewQueue.d.ts +53 -0
- package/dist/diff/reviewQueue.d.ts.map +1 -0
- package/dist/diff/reviewQueue.js +349 -0
- package/dist/events/recap.d.ts +54 -0
- package/dist/events/recap.d.ts.map +1 -0
- package/dist/events/recap.js +161 -0
- package/dist/events/sessionEvents.d.ts +54 -0
- package/dist/events/sessionEvents.d.ts.map +1 -0
- package/dist/events/sessionEvents.js +324 -0
- package/dist/events/sessionStats.d.ts +89 -0
- package/dist/events/sessionStats.d.ts.map +1 -0
- package/dist/events/sessionStats.js +264 -0
- package/dist/import/claudeImport.d.ts +44 -0
- package/dist/import/claudeImport.d.ts.map +1 -0
- package/dist/import/claudeImport.js +337 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -0
- package/dist/project/projectCommands.d.ts +33 -0
- package/dist/project/projectCommands.d.ts.map +1 -0
- package/dist/project/projectCommands.js +246 -0
- package/dist/search/conversationTurns.d.ts +53 -0
- package/dist/search/conversationTurns.d.ts.map +1 -0
- package/dist/search/conversationTurns.js +170 -0
- package/dist/search/globalSearch.d.ts +144 -0
- package/dist/search/globalSearch.d.ts.map +1 -0
- package/dist/search/globalSearch.js +350 -0
- package/dist/share/sessionChannel.d.ts +136 -0
- package/dist/share/sessionChannel.d.ts.map +1 -0
- package/dist/share/sessionChannel.js +373 -0
- package/dist/share/sessionShare.d.ts +104 -0
- package/dist/share/sessionShare.d.ts.map +1 -0
- package/dist/share/sessionShare.js +336 -0
- package/dist/ui/configCmd.d.ts +48 -0
- package/dist/ui/configCmd.d.ts.map +1 -0
- package/dist/ui/configCmd.js +146 -0
- package/dist/ui/deployPanel.d.ts +91 -0
- package/dist/ui/deployPanel.d.ts.map +1 -0
- package/dist/ui/deployPanel.js +442 -0
- package/dist/ui/mergeAssistant.d.ts +120 -0
- package/dist/ui/mergeAssistant.d.ts.map +1 -0
- package/dist/ui/mergeAssistant.js +391 -0
- package/dist/ui/notify.d.ts +114 -0
- package/dist/ui/notify.d.ts.map +1 -0
- package/dist/ui/notify.js +235 -0
- package/dist/ui/promptSuggest.d.ts +56 -0
- package/dist/ui/promptSuggest.d.ts.map +1 -0
- package/dist/ui/promptSuggest.js +160 -0
- package/dist/ui/taskGraph.d.ts +44 -0
- package/dist/ui/taskGraph.d.ts.map +1 -0
- package/dist/ui/taskGraph.js +289 -0
- package/dist/util/addDir.d.ts +31 -0
- package/dist/util/addDir.d.ts.map +1 -0
- package/dist/util/addDir.js +124 -0
- package/dist/util/batch.d.ts +16 -0
- package/dist/util/batch.d.ts.map +1 -0
- package/dist/util/batch.js +92 -0
- package/dist/util/cellWidth.d.ts +12 -0
- package/dist/util/cellWidth.d.ts.map +1 -0
- package/dist/util/cellWidth.js +161 -0
- package/dist/util/duration.d.ts +8 -0
- package/dist/util/duration.d.ts.map +1 -0
- package/dist/util/duration.js +50 -0
- package/dist/util/loopJobs.d.ts +67 -0
- package/dist/util/loopJobs.d.ts.map +1 -0
- package/dist/util/loopJobs.js +169 -0
- package/dist/util/settingsFile.d.ts +14 -0
- package/dist/util/settingsFile.d.ts.map +1 -0
- package/dist/util/settingsFile.js +99 -0
- package/dist/util/toolLabel.d.ts +2 -0
- package/dist/util/toolLabel.d.ts.map +1 -0
- package/dist/util/toolLabel.js +17 -0
- package/dist/verify/failureTimeline.d.ts +100 -0
- package/dist/verify/failureTimeline.d.ts.map +1 -0
- package/dist/verify/failureTimeline.js +253 -0
- package/dist/verify/verification.d.ts +75 -0
- package/dist/verify/verification.d.ts.map +1 -0
- package/dist/verify/verification.js +315 -0
- package/package.json +5 -4
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Lives in core (moved from the CLI's ui/globalSearch.ts, 2026-10-04) so
|
|
3
|
+
// /find searches with the same engine in the VS Code panel; the CLI module is
|
|
4
|
+
// a re-export shim.
|
|
5
|
+
// §52 Global search — one query across files, conversation, tools, git, errors and agents.
|
|
6
|
+
//
|
|
7
|
+
// WHY: every scope already had its own command (/diff, /expand, /failures, /agents, /find), so
|
|
8
|
+
// answering "where does JWT show up at all?" meant running five of them and remembering which
|
|
9
|
+
// one held the answer. This module is the missing index over the sources the session already
|
|
10
|
+
// holds — pure functions, so a count is exactly what the scope contains and nothing is invented.
|
|
11
|
+
// It finds and ranks; the per-scope commands still do the jumping.
|
|
12
|
+
//
|
|
13
|
+
// Pure (no Ink, no console, no fs) so ranking, clipping and the counts block are unit-testable.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.LABEL_CLIP = exports.DETAIL_CLIP = exports.MAX_PER_SCOPE = exports.SCOPE_LABEL = exports.SCOPES = void 0;
|
|
16
|
+
exports.fitLine = fitLine;
|
|
17
|
+
exports.scoreText = scoreText;
|
|
18
|
+
exports.searchFiles = searchFiles;
|
|
19
|
+
exports.searchConversation = searchConversation;
|
|
20
|
+
exports.searchTools = searchTools;
|
|
21
|
+
exports.searchGit = searchGit;
|
|
22
|
+
exports.searchErrors = searchErrors;
|
|
23
|
+
exports.searchAgents = searchAgents;
|
|
24
|
+
exports.globalSearch = globalSearch;
|
|
25
|
+
exports.filterScope = filterScope;
|
|
26
|
+
exports.formatCounts = formatCounts;
|
|
27
|
+
exports.formatHits = formatHits;
|
|
28
|
+
exports.scopeHint = scopeHint;
|
|
29
|
+
exports.parseSearchArgs = parseSearchArgs;
|
|
30
|
+
exports.searchUsage = searchUsage;
|
|
31
|
+
const cellWidth_1 = require("../util/cellWidth");
|
|
32
|
+
/** Display order — §52 lists files first because that is where a code query usually lives. */
|
|
33
|
+
exports.SCOPES = ['files', 'conversation', 'tools', 'git', 'errors', 'agents'];
|
|
34
|
+
exports.SCOPE_LABEL = {
|
|
35
|
+
files: 'Files',
|
|
36
|
+
conversation: 'Conversation',
|
|
37
|
+
tools: 'Tools',
|
|
38
|
+
git: 'Git',
|
|
39
|
+
errors: 'Errors',
|
|
40
|
+
agents: 'Agents',
|
|
41
|
+
};
|
|
42
|
+
/** Flags accepted after the query, including the short forms a user is likely to try first. */
|
|
43
|
+
const SCOPE_FLAG = {
|
|
44
|
+
'--files': 'files',
|
|
45
|
+
'--file': 'files',
|
|
46
|
+
'--chat': 'conversation',
|
|
47
|
+
'--conversation': 'conversation',
|
|
48
|
+
'--conv': 'conversation',
|
|
49
|
+
'--tools': 'tools',
|
|
50
|
+
'--tool': 'tools',
|
|
51
|
+
'--git': 'git',
|
|
52
|
+
'--errors': 'errors',
|
|
53
|
+
'--error': 'errors',
|
|
54
|
+
'--agents': 'agents',
|
|
55
|
+
'--agent': 'agents',
|
|
56
|
+
};
|
|
57
|
+
/** Hits printed per scope. The counts line stays complete — only the list is capped. */
|
|
58
|
+
exports.MAX_PER_SCOPE = 5;
|
|
59
|
+
/** A matched line kept for context, so a hit is recognisable without opening anything. */
|
|
60
|
+
exports.DETAIL_CLIP = 96;
|
|
61
|
+
exports.LABEL_CLIP = 72;
|
|
62
|
+
function clip(s, max) {
|
|
63
|
+
const flat = s.replace(/\s+/g, ' ').trim();
|
|
64
|
+
return (0, cellWidth_1.truncateCells)(flat, max);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Shorten an ASSEMBLED line to `cols`, keeping its leading indent.
|
|
68
|
+
*
|
|
69
|
+
* `clip` flattens whitespace, which is right for a path or a matched line but destroys a row
|
|
70
|
+
* whose alignment is the point: `clip(' Files 12')` returns `Files 12`, and every column in
|
|
71
|
+
* the counts block collapses. Measured on the first run of the tests in this module.
|
|
72
|
+
*/
|
|
73
|
+
function fitLine(body, cols, indent = '') {
|
|
74
|
+
const room = Math.max(1, cols - (0, cellWidth_1.visibleLen)(indent));
|
|
75
|
+
if ((0, cellWidth_1.visibleLen)(body) <= room)
|
|
76
|
+
return indent + body;
|
|
77
|
+
const cut = (0, cellWidth_1.truncateCells)(body, room);
|
|
78
|
+
// A cut lands mid-style (the closing code of the run it interrupted is never reached), and an
|
|
79
|
+
// SGR left open bleeds into every row printed after it. Only styled rows get the reset, so
|
|
80
|
+
// NO_COLOR output stays byte-identical to what it always was.
|
|
81
|
+
return indent + (cut.includes('\u001b') ? cut + '\u001b[0m' : cut);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Rank one text against the query. `Infinity` = no match.
|
|
85
|
+
*
|
|
86
|
+
* Substring only, case-insensitive — this is a search box for code, where a fuzzy match is
|
|
87
|
+
* noise. The ladder, weakest last:
|
|
88
|
+
*
|
|
89
|
+
* 0 the text IS the query `auth` vs `auth`
|
|
90
|
+
* 1 the text begins with the query `auth.ts` vs `auth`
|
|
91
|
+
* 2 the query begins a word `src/auth.ts`, `jwt-refresh.ts`
|
|
92
|
+
* 3 the query starts mid-word `myjwthelper.ts`, `contest/a.ts`
|
|
93
|
+
*
|
|
94
|
+
* Rung 1 exists on its own because a tie between `auth.ts` and `x/auth-mid.ts` used to be
|
|
95
|
+
* settled by whatever order the scope listed them in, which put the worse match first.
|
|
96
|
+
*/
|
|
97
|
+
function scoreText(query, text) {
|
|
98
|
+
const q = query.toLowerCase();
|
|
99
|
+
if (!q)
|
|
100
|
+
return Infinity;
|
|
101
|
+
const t = text.toLowerCase();
|
|
102
|
+
const at = t.indexOf(q);
|
|
103
|
+
if (at < 0)
|
|
104
|
+
return Infinity;
|
|
105
|
+
if (t === q)
|
|
106
|
+
return 0;
|
|
107
|
+
if (at === 0)
|
|
108
|
+
return 1;
|
|
109
|
+
if (!/[a-z0-9]/.test(t[at - 1]))
|
|
110
|
+
return 2;
|
|
111
|
+
return 3;
|
|
112
|
+
}
|
|
113
|
+
/** Best first: score, then the order the source listed them in. */
|
|
114
|
+
function top(hits, limit) {
|
|
115
|
+
const sorted = hits.sort((a, b) => a.score - b.score || a.index - b.index);
|
|
116
|
+
return limit === Infinity ? sorted : sorted.slice(0, limit);
|
|
117
|
+
}
|
|
118
|
+
function push(hits, scope, index, label, detail, score) {
|
|
119
|
+
hits.push({
|
|
120
|
+
scope,
|
|
121
|
+
key: `${scope}:${index}`,
|
|
122
|
+
label: clip(label, exports.LABEL_CLIP),
|
|
123
|
+
detail: detail && clip(detail, exports.DETAIL_CLIP),
|
|
124
|
+
score,
|
|
125
|
+
index,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// ─── Per-scope matchers ───────────────────────────────────────────────────────
|
|
129
|
+
/** Files: the path itself. */
|
|
130
|
+
function searchFiles(query, files) {
|
|
131
|
+
const hits = [];
|
|
132
|
+
files.forEach((f, i) => {
|
|
133
|
+
const score = scoreText(query, f);
|
|
134
|
+
if (score !== Infinity)
|
|
135
|
+
push(hits, 'files', i + 1, f, undefined, score);
|
|
136
|
+
});
|
|
137
|
+
return top(hits, Infinity);
|
|
138
|
+
}
|
|
139
|
+
/** Conversation: prompts and reply lines, via the transcript the turn navigation already builds. */
|
|
140
|
+
function searchConversation(query, turns) {
|
|
141
|
+
const hits = [];
|
|
142
|
+
turns.forEach((t) => {
|
|
143
|
+
const score = scoreText(query, t.prompt);
|
|
144
|
+
if (score !== Infinity)
|
|
145
|
+
push(hits, 'conversation', t.index, `turn ${t.index} you ${t.prompt}`, undefined, score);
|
|
146
|
+
t.lines.forEach((line) => {
|
|
147
|
+
const s = scoreText(query, line);
|
|
148
|
+
if (s !== Infinity)
|
|
149
|
+
push(hits, 'conversation', t.index, `turn ${t.index}`, line, s + 1);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
return top(hits, Infinity);
|
|
153
|
+
}
|
|
154
|
+
/** Tools: the call label and the output the transcript folded away. */
|
|
155
|
+
function searchTools(query, entries) {
|
|
156
|
+
const hits = [];
|
|
157
|
+
entries.forEach((e) => {
|
|
158
|
+
const label = `${e.name}: ${e.label}`;
|
|
159
|
+
let best = scoreText(query, label);
|
|
160
|
+
let detail;
|
|
161
|
+
for (const line of e.output.split('\n')) {
|
|
162
|
+
const s = scoreText(query, line);
|
|
163
|
+
if (s !== Infinity) {
|
|
164
|
+
if (s + 1 < best)
|
|
165
|
+
best = s + 1;
|
|
166
|
+
detail = line; // one output line is enough to recognise the call; /expand <id> shows the rest
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ONE row per tool call: the count then answers "how many calls mention this", and the
|
|
171
|
+
// command that is itself the query does not show up as a second, identical-looking hit.
|
|
172
|
+
if (best !== Infinity)
|
|
173
|
+
push(hits, 'tools', e.id, `#${e.id} ${label}`, detail, best);
|
|
174
|
+
});
|
|
175
|
+
return top(hits, Infinity);
|
|
176
|
+
}
|
|
177
|
+
/** Git: changed paths, with the status letter kept in front so `M`/`??` is visible. */
|
|
178
|
+
function searchGit(query, changes) {
|
|
179
|
+
const hits = [];
|
|
180
|
+
changes.forEach((c, i) => {
|
|
181
|
+
const score = scoreText(query, c.path);
|
|
182
|
+
if (score !== Infinity)
|
|
183
|
+
push(hits, 'git', i + 1, `${c.status ? c.status + ' ' : ''}${c.path}`, undefined, score);
|
|
184
|
+
});
|
|
185
|
+
return top(hits, Infinity);
|
|
186
|
+
}
|
|
187
|
+
/** Errors: the failing command, the files it touched, and the re-tests. */
|
|
188
|
+
function searchErrors(query, incidents) {
|
|
189
|
+
const hits = [];
|
|
190
|
+
incidents.forEach((inc) => {
|
|
191
|
+
const head = `#${inc.id} ${inc.command}`;
|
|
192
|
+
const inHead = scoreText(query, head);
|
|
193
|
+
if (inHead !== Infinity)
|
|
194
|
+
push(hits, 'errors', inc.id, head, undefined, inHead);
|
|
195
|
+
for (const f of inc.files) {
|
|
196
|
+
const s = scoreText(query, f);
|
|
197
|
+
if (s !== Infinity)
|
|
198
|
+
push(hits, 'errors', inc.id, head, f, s + 1);
|
|
199
|
+
}
|
|
200
|
+
for (const r of inc.retests) {
|
|
201
|
+
const s = scoreText(query, r.command);
|
|
202
|
+
if (s !== Infinity)
|
|
203
|
+
push(hits, 'errors', inc.id, head, `[${r.ok ? 'ok' : 'fail'}] ${r.command}`, s + 1);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
return top(hits, Infinity);
|
|
207
|
+
}
|
|
208
|
+
/** Agents: type, task label and the files each one touched. */
|
|
209
|
+
function searchAgents(query, nodes) {
|
|
210
|
+
const hits = [];
|
|
211
|
+
nodes.forEach((n, i) => {
|
|
212
|
+
const head = `${n.name} ${n.label}`;
|
|
213
|
+
const inHead = scoreText(query, head);
|
|
214
|
+
if (inHead !== Infinity)
|
|
215
|
+
push(hits, 'agents', i + 1, head, n.error, inHead);
|
|
216
|
+
for (const f of n.files) {
|
|
217
|
+
const s = scoreText(query, f);
|
|
218
|
+
if (s !== Infinity)
|
|
219
|
+
push(hits, 'agents', i + 1, head, f, s + 1);
|
|
220
|
+
}
|
|
221
|
+
if (n.report) {
|
|
222
|
+
const s = scoreText(query, n.report);
|
|
223
|
+
if (s !== Infinity)
|
|
224
|
+
push(hits, 'agents', i + 1, head, 'report', s + 2);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return top(hits, Infinity);
|
|
228
|
+
}
|
|
229
|
+
// ─── The search itself ───────────────────────────────────────────────────────
|
|
230
|
+
function globalSearch(query, sources, perScope = exports.MAX_PER_SCOPE) {
|
|
231
|
+
const q = query.trim();
|
|
232
|
+
if (!q)
|
|
233
|
+
return { query: '', hits: [], counts: [] };
|
|
234
|
+
const all = [];
|
|
235
|
+
if (sources.files)
|
|
236
|
+
all.push(...searchFiles(q, sources.files));
|
|
237
|
+
if (sources.turns)
|
|
238
|
+
all.push(...searchConversation(q, sources.turns));
|
|
239
|
+
if (sources.tools)
|
|
240
|
+
all.push(...searchTools(q, sources.tools));
|
|
241
|
+
if (sources.git)
|
|
242
|
+
all.push(...searchGit(q, sources.git));
|
|
243
|
+
if (sources.errors)
|
|
244
|
+
all.push(...searchErrors(q, sources.errors));
|
|
245
|
+
if (sources.agents)
|
|
246
|
+
all.push(...searchAgents(q, sources.agents));
|
|
247
|
+
const counts = exports.SCOPES.map((scope) => ({ scope, count: all.filter((h) => h.scope === scope).length })).filter((c) => c.count > 0);
|
|
248
|
+
const hits = [];
|
|
249
|
+
for (const scope of exports.SCOPES) {
|
|
250
|
+
hits.push(...top(all.filter((h) => h.scope === scope), perScope));
|
|
251
|
+
}
|
|
252
|
+
return { query: q, hits, counts };
|
|
253
|
+
}
|
|
254
|
+
function filterScope(hits, scope) {
|
|
255
|
+
return scope ? hits.filter((h) => h.scope === scope) : hits;
|
|
256
|
+
}
|
|
257
|
+
// ─── Rendering ────────────────────────────────────────────────────────────────
|
|
258
|
+
/**
|
|
259
|
+
* The §52 counts block:
|
|
260
|
+
*
|
|
261
|
+
* > JWT
|
|
262
|
+
*
|
|
263
|
+
* Files 12
|
|
264
|
+
* Conversation 8
|
|
265
|
+
* Git 3
|
|
266
|
+
* Agents 2
|
|
267
|
+
*
|
|
268
|
+
* Labels pad to the widest, counts right-align to the widest — so the numbers line up even
|
|
269
|
+
* when "Conversation" sits next to "Files". Every line is clipped to `cols`; at narrow widths
|
|
270
|
+
* the label is what gets cut, never the count.
|
|
271
|
+
*/
|
|
272
|
+
function formatCounts(result, cols, opts = {}) {
|
|
273
|
+
const out = [];
|
|
274
|
+
if (!result.query)
|
|
275
|
+
return out;
|
|
276
|
+
out.push(fitLine(`> ${result.query}`, cols));
|
|
277
|
+
if (!result.counts.length) {
|
|
278
|
+
out.push('');
|
|
279
|
+
out.push(fitLine('No match in files, conversation, tools, git, errors or agents.', cols, ' '));
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
// With one scope selected the section heading below already carries its count (`Git 1`),
|
|
283
|
+
// so the table would only repeat it — but the `> query` header still belongs to the result.
|
|
284
|
+
if (opts.table === false)
|
|
285
|
+
return out;
|
|
286
|
+
const labelW = Math.max(...result.counts.map((c) => exports.SCOPE_LABEL[c.scope].length)) + 1;
|
|
287
|
+
const numW = Math.max(...result.counts.map((c) => String(c.count).length));
|
|
288
|
+
out.push('');
|
|
289
|
+
for (const c of result.counts) {
|
|
290
|
+
const label = exports.SCOPE_LABEL[c.scope].padEnd(labelW);
|
|
291
|
+
const num = String(c.count).padStart(numW);
|
|
292
|
+
out.push(fitLine(`${label}${num}`, cols, ' '));
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* One printed row per hit: ` 3. Git M src/app.js`, with the matched line under it when the
|
|
298
|
+
* label alone would not say why it matched.
|
|
299
|
+
*
|
|
300
|
+
* `tag` prepends the 3-letter scope — on when the rows are mixed, off inside a per-scope
|
|
301
|
+
* section where the heading already said it.
|
|
302
|
+
*/
|
|
303
|
+
function formatHits(hits, cols, indent = ' ', tag = true) {
|
|
304
|
+
const out = [];
|
|
305
|
+
hits.forEach((h, i) => {
|
|
306
|
+
const n = String(i + 1).padStart(3);
|
|
307
|
+
const mark = tag ? `${exports.SCOPE_LABEL[h.scope].slice(0, 3)} ` : '';
|
|
308
|
+
out.push(fitLine(`${n}. ${mark}${h.label}`, cols, indent));
|
|
309
|
+
if (h.detail && h.detail !== h.label)
|
|
310
|
+
out.push(fitLine(h.detail, cols, indent + ' '));
|
|
311
|
+
});
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
/** `Scopes: /find <q> --files --git` — only the flags that would actually narrow this result. */
|
|
315
|
+
function scopeHint(counts) {
|
|
316
|
+
if (!counts.length)
|
|
317
|
+
return '';
|
|
318
|
+
const flags = counts.map((c) => '--' + c.scope);
|
|
319
|
+
return `Scopes: /find <query> ${flags.join(' ')}`;
|
|
320
|
+
}
|
|
321
|
+
/** `/find JWT --git --tools` → query "JWT", scopes [git, tools]. `--all` clears the narrowing. */
|
|
322
|
+
function parseSearchArgs(arg) {
|
|
323
|
+
const words = arg.trim().split(/\s+/).filter(Boolean);
|
|
324
|
+
const scopes = [];
|
|
325
|
+
const bad = [];
|
|
326
|
+
const rest = [];
|
|
327
|
+
let all = false;
|
|
328
|
+
for (const w of words) {
|
|
329
|
+
const scope = SCOPE_FLAG[w.toLowerCase()];
|
|
330
|
+
if (scope) {
|
|
331
|
+
scopes.push(scope);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (w.toLowerCase() === '--all') {
|
|
335
|
+
all = true;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (w.startsWith('--')) {
|
|
339
|
+
bad.push(w);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
rest.push(w);
|
|
343
|
+
}
|
|
344
|
+
return { query: rest.join(' '), scopes: all || !scopes.length ? null : [...new Set(scopes)], bad };
|
|
345
|
+
}
|
|
346
|
+
function searchUsage() {
|
|
347
|
+
const flags = exports.SCOPES.map((s) => (s === 'conversation' ? '--conversation|--chat' : `--${s}`));
|
|
348
|
+
return `Usage: /find <text> ${flags.map((f) => `[${f}]`).join(' ')}`;
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=globalSearch.js.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ShareIoFactory, ShareStatus } from './sessionShare';
|
|
2
|
+
/** One pushed message, as it came off the wire (already shape-checked). */
|
|
3
|
+
export interface ChannelMessage {
|
|
4
|
+
nonce: string;
|
|
5
|
+
source: string;
|
|
6
|
+
text: string;
|
|
7
|
+
ts: number;
|
|
8
|
+
}
|
|
9
|
+
/** `ask` holds pushes for a human verdict; `auto` delivers them on arrival. */
|
|
10
|
+
export type ChannelPolicy = 'ask' | 'auto';
|
|
11
|
+
/** What the module reports for the terminal to render — data, not prose. */
|
|
12
|
+
export type ChannelNotice = {
|
|
13
|
+
kind: 'queued';
|
|
14
|
+
msg: ChannelMessage;
|
|
15
|
+
position: number;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'delivered';
|
|
18
|
+
msg: ChannelMessage;
|
|
19
|
+
auto: boolean;
|
|
20
|
+
folded: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'discarded';
|
|
23
|
+
msg: ChannelMessage;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'dropped';
|
|
26
|
+
msg: ChannelMessage;
|
|
27
|
+
reason: 'queue_full';
|
|
28
|
+
};
|
|
29
|
+
export interface SessionChannelDeps {
|
|
30
|
+
/** Origin of the platform API — the Socket.IO server shares the host. */
|
|
31
|
+
url: string;
|
|
32
|
+
sessionId: string;
|
|
33
|
+
/** Called before EVERY (re)connect attempt, so a rotated token is picked up. */
|
|
34
|
+
tokenProvider: () => string | null;
|
|
35
|
+
/** Called once after an auth-shaped connect error, like the share half. */
|
|
36
|
+
refreshToken?: () => Promise<boolean>;
|
|
37
|
+
ioFactory?: ShareIoFactory;
|
|
38
|
+
onStatus?: (s: ShareStatus) => void;
|
|
39
|
+
onNotice?: (n: ChannelNotice) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Deliver an accepted (or auto) message: `formatted` is what the model sees.
|
|
42
|
+
* Returns `queued` when the message was folded into a running turn instead
|
|
43
|
+
* of starting one (the REPL's busy path) — reported to watchers verbatim.
|
|
44
|
+
*/
|
|
45
|
+
onDeliver?: (formatted: string, msg: ChannelMessage) => 'accepted' | 'queued' | void;
|
|
46
|
+
/** Test seam — the only reason a token is not always crypto-random. */
|
|
47
|
+
mintToken?: () => string;
|
|
48
|
+
}
|
|
49
|
+
/** Token shape the CLI mints and the relay/route only ever hash. */
|
|
50
|
+
export declare const CHANNEL_TOKEN_PREFIX = "nxch_";
|
|
51
|
+
/** More undecided pushes than this and new ones are refused, not queued — an
|
|
52
|
+
* external source must not be able to bury the one message a human needs. */
|
|
53
|
+
export declare const CHANNEL_QUEUE_CAP = 10;
|
|
54
|
+
/** Matches the relay's and the route's own cap (defense in depth). */
|
|
55
|
+
export declare const CHANNEL_TEXT_MAX_CHARS = 8192;
|
|
56
|
+
/** S13c: what a source may read back — one turn's closing text, same bound. */
|
|
57
|
+
export declare const CHANNEL_REPLY_MAX_CHARS = 8192;
|
|
58
|
+
export declare function hashChannelToken(token: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* The push endpoint for a session. Built here so the CLI prints exactly the
|
|
61
|
+
* URL the route serves — one string, one place.
|
|
62
|
+
*/
|
|
63
|
+
export declare function channelPushUrl(id: string, url: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Escape `<` so a hostile push cannot close the wrapper tag early; `&` and
|
|
66
|
+
* `>` stay readable because CI URLs are the common case and the model reads
|
|
67
|
+
* this text, not an XML parser.
|
|
68
|
+
*/
|
|
69
|
+
export declare function escapeChannelText(text: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* What the conversation receives. Same `<channel source=…>` shape Claude Code
|
|
72
|
+
* documents for its own channels, so prompts and habits built for one read
|
|
73
|
+
* the same here — and it can never parse as a slash command.
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatChannelEvent(msg: ChannelMessage): string;
|
|
76
|
+
export declare class SessionChannel {
|
|
77
|
+
private readonly deps;
|
|
78
|
+
private socket;
|
|
79
|
+
private id;
|
|
80
|
+
private status;
|
|
81
|
+
private stopped;
|
|
82
|
+
private refreshing;
|
|
83
|
+
private policy;
|
|
84
|
+
private queue;
|
|
85
|
+
private seenNonces;
|
|
86
|
+
/** S13c: nonces delivered but not yet answered by a finished turn. */
|
|
87
|
+
private awaitingReply;
|
|
88
|
+
/** Minted once per open; rotated by a re-open after /channel close. */
|
|
89
|
+
private token;
|
|
90
|
+
private tokenHash;
|
|
91
|
+
constructor(deps: SessionChannelDeps);
|
|
92
|
+
get sessionId(): string;
|
|
93
|
+
get state(): ShareStatus;
|
|
94
|
+
get activePolicy(): ChannelPolicy;
|
|
95
|
+
/** The push URL a human pastes into CI; the token is NOT part of it. */
|
|
96
|
+
get pushUrl(): string;
|
|
97
|
+
/** Shown once by `/channel open` — memory only, never persisted anywhere. */
|
|
98
|
+
get rawToken(): string | null;
|
|
99
|
+
/** Pending pushes, oldest first. A copy: callers cannot mutate the queue. */
|
|
100
|
+
pending(): ChannelMessage[];
|
|
101
|
+
start(policy?: ChannelPolicy): this;
|
|
102
|
+
/** Switch policy — `auto` drains whatever is already queued, oldest first. */
|
|
103
|
+
setPolicy(policy: ChannelPolicy): ChannelPolicy;
|
|
104
|
+
/** Accept (deliver) one pending push — 1-based `index`, default the oldest. */
|
|
105
|
+
accept(index?: number): ChannelMessage | null;
|
|
106
|
+
/** Refuse one pending push — same indexing as accept. */
|
|
107
|
+
discard(index?: number): ChannelMessage | null;
|
|
108
|
+
/** /clear and /fork: a rebind reuses the token for the new session id. */
|
|
109
|
+
rebind(id: string): void;
|
|
110
|
+
stop(reason?: string): void;
|
|
111
|
+
private take;
|
|
112
|
+
private deliver;
|
|
113
|
+
/**
|
|
114
|
+
* S13c — close the loop: hand every delivered-but-unanswered push the
|
|
115
|
+
* closing text of the turn that consumed it (chat.ts calls this when a turn
|
|
116
|
+
* ends; `null`/blank means the turn said nothing useful, which is reported
|
|
117
|
+
* as an EXPLICIT no-text marker rather than silence). Frames are keyed by
|
|
118
|
+
* nonce and stored by the relay, readable only with the channel token.
|
|
119
|
+
* Returns how many nonces were answered.
|
|
120
|
+
*/
|
|
121
|
+
publishReplies(reply: string | null, opts?: {
|
|
122
|
+
interrupted?: boolean;
|
|
123
|
+
}): number;
|
|
124
|
+
/** Shape-check a pushed frame — the relay is a pipe, delivery trusts nothing. */
|
|
125
|
+
private validateMessage;
|
|
126
|
+
private ack;
|
|
127
|
+
private notice;
|
|
128
|
+
private emitOpen;
|
|
129
|
+
private emitTo;
|
|
130
|
+
private setStatus;
|
|
131
|
+
}
|
|
132
|
+
export declare function startSessionChannel(deps: SessionChannelDeps, policy?: ChannelPolicy): SessionChannel;
|
|
133
|
+
export declare function activeSessionChannel(): SessionChannel | null;
|
|
134
|
+
export declare function rebindSessionChannel(id: string): void;
|
|
135
|
+
export declare function stopSessionChannel(reason?: string): void;
|
|
136
|
+
//# sourceMappingURL=sessionChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionChannel.d.ts","sourceRoot":"","sources":["../../src/share/sessionChannel.ts"],"names":[],"mappings":"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"}
|