umbrella-context 0.1.37 → 0.1.39
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 +9 -2
- package/dist/adapters/umbrella-onboarding.js +3 -2
- package/dist/commands/connect.js +3 -2
- package/dist/commands/connectors.js +3 -0
- package/dist/commands/curate.d.ts +13 -1
- package/dist/commands/curate.js +146 -10
- package/dist/commands/fix.js +2 -10
- package/dist/commands/hub.d.ts +3 -1
- package/dist/commands/hub.js +218 -68
- package/dist/commands/interactive.js +21 -10
- package/dist/commands/locations.d.ts +6 -1
- package/dist/commands/locations.js +91 -5
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/pull.d.ts +6 -1
- package/dist/commands/pull.js +119 -3
- package/dist/commands/push.d.ts +6 -1
- package/dist/commands/push.js +147 -3
- package/dist/commands/restart.js +6 -1
- package/dist/commands/search.d.ts +5 -1
- package/dist/commands/search.js +857 -36
- package/dist/commands/session.js +0 -3
- package/dist/commands/setup.js +186 -26
- package/dist/commands/space.js +4 -3
- package/dist/commands/status.d.ts +16 -1
- package/dist/commands/status.js +111 -47
- package/dist/commands/tui.js +339 -107
- package/dist/config.d.ts +4 -0
- package/dist/config.js +6 -0
- package/dist/index.js +21 -3
- package/dist/repo-state.d.ts +115 -0
- package/dist/repo-state.js +195 -12
- package/package.json +2 -2
package/dist/config.d.ts
CHANGED
|
@@ -24,6 +24,9 @@ export interface HubRegistry {
|
|
|
24
24
|
id: string;
|
|
25
25
|
name: string;
|
|
26
26
|
url: string;
|
|
27
|
+
authScheme?: "none" | "bearer" | "token" | "basic" | "custom-header";
|
|
28
|
+
token?: string;
|
|
29
|
+
headerName?: string;
|
|
27
30
|
updatedAt: string;
|
|
28
31
|
}
|
|
29
32
|
export interface StoredCliState {
|
|
@@ -47,6 +50,7 @@ export declare class ConfigManager {
|
|
|
47
50
|
get locations(): SavedLocation[];
|
|
48
51
|
get providers(): SavedProvider[];
|
|
49
52
|
upsertLocation(location: SavedLocation): void;
|
|
53
|
+
replaceLocations(locations: SavedLocation[]): void;
|
|
50
54
|
upsertProvider(provider: SavedProvider): void;
|
|
51
55
|
removeProvider(id: string): void;
|
|
52
56
|
get configPath(): string;
|
package/dist/config.js
CHANGED
|
@@ -87,6 +87,11 @@ export class ConfigManager {
|
|
|
87
87
|
locations.push(location);
|
|
88
88
|
this.conf.set("locations", locations.sort((a, b) => a.repoRoot.localeCompare(b.repoRoot)));
|
|
89
89
|
}
|
|
90
|
+
replaceLocations(locations) {
|
|
91
|
+
this.conf.set("locations", locations
|
|
92
|
+
.slice()
|
|
93
|
+
.sort((a, b) => a.repoRoot.localeCompare(b.repoRoot)));
|
|
94
|
+
}
|
|
90
95
|
upsertProvider(provider) {
|
|
91
96
|
const providers = this.providers.filter((entry) => entry.id !== provider.id && entry.name.toLowerCase() !== provider.name.toLowerCase());
|
|
92
97
|
providers.push(provider);
|
|
@@ -115,6 +120,7 @@ export class ConfigManager {
|
|
|
115
120
|
id: "umbrella-default",
|
|
116
121
|
name: "Umbrella Hub",
|
|
117
122
|
url: "https://hub.umbrella.local/default",
|
|
123
|
+
authScheme: "none",
|
|
118
124
|
updatedAt: new Date(0).toISOString(),
|
|
119
125
|
},
|
|
120
126
|
];
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { cac } from "cac";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { setupCommand } from "./commands/setup.js";
|
|
4
5
|
import { loginCommand } from "./commands/login.js";
|
|
5
6
|
import { connectCommand } from "./commands/connect.js";
|
|
@@ -29,6 +30,16 @@ import { treeCommand } from "./commands/tree.js";
|
|
|
29
30
|
import { transportCommand } from "./commands/transport.js";
|
|
30
31
|
import { bridgeCommand } from "./commands/bridge.js";
|
|
31
32
|
import { curateViewCommandAction } from "./commands/curate.js";
|
|
33
|
+
function readCliVersion() {
|
|
34
|
+
try {
|
|
35
|
+
const packageJsonUrl = new URL("../package.json", import.meta.url);
|
|
36
|
+
const packageJson = JSON.parse(readFileSync(packageJsonUrl, "utf8"));
|
|
37
|
+
return packageJson.version ?? "0.0.0";
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return "0.0.0";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
32
43
|
const cli = cac("umbrella-context");
|
|
33
44
|
cli.option("--config <path>", "Path to config file");
|
|
34
45
|
import { seedCommand } from "./commands/seed.js";
|
|
@@ -61,12 +72,19 @@ tasksCommand(cli);
|
|
|
61
72
|
treeCommand(cli);
|
|
62
73
|
transportCommand(cli);
|
|
63
74
|
bridgeCommand(cli);
|
|
64
|
-
cli
|
|
75
|
+
cli
|
|
76
|
+
.command("query [...args]", `Alias for search
|
|
77
|
+
|
|
78
|
+
Ask a natural-language question about your codebase or saved team context.`)
|
|
79
|
+
.option("--format <format>", "Output format (text or json)")
|
|
80
|
+
.example('query "How does authentication work?"')
|
|
81
|
+
.example('query "How does auth work?" --format json')
|
|
82
|
+
.action(async (args, opts) => {
|
|
65
83
|
const { searchCommandAction } = await import("./commands/search.js");
|
|
66
|
-
await searchCommandAction(args.join(" "));
|
|
84
|
+
await searchCommandAction(args.join(" "), opts);
|
|
67
85
|
});
|
|
68
86
|
cli.help();
|
|
69
|
-
cli.version(
|
|
87
|
+
cli.version(readCliVersion());
|
|
70
88
|
const argv = process.argv.slice(2);
|
|
71
89
|
if (argv[0] === "curate" && argv[1] === "view" && argv.length === 2) {
|
|
72
90
|
await curateViewCommandAction();
|
package/dist/repo-state.d.ts
CHANGED
|
@@ -3,10 +3,49 @@ export type LocalMemoryEntry = {
|
|
|
3
3
|
id: string;
|
|
4
4
|
content: string;
|
|
5
5
|
tags: string[];
|
|
6
|
+
keywords: string[];
|
|
7
|
+
category: string | null;
|
|
8
|
+
accessLevel: "space" | "company";
|
|
6
9
|
systemType: string;
|
|
7
10
|
source: string;
|
|
8
11
|
createdAt: string;
|
|
9
12
|
};
|
|
13
|
+
export type LocalQueryCacheEntry = {
|
|
14
|
+
id: string;
|
|
15
|
+
query: string;
|
|
16
|
+
normalizedQuery: string;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
payload: {
|
|
19
|
+
companyName: string;
|
|
20
|
+
spaceName: string;
|
|
21
|
+
query: string;
|
|
22
|
+
localMatches: LocalMemoryEntry[];
|
|
23
|
+
serverMatches: Array<Record<string, unknown>>;
|
|
24
|
+
total: number;
|
|
25
|
+
suggestions: string[];
|
|
26
|
+
providerReady?: boolean;
|
|
27
|
+
providerRequiredForSynthesis?: boolean;
|
|
28
|
+
directAnswer?: {
|
|
29
|
+
summary: string;
|
|
30
|
+
details: string;
|
|
31
|
+
sources: string[];
|
|
32
|
+
confidence: "high" | "medium";
|
|
33
|
+
gaps?: string;
|
|
34
|
+
} | null;
|
|
35
|
+
synthesizedAnswer?: {
|
|
36
|
+
summary: string;
|
|
37
|
+
details: string;
|
|
38
|
+
sources: string[];
|
|
39
|
+
confidence: "high" | "medium";
|
|
40
|
+
gaps?: string;
|
|
41
|
+
provider?: string;
|
|
42
|
+
model?: string;
|
|
43
|
+
} | null;
|
|
44
|
+
};
|
|
45
|
+
createdAt: string;
|
|
46
|
+
updatedAt: string;
|
|
47
|
+
hits: number;
|
|
48
|
+
};
|
|
10
49
|
export type LocalFixEntry = {
|
|
11
50
|
id: string;
|
|
12
51
|
errorSignal: string;
|
|
@@ -219,6 +258,81 @@ export declare function getRepoContext(cwd?: string): Promise<{
|
|
|
219
258
|
export declare function updateRepoContext(updater: (current: RepoContextState) => RepoContextState | Promise<RepoContextState>, cwd?: string): Promise<RepoContextState>;
|
|
220
259
|
export declare function addPendingMemory(input: Omit<LocalMemoryEntry, "createdAt" | "id">, cwd?: string): Promise<LocalMemoryEntry>;
|
|
221
260
|
export declare function getPendingMemories(cwd?: string): Promise<LocalMemoryEntry[]>;
|
|
261
|
+
export declare function getQueryCacheEntries(cwd?: string): Promise<{
|
|
262
|
+
payload: {
|
|
263
|
+
localMatches: LocalMemoryEntry[];
|
|
264
|
+
serverMatches: Record<string, unknown>[];
|
|
265
|
+
suggestions: string[];
|
|
266
|
+
providerReady: boolean;
|
|
267
|
+
providerRequiredForSynthesis: boolean;
|
|
268
|
+
directAnswer: {
|
|
269
|
+
summary: string;
|
|
270
|
+
details: string;
|
|
271
|
+
sources: string[];
|
|
272
|
+
confidence: "high" | "medium";
|
|
273
|
+
gaps?: string;
|
|
274
|
+
} | null;
|
|
275
|
+
synthesizedAnswer: {
|
|
276
|
+
summary: string;
|
|
277
|
+
details: string;
|
|
278
|
+
sources: string[];
|
|
279
|
+
confidence: "high" | "medium";
|
|
280
|
+
gaps?: string;
|
|
281
|
+
provider?: string;
|
|
282
|
+
model?: string;
|
|
283
|
+
} | null;
|
|
284
|
+
total: number;
|
|
285
|
+
companyName: string;
|
|
286
|
+
spaceName: string;
|
|
287
|
+
query: string;
|
|
288
|
+
};
|
|
289
|
+
hits: number;
|
|
290
|
+
createdAt: string;
|
|
291
|
+
updatedAt: string;
|
|
292
|
+
id: string;
|
|
293
|
+
query: string;
|
|
294
|
+
normalizedQuery: string;
|
|
295
|
+
fingerprint: string;
|
|
296
|
+
}[]>;
|
|
297
|
+
export declare function getQueryCacheEntry(query: string, fingerprint: string, cwd?: string): Promise<{
|
|
298
|
+
payload: {
|
|
299
|
+
localMatches: LocalMemoryEntry[];
|
|
300
|
+
serverMatches: Record<string, unknown>[];
|
|
301
|
+
suggestions: string[];
|
|
302
|
+
providerReady: boolean;
|
|
303
|
+
providerRequiredForSynthesis: boolean;
|
|
304
|
+
directAnswer: {
|
|
305
|
+
summary: string;
|
|
306
|
+
details: string;
|
|
307
|
+
sources: string[];
|
|
308
|
+
confidence: "high" | "medium";
|
|
309
|
+
gaps?: string;
|
|
310
|
+
} | null;
|
|
311
|
+
synthesizedAnswer: {
|
|
312
|
+
summary: string;
|
|
313
|
+
details: string;
|
|
314
|
+
sources: string[];
|
|
315
|
+
confidence: "high" | "medium";
|
|
316
|
+
gaps?: string;
|
|
317
|
+
provider?: string;
|
|
318
|
+
model?: string;
|
|
319
|
+
} | null;
|
|
320
|
+
total: number;
|
|
321
|
+
companyName: string;
|
|
322
|
+
spaceName: string;
|
|
323
|
+
query: string;
|
|
324
|
+
};
|
|
325
|
+
hits: number;
|
|
326
|
+
createdAt: string;
|
|
327
|
+
updatedAt: string;
|
|
328
|
+
id: string;
|
|
329
|
+
query: string;
|
|
330
|
+
normalizedQuery: string;
|
|
331
|
+
fingerprint: string;
|
|
332
|
+
} | null>;
|
|
333
|
+
export declare function buildLocalQueryFingerprint(cwd?: string): Promise<string>;
|
|
334
|
+
export declare function storeQueryCacheEntry(input: Omit<LocalQueryCacheEntry, "createdAt" | "hits" | "id" | "updatedAt">, cwd?: string): Promise<void>;
|
|
335
|
+
export declare function markQueryCacheHit(entryId: string, cwd?: string): Promise<void>;
|
|
222
336
|
export declare function clearPendingMemories(cwd?: string): Promise<void>;
|
|
223
337
|
export declare function resetRepoState(cwd?: string): Promise<{
|
|
224
338
|
repoRoot: string;
|
|
@@ -260,6 +374,7 @@ export declare function appendTaskStreamingContent(taskId: string, content: stri
|
|
|
260
374
|
export declare function addTaskReasoningContent(taskId: string, content: string, isThinking?: boolean, cwd?: string): Promise<LocalTask | null>;
|
|
261
375
|
export declare function addTaskToolCall(taskId: string, toolCall: TaskToolCallPatch, cwd?: string): Promise<LocalTask | null>;
|
|
262
376
|
export declare function completeTask(taskId: string, status: "completed" | "failed", detail?: string | null, cwd?: string): Promise<LocalTask | null>;
|
|
377
|
+
export declare function removeTask(taskId: string, cwd?: string): Promise<void>;
|
|
263
378
|
export declare function addConnectorRun(input: Omit<LocalConnectorRun, "id" | "ranAt">, cwd?: string): Promise<LocalConnectorRun>;
|
|
264
379
|
export declare function writeConnectorRunReport(run: LocalConnectorRun, cwd?: string): Promise<string>;
|
|
265
380
|
export declare function getSessionState(cwd?: string): Promise<RepoSessionState | null>;
|
package/dist/repo-state.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
3
|
+
import { createHash, randomUUID } from "crypto";
|
|
4
4
|
import { execFileSync } from "child_process";
|
|
5
5
|
import { configManager } from "./config.js";
|
|
6
6
|
const UM_DIR = ".um";
|
|
@@ -15,9 +15,42 @@ const SESSION_FILE = "session.json";
|
|
|
15
15
|
const TASKS_FILE = "tasks.json";
|
|
16
16
|
const TRANSPORT_FILE = "transport.json";
|
|
17
17
|
const CONTEXT_TREE_STATE_FILE = "context-tree.json";
|
|
18
|
+
const QUERY_CACHE_FILE = "query-cache.json";
|
|
18
19
|
const HUB_ASSETS_DIR = "hub";
|
|
19
20
|
const CONNECTOR_ASSETS_DIR = "connectors";
|
|
20
21
|
const CONNECTOR_RUN_REPORTS_DIR = "connector-runs";
|
|
22
|
+
const SESSION_PANEL_ALIASES = {
|
|
23
|
+
connect: "setup",
|
|
24
|
+
fix: "activity",
|
|
25
|
+
login: "setup",
|
|
26
|
+
projects: "spaces",
|
|
27
|
+
pull: "home",
|
|
28
|
+
push: "home",
|
|
29
|
+
query: "home",
|
|
30
|
+
record: "activity",
|
|
31
|
+
search: "home",
|
|
32
|
+
space: "spaces",
|
|
33
|
+
};
|
|
34
|
+
function normalizeSessionPanel(panel) {
|
|
35
|
+
if (!panel)
|
|
36
|
+
return null;
|
|
37
|
+
return SESSION_PANEL_ALIASES[panel] ?? panel;
|
|
38
|
+
}
|
|
39
|
+
function normalizeLocalMemoryEntry(entry) {
|
|
40
|
+
return {
|
|
41
|
+
...entry,
|
|
42
|
+
tags: entry.tags ?? [],
|
|
43
|
+
keywords: entry.keywords ?? [],
|
|
44
|
+
category: entry.category ?? null,
|
|
45
|
+
accessLevel: entry.accessLevel ?? "space",
|
|
46
|
+
systemType: entry.systemType ?? "system1_knowledge",
|
|
47
|
+
source: entry.source ?? "cli",
|
|
48
|
+
createdAt: entry.createdAt ?? new Date().toISOString(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function normalizeLocalMemoryEntries(entries) {
|
|
52
|
+
return entries.map((entry) => normalizeLocalMemoryEntry(entry));
|
|
53
|
+
}
|
|
21
54
|
function normalizeSessionState(state) {
|
|
22
55
|
if (!state)
|
|
23
56
|
return null;
|
|
@@ -27,14 +60,19 @@ function normalizeSessionState(state) {
|
|
|
27
60
|
recentQueries: state.recentQueries ?? [],
|
|
28
61
|
recentCurations: state.recentCurations ?? [],
|
|
29
62
|
lastSummary: state.lastSummary ?? null,
|
|
30
|
-
currentPanel: state.currentPanel ?? null,
|
|
63
|
+
currentPanel: normalizeSessionPanel(state.currentPanel ?? null),
|
|
31
64
|
currentFocus: state.currentFocus ?? null,
|
|
32
|
-
panelHistory: state.panelHistory ?? []
|
|
65
|
+
panelHistory: (state.panelHistory ?? [])
|
|
66
|
+
.map((panel) => normalizeSessionPanel(panel))
|
|
67
|
+
.filter((panel) => Boolean(panel)),
|
|
33
68
|
recentProviderIds: state.recentProviderIds ?? [],
|
|
34
69
|
recentModels: state.recentModels ?? [],
|
|
35
70
|
recentHubSlugs: state.recentHubSlugs ?? [],
|
|
36
71
|
recentConnectorSources: state.recentConnectorSources ?? [],
|
|
37
|
-
events: state.events ?? []
|
|
72
|
+
events: (state.events ?? []).map((event) => ({
|
|
73
|
+
...event,
|
|
74
|
+
panel: normalizeSessionPanel(event.panel ?? null),
|
|
75
|
+
})),
|
|
38
76
|
};
|
|
39
77
|
}
|
|
40
78
|
function normalizeTransportState(state) {
|
|
@@ -191,6 +229,9 @@ function getTransportFile(repoRoot) {
|
|
|
191
229
|
function getContextTreeStateFile(repoRoot) {
|
|
192
230
|
return path.join(getUmDir(repoRoot), CONTEXT_TREE_STATE_FILE);
|
|
193
231
|
}
|
|
232
|
+
function getQueryCacheFile(repoRoot) {
|
|
233
|
+
return path.join(getUmDir(repoRoot), QUERY_CACHE_FILE);
|
|
234
|
+
}
|
|
194
235
|
async function ensureUmDir(repoRoot) {
|
|
195
236
|
await fs.mkdir(getUmDir(repoRoot), { recursive: true });
|
|
196
237
|
}
|
|
@@ -303,6 +344,9 @@ export async function addPendingMemory(input, cwd = process.cwd()) {
|
|
|
303
344
|
id: randomUUID(),
|
|
304
345
|
createdAt: new Date().toISOString(),
|
|
305
346
|
...input,
|
|
347
|
+
keywords: input.keywords ?? [],
|
|
348
|
+
category: input.category ?? null,
|
|
349
|
+
accessLevel: input.accessLevel ?? "space",
|
|
306
350
|
};
|
|
307
351
|
current.push(entry);
|
|
308
352
|
await writeJsonFile(getPendingMemoriesFile(repoRoot), current);
|
|
@@ -315,7 +359,95 @@ export async function addPendingMemory(input, cwd = process.cwd()) {
|
|
|
315
359
|
}
|
|
316
360
|
export async function getPendingMemories(cwd = process.cwd()) {
|
|
317
361
|
const { repoRoot } = await getRepoContext(cwd);
|
|
318
|
-
|
|
362
|
+
const entries = await readJsonFile(getPendingMemoriesFile(repoRoot), []);
|
|
363
|
+
return normalizeLocalMemoryEntries(entries);
|
|
364
|
+
}
|
|
365
|
+
function normalizeQueryCacheEntries(entries) {
|
|
366
|
+
return entries.map((entry) => ({
|
|
367
|
+
...entry,
|
|
368
|
+
payload: {
|
|
369
|
+
...entry.payload,
|
|
370
|
+
localMatches: normalizeLocalMemoryEntries(entry.payload.localMatches ?? []),
|
|
371
|
+
serverMatches: entry.payload.serverMatches ?? [],
|
|
372
|
+
suggestions: entry.payload.suggestions ?? [],
|
|
373
|
+
providerReady: entry.payload.providerReady ?? false,
|
|
374
|
+
providerRequiredForSynthesis: entry.payload.providerRequiredForSynthesis ?? false,
|
|
375
|
+
directAnswer: entry.payload.directAnswer ?? null,
|
|
376
|
+
synthesizedAnswer: entry.payload.synthesizedAnswer ?? null,
|
|
377
|
+
total: entry.payload.total ??
|
|
378
|
+
(entry.payload.localMatches?.length ?? 0) + (entry.payload.serverMatches?.length ?? 0),
|
|
379
|
+
},
|
|
380
|
+
hits: entry.hits ?? 0,
|
|
381
|
+
createdAt: entry.createdAt ?? new Date().toISOString(),
|
|
382
|
+
updatedAt: entry.updatedAt ?? new Date().toISOString(),
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
export async function getQueryCacheEntries(cwd = process.cwd()) {
|
|
386
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
387
|
+
const entries = await readJsonFile(getQueryCacheFile(repoRoot), []);
|
|
388
|
+
return normalizeQueryCacheEntries(entries);
|
|
389
|
+
}
|
|
390
|
+
export async function getQueryCacheEntry(query, fingerprint, cwd = process.cwd()) {
|
|
391
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
392
|
+
const entries = await getQueryCacheEntries(cwd);
|
|
393
|
+
return (entries.find((entry) => entry.normalizedQuery === normalizedQuery && entry.fingerprint === fingerprint) ?? null);
|
|
394
|
+
}
|
|
395
|
+
export async function buildLocalQueryFingerprint(cwd = process.cwd()) {
|
|
396
|
+
const [pending, pulled] = await Promise.all([getPendingMemories(cwd), getPulledMemories(cwd)]);
|
|
397
|
+
const stablePayload = [...pending, ...pulled]
|
|
398
|
+
.map((entry) => ({
|
|
399
|
+
id: entry.id,
|
|
400
|
+
content: entry.content,
|
|
401
|
+
tags: [...entry.tags].sort(),
|
|
402
|
+
keywords: [...(entry.keywords ?? [])].sort(),
|
|
403
|
+
category: entry.category ?? null,
|
|
404
|
+
accessLevel: entry.accessLevel ?? "space",
|
|
405
|
+
source: entry.source,
|
|
406
|
+
systemType: entry.systemType,
|
|
407
|
+
createdAt: entry.createdAt,
|
|
408
|
+
}))
|
|
409
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
410
|
+
return createHash("sha1")
|
|
411
|
+
.update(JSON.stringify(stablePayload))
|
|
412
|
+
.digest("hex");
|
|
413
|
+
}
|
|
414
|
+
export async function storeQueryCacheEntry(input, cwd = process.cwd()) {
|
|
415
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
416
|
+
await ensureUmDir(repoRoot);
|
|
417
|
+
const entries = await getQueryCacheEntries(cwd);
|
|
418
|
+
const now = new Date().toISOString();
|
|
419
|
+
const existing = entries.find((entry) => entry.normalizedQuery === input.normalizedQuery && entry.fingerprint === input.fingerprint);
|
|
420
|
+
const nextEntries = existing
|
|
421
|
+
? entries.map((entry) => entry.id === existing.id
|
|
422
|
+
? {
|
|
423
|
+
...entry,
|
|
424
|
+
query: input.query,
|
|
425
|
+
payload: input.payload,
|
|
426
|
+
updatedAt: now,
|
|
427
|
+
}
|
|
428
|
+
: entry)
|
|
429
|
+
: [
|
|
430
|
+
{
|
|
431
|
+
id: randomUUID(),
|
|
432
|
+
createdAt: now,
|
|
433
|
+
updatedAt: now,
|
|
434
|
+
hits: 0,
|
|
435
|
+
...input,
|
|
436
|
+
},
|
|
437
|
+
...entries,
|
|
438
|
+
];
|
|
439
|
+
await writeJsonFile(getQueryCacheFile(repoRoot), nextEntries.slice(0, 50));
|
|
440
|
+
}
|
|
441
|
+
export async function markQueryCacheHit(entryId, cwd = process.cwd()) {
|
|
442
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
443
|
+
const entries = await getQueryCacheEntries(cwd);
|
|
444
|
+
await writeJsonFile(getQueryCacheFile(repoRoot), entries.map((entry) => entry.id === entryId
|
|
445
|
+
? {
|
|
446
|
+
...entry,
|
|
447
|
+
hits: (entry.hits ?? 0) + 1,
|
|
448
|
+
updatedAt: new Date().toISOString(),
|
|
449
|
+
}
|
|
450
|
+
: entry));
|
|
319
451
|
}
|
|
320
452
|
export async function clearPendingMemories(cwd = process.cwd()) {
|
|
321
453
|
const { repoRoot } = await getRepoContext(cwd);
|
|
@@ -401,7 +533,7 @@ export async function resetRepoState(cwd = process.cwd()) {
|
|
|
401
533
|
export async function setPulledMemories(memories, cwd = process.cwd()) {
|
|
402
534
|
const { repoRoot } = await getRepoContext(cwd);
|
|
403
535
|
await ensureUmDir(repoRoot);
|
|
404
|
-
await writeJsonFile(getPulledMemoriesFile(repoRoot), memories);
|
|
536
|
+
await writeJsonFile(getPulledMemoriesFile(repoRoot), normalizeLocalMemoryEntries(memories));
|
|
405
537
|
await updateRepoContext((state) => ({
|
|
406
538
|
...state,
|
|
407
539
|
updatedAt: new Date().toISOString(),
|
|
@@ -419,7 +551,8 @@ export async function setPulledMemories(memories, cwd = process.cwd()) {
|
|
|
419
551
|
}
|
|
420
552
|
export async function getPulledMemories(cwd = process.cwd()) {
|
|
421
553
|
const { repoRoot } = await getRepoContext(cwd);
|
|
422
|
-
|
|
554
|
+
const entries = await readJsonFile(getPulledMemoriesFile(repoRoot), []);
|
|
555
|
+
return normalizeLocalMemoryEntries(entries);
|
|
423
556
|
}
|
|
424
557
|
export async function setPulledFixes(fixes, cwd = process.cwd()) {
|
|
425
558
|
const { repoRoot } = await getRepoContext(cwd);
|
|
@@ -867,6 +1000,14 @@ export async function completeTask(taskId, status, detail, cwd = process.cwd())
|
|
|
867
1000
|
result: detail ?? null,
|
|
868
1001
|
}, cwd);
|
|
869
1002
|
}
|
|
1003
|
+
export async function removeTask(taskId, cwd = process.cwd()) {
|
|
1004
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
1005
|
+
const tasks = await readJsonFile(getTasksFile(repoRoot), []);
|
|
1006
|
+
const nextTasks = tasks.filter((task) => task.id !== taskId);
|
|
1007
|
+
await writeJsonFile(getTasksFile(repoRoot), nextTasks);
|
|
1008
|
+
await syncTransportFromTasks(cwd);
|
|
1009
|
+
await rebuildContextTreeState(cwd);
|
|
1010
|
+
}
|
|
870
1011
|
export async function addConnectorRun(input, cwd = process.cwd()) {
|
|
871
1012
|
const { repoRoot } = await getRepoContext(cwd);
|
|
872
1013
|
await ensureUmDir(repoRoot);
|
|
@@ -1024,13 +1165,14 @@ export async function setSessionSummary(summary, cwd = process.cwd()) {
|
|
|
1024
1165
|
}), cwd);
|
|
1025
1166
|
}
|
|
1026
1167
|
export async function setSessionPanel(panel, focus, cwd = process.cwd()) {
|
|
1168
|
+
const normalizedPanel = normalizeSessionPanel(panel);
|
|
1027
1169
|
return updateSessionState((state) => ({
|
|
1028
1170
|
...state,
|
|
1029
1171
|
updatedAt: new Date().toISOString(),
|
|
1030
|
-
currentPanel:
|
|
1172
|
+
currentPanel: normalizedPanel,
|
|
1031
1173
|
currentFocus: focus ?? null,
|
|
1032
|
-
panelHistory:
|
|
1033
|
-
? [
|
|
1174
|
+
panelHistory: normalizedPanel
|
|
1175
|
+
? [normalizedPanel, ...(state.panelHistory ?? []).filter((entry) => entry !== normalizedPanel)].slice(0, 10)
|
|
1034
1176
|
: (state.panelHistory ?? []),
|
|
1035
1177
|
}), cwd);
|
|
1036
1178
|
}
|
|
@@ -1080,8 +1222,49 @@ export function summarizeLocalMemoryMatches(entries, query) {
|
|
|
1080
1222
|
const needle = query.trim().toLowerCase();
|
|
1081
1223
|
if (!needle)
|
|
1082
1224
|
return [];
|
|
1225
|
+
const normalizedWords = needle
|
|
1226
|
+
.replace(/[?!.:,;()[\]{}]/g, " ")
|
|
1227
|
+
.split(/\s+/)
|
|
1228
|
+
.map((word) => word.trim())
|
|
1229
|
+
.filter(Boolean)
|
|
1230
|
+
.filter((word) => !new Set([
|
|
1231
|
+
"a",
|
|
1232
|
+
"an",
|
|
1233
|
+
"and",
|
|
1234
|
+
"are",
|
|
1235
|
+
"do",
|
|
1236
|
+
"does",
|
|
1237
|
+
"how",
|
|
1238
|
+
"implemented",
|
|
1239
|
+
"is",
|
|
1240
|
+
"the",
|
|
1241
|
+
"what",
|
|
1242
|
+
"where",
|
|
1243
|
+
"why",
|
|
1244
|
+
]).has(word));
|
|
1245
|
+
const synonymMap = {
|
|
1246
|
+
auth: ["authentication", "signin", "sign-in", "jwt", "token", "cookie", "cookies"],
|
|
1247
|
+
authentication: ["auth", "signin", "sign-in", "jwt", "token", "cookie", "cookies"],
|
|
1248
|
+
rate: ["throttle", "limit", "limits", "limiting"],
|
|
1249
|
+
limits: ["limit", "rate", "throttle", "limiting"],
|
|
1250
|
+
};
|
|
1083
1251
|
return entries.filter((entry) => {
|
|
1084
|
-
const haystack = [entry.content, ...entry.tags
|
|
1085
|
-
|
|
1252
|
+
const haystack = [entry.content, ...entry.tags, ...(entry.keywords ?? [])]
|
|
1253
|
+
.join(" ")
|
|
1254
|
+
.toLowerCase();
|
|
1255
|
+
if (haystack.includes(needle))
|
|
1256
|
+
return true;
|
|
1257
|
+
let matchedWords = 0;
|
|
1258
|
+
for (const word of normalizedWords) {
|
|
1259
|
+
const variants = [word, ...(synonymMap[word] ?? [])];
|
|
1260
|
+
if (variants.some((variant) => haystack.includes(variant))) {
|
|
1261
|
+
matchedWords += 1;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (normalizedWords.length === 0)
|
|
1265
|
+
return false;
|
|
1266
|
+
if (normalizedWords.length === 1)
|
|
1267
|
+
return matchedWords >= 1;
|
|
1268
|
+
return matchedWords >= Math.min(2, normalizedWords.length);
|
|
1086
1269
|
});
|
|
1087
1270
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "umbrella-context",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "Umbrella Context CLI for connecting a device to company context spaces, querying saved context, and syncing MCP access.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"conf": "^13.1.0",
|
|
28
28
|
"ink": "^5.2.1",
|
|
29
29
|
"ink-spinner": "^5.0.0",
|
|
30
|
-
"ink-text-input": "^
|
|
30
|
+
"ink-text-input": "^6.0.0",
|
|
31
31
|
"prompts": "^2.4.2",
|
|
32
32
|
"react": "^18.3.1",
|
|
33
33
|
"zod": "^3.25.76",
|