umbrella-context 0.1.2
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 +41 -0
- package/dist/commands/connect.d.ts +1 -0
- package/dist/commands/connect.js +69 -0
- package/dist/commands/curate.d.ts +5 -0
- package/dist/commands/curate.js +35 -0
- package/dist/commands/fix.d.ts +1 -0
- package/dist/commands/fix.js +40 -0
- package/dist/commands/interactive.d.ts +1 -0
- package/dist/commands/interactive.js +145 -0
- package/dist/commands/locations.d.ts +1 -0
- package/dist/commands/locations.js +17 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +7 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +73 -0
- package/dist/commands/pull.d.ts +2 -0
- package/dist/commands/pull.js +58 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +65 -0
- package/dist/commands/record.d.ts +1 -0
- package/dist/commands/record.js +37 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +57 -0
- package/dist/commands/seed.d.ts +1 -0
- package/dist/commands/seed.js +117 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +171 -0
- package/dist/commands/space.d.ts +1 -0
- package/dist/commands/space.js +75 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +32 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +63 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +42 -0
- package/dist/repo-state.d.ts +53 -0
- package/dist/repo-state.js +171 -0
- package/dist/umbrella.d.ts +45 -0
- package/dist/umbrella.js +108 -0
- package/package.json +41 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
const UM_DIR = ".um";
|
|
5
|
+
const CONTEXT_FILE = "context.json";
|
|
6
|
+
const PENDING_MEMORIES_FILE = "pending-memories.json";
|
|
7
|
+
const PULLED_MEMORIES_FILE = "pulled-memories.json";
|
|
8
|
+
const PULLED_FIXES_FILE = "pulled-fixes.json";
|
|
9
|
+
async function pathExists(targetPath) {
|
|
10
|
+
try {
|
|
11
|
+
await fs.access(targetPath);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function findRepoRoot(startDir = process.cwd()) {
|
|
19
|
+
let current = path.resolve(startDir);
|
|
20
|
+
while (true) {
|
|
21
|
+
if (await pathExists(path.join(current, ".git")))
|
|
22
|
+
return current;
|
|
23
|
+
if (await pathExists(path.join(current, "package.json")))
|
|
24
|
+
return current;
|
|
25
|
+
const parent = path.dirname(current);
|
|
26
|
+
if (parent === current)
|
|
27
|
+
return path.resolve(startDir);
|
|
28
|
+
current = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function getUmDir(repoRoot) {
|
|
32
|
+
return path.join(repoRoot, UM_DIR);
|
|
33
|
+
}
|
|
34
|
+
function getContextFile(repoRoot) {
|
|
35
|
+
return path.join(getUmDir(repoRoot), CONTEXT_FILE);
|
|
36
|
+
}
|
|
37
|
+
function getPendingMemoriesFile(repoRoot) {
|
|
38
|
+
return path.join(getUmDir(repoRoot), PENDING_MEMORIES_FILE);
|
|
39
|
+
}
|
|
40
|
+
function getPulledMemoriesFile(repoRoot) {
|
|
41
|
+
return path.join(getUmDir(repoRoot), PULLED_MEMORIES_FILE);
|
|
42
|
+
}
|
|
43
|
+
function getPulledFixesFile(repoRoot) {
|
|
44
|
+
return path.join(getUmDir(repoRoot), PULLED_FIXES_FILE);
|
|
45
|
+
}
|
|
46
|
+
async function ensureUmDir(repoRoot) {
|
|
47
|
+
await fs.mkdir(getUmDir(repoRoot), { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
async function readJsonFile(filePath, fallback) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
52
|
+
return JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function writeJsonFile(filePath, data) {
|
|
59
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8");
|
|
60
|
+
}
|
|
61
|
+
export async function ensureRepoContext(config, cwd = process.cwd()) {
|
|
62
|
+
const repoRoot = await findRepoRoot(cwd);
|
|
63
|
+
await ensureUmDir(repoRoot);
|
|
64
|
+
const now = new Date().toISOString();
|
|
65
|
+
const nextState = {
|
|
66
|
+
version: 1,
|
|
67
|
+
repoRoot,
|
|
68
|
+
umDir: getUmDir(repoRoot),
|
|
69
|
+
companyId: config.companyId,
|
|
70
|
+
companyName: config.companyName,
|
|
71
|
+
workspaceId: config.workspaceId,
|
|
72
|
+
workspaceName: config.workspaceName,
|
|
73
|
+
projectId: config.projectId,
|
|
74
|
+
projectName: config.projectName,
|
|
75
|
+
umbrellaUrl: config.umbrellaUrl ?? null,
|
|
76
|
+
serverUrl: config.serverUrl,
|
|
77
|
+
initializedAt: now,
|
|
78
|
+
updatedAt: now,
|
|
79
|
+
lastPushAt: null,
|
|
80
|
+
lastPullAt: null,
|
|
81
|
+
};
|
|
82
|
+
const existing = await readJsonFile(getContextFile(repoRoot), null);
|
|
83
|
+
await writeJsonFile(getContextFile(repoRoot), {
|
|
84
|
+
...(existing ?? {}),
|
|
85
|
+
...nextState,
|
|
86
|
+
initializedAt: existing?.initializedAt ?? now,
|
|
87
|
+
});
|
|
88
|
+
return repoRoot;
|
|
89
|
+
}
|
|
90
|
+
export async function getRepoContext(cwd = process.cwd()) {
|
|
91
|
+
const repoRoot = await findRepoRoot(cwd);
|
|
92
|
+
const state = await readJsonFile(getContextFile(repoRoot), null);
|
|
93
|
+
return { repoRoot, state, umDir: getUmDir(repoRoot) };
|
|
94
|
+
}
|
|
95
|
+
export async function updateRepoContext(updater, cwd = process.cwd()) {
|
|
96
|
+
const { repoRoot, state } = await getRepoContext(cwd);
|
|
97
|
+
if (!state)
|
|
98
|
+
throw new Error("Context is not initialized for this repo. Run umbrella-context setup first.");
|
|
99
|
+
const next = await updater(state);
|
|
100
|
+
await writeJsonFile(getContextFile(repoRoot), next);
|
|
101
|
+
return next;
|
|
102
|
+
}
|
|
103
|
+
export async function addPendingMemory(input, cwd = process.cwd()) {
|
|
104
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
105
|
+
await ensureUmDir(repoRoot);
|
|
106
|
+
const current = await readJsonFile(getPendingMemoriesFile(repoRoot), []);
|
|
107
|
+
const entry = {
|
|
108
|
+
id: randomUUID(),
|
|
109
|
+
createdAt: new Date().toISOString(),
|
|
110
|
+
...input,
|
|
111
|
+
};
|
|
112
|
+
current.push(entry);
|
|
113
|
+
await writeJsonFile(getPendingMemoriesFile(repoRoot), current);
|
|
114
|
+
await updateRepoContext((state) => ({
|
|
115
|
+
...state,
|
|
116
|
+
updatedAt: new Date().toISOString(),
|
|
117
|
+
}), cwd);
|
|
118
|
+
return entry;
|
|
119
|
+
}
|
|
120
|
+
export async function getPendingMemories(cwd = process.cwd()) {
|
|
121
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
122
|
+
return readJsonFile(getPendingMemoriesFile(repoRoot), []);
|
|
123
|
+
}
|
|
124
|
+
export async function clearPendingMemories(cwd = process.cwd()) {
|
|
125
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
126
|
+
await writeJsonFile(getPendingMemoriesFile(repoRoot), []);
|
|
127
|
+
}
|
|
128
|
+
export async function setPulledMemories(memories, cwd = process.cwd()) {
|
|
129
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
130
|
+
await ensureUmDir(repoRoot);
|
|
131
|
+
await writeJsonFile(getPulledMemoriesFile(repoRoot), memories);
|
|
132
|
+
await updateRepoContext((state) => ({
|
|
133
|
+
...state,
|
|
134
|
+
updatedAt: new Date().toISOString(),
|
|
135
|
+
lastPullAt: new Date().toISOString(),
|
|
136
|
+
}), cwd);
|
|
137
|
+
}
|
|
138
|
+
export async function getPulledMemories(cwd = process.cwd()) {
|
|
139
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
140
|
+
return readJsonFile(getPulledMemoriesFile(repoRoot), []);
|
|
141
|
+
}
|
|
142
|
+
export async function setPulledFixes(fixes, cwd = process.cwd()) {
|
|
143
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
144
|
+
await ensureUmDir(repoRoot);
|
|
145
|
+
await writeJsonFile(getPulledFixesFile(repoRoot), fixes);
|
|
146
|
+
await updateRepoContext((state) => ({
|
|
147
|
+
...state,
|
|
148
|
+
updatedAt: new Date().toISOString(),
|
|
149
|
+
lastPullAt: new Date().toISOString(),
|
|
150
|
+
}), cwd);
|
|
151
|
+
}
|
|
152
|
+
export async function getPulledFixes(cwd = process.cwd()) {
|
|
153
|
+
const { repoRoot } = await getRepoContext(cwd);
|
|
154
|
+
return readJsonFile(getPulledFixesFile(repoRoot), []);
|
|
155
|
+
}
|
|
156
|
+
export async function markPushCompleted(cwd = process.cwd()) {
|
|
157
|
+
return updateRepoContext((state) => ({
|
|
158
|
+
...state,
|
|
159
|
+
updatedAt: new Date().toISOString(),
|
|
160
|
+
lastPushAt: new Date().toISOString(),
|
|
161
|
+
}), cwd);
|
|
162
|
+
}
|
|
163
|
+
export function summarizeLocalMemoryMatches(entries, query) {
|
|
164
|
+
const needle = query.trim().toLowerCase();
|
|
165
|
+
if (!needle)
|
|
166
|
+
return [];
|
|
167
|
+
return entries.filter((entry) => {
|
|
168
|
+
const haystack = [entry.content, ...entry.tags].join(" ").toLowerCase();
|
|
169
|
+
return haystack.includes(needle);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
type DeploymentHealth = {
|
|
2
|
+
deploymentMode?: "local_trusted" | "authenticated";
|
|
3
|
+
};
|
|
4
|
+
export type UmbrellaCompany = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
issuePrefix: string;
|
|
8
|
+
};
|
|
9
|
+
export type UmbrellaContextSpace = {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
isPrimary: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type UmbrellaCliSetup = {
|
|
15
|
+
companyId: string;
|
|
16
|
+
companyName: string;
|
|
17
|
+
workspaceId: string;
|
|
18
|
+
workspaceName: string;
|
|
19
|
+
activeSpaceId: string;
|
|
20
|
+
activeSpaceName: string;
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
apiKey: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function getUmbrellaHealth(serverUrl: string): Promise<DeploymentHealth>;
|
|
25
|
+
export declare function signInToUmbrella(serverUrl: string, email: string, password: string): Promise<{
|
|
26
|
+
cookie: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function listUmbrellaCompanies(serverUrl: string, cookie?: string | null): Promise<UmbrellaCompany[]>;
|
|
29
|
+
export declare function createUmbrellaCompany(serverUrl: string, name: string, cookie?: string | null): Promise<UmbrellaCompany>;
|
|
30
|
+
type LearningSummary = {
|
|
31
|
+
workspaceId: string | null;
|
|
32
|
+
workspaceName: string | null;
|
|
33
|
+
activeProjectId: string | null;
|
|
34
|
+
projectName: string | null;
|
|
35
|
+
projects: Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
isPrimary: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
export declare function getCompanyContextSummary(serverUrl: string, companyId: string, cookie?: string | null): Promise<LearningSummary>;
|
|
42
|
+
export declare function createContextSpace(serverUrl: string, companyId: string, name: string, cookie?: string | null): Promise<LearningSummary>;
|
|
43
|
+
export declare function getCliSetup(serverUrl: string, companyId: string, spaceId?: string | null, cookie?: string | null): Promise<UmbrellaCliSetup>;
|
|
44
|
+
export declare function toContextSpaces(summary: LearningSummary): UmbrellaContextSpace[];
|
|
45
|
+
export {};
|
package/dist/umbrella.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
function trimTrailingSlash(value) {
|
|
2
|
+
return value.replace(/\/+$/, "");
|
|
3
|
+
}
|
|
4
|
+
async function requestJson(url, options) {
|
|
5
|
+
const headers = {
|
|
6
|
+
Accept: "application/json",
|
|
7
|
+
};
|
|
8
|
+
if (options?.body !== undefined) {
|
|
9
|
+
headers["Content-Type"] = "application/json";
|
|
10
|
+
}
|
|
11
|
+
if (options?.cookie) {
|
|
12
|
+
headers.Cookie = options.cookie;
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
method: options?.method ?? (options?.body === undefined ? "GET" : "POST"),
|
|
16
|
+
headers,
|
|
17
|
+
body: options?.body === undefined ? undefined : JSON.stringify(options.body),
|
|
18
|
+
});
|
|
19
|
+
const setCookieHeader = response.headers.getSetCookie?.() ?? [];
|
|
20
|
+
const nextCookie = setCookieHeader
|
|
21
|
+
.map((entry) => entry.split(";")[0]?.trim())
|
|
22
|
+
.filter((entry) => Boolean(entry))
|
|
23
|
+
.join("; ") || null;
|
|
24
|
+
const payload = await response.json().catch(() => null);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const message = payload && typeof payload === "object"
|
|
27
|
+
? (() => {
|
|
28
|
+
const record = payload;
|
|
29
|
+
const errorValue = record.error;
|
|
30
|
+
if (typeof errorValue === "string")
|
|
31
|
+
return errorValue;
|
|
32
|
+
if (errorValue && typeof errorValue === "object") {
|
|
33
|
+
const messageValue = errorValue.message;
|
|
34
|
+
if (typeof messageValue === "string")
|
|
35
|
+
return messageValue;
|
|
36
|
+
}
|
|
37
|
+
return `Request failed (${response.status})`;
|
|
38
|
+
})()
|
|
39
|
+
: `Request failed (${response.status})`;
|
|
40
|
+
throw new Error(message);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
data: payload,
|
|
44
|
+
cookie: nextCookie,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function getUmbrellaHealth(serverUrl) {
|
|
48
|
+
const { data } = await requestJson(`${trimTrailingSlash(serverUrl)}/api/health`);
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
export async function signInToUmbrella(serverUrl, email, password) {
|
|
52
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
53
|
+
const { cookie } = await requestJson(`${normalized}/api/auth/sign-in/email`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
body: { email, password },
|
|
56
|
+
});
|
|
57
|
+
const session = await requestJson(`${normalized}/api/auth/get-session`, {
|
|
58
|
+
cookie,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
cookie: session.cookie ?? cookie,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function listUmbrellaCompanies(serverUrl, cookie) {
|
|
65
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
66
|
+
const { data } = await requestJson(`${normalized}/api/companies`, {
|
|
67
|
+
cookie,
|
|
68
|
+
});
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
export async function createUmbrellaCompany(serverUrl, name, cookie) {
|
|
72
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
73
|
+
const { data } = await requestJson(`${normalized}/api/companies`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
cookie,
|
|
76
|
+
body: { name },
|
|
77
|
+
});
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
export async function getCompanyContextSummary(serverUrl, companyId, cookie) {
|
|
81
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
82
|
+
const { data } = await requestJson(`${normalized}/api/companies/${companyId}/learning`, {
|
|
83
|
+
cookie,
|
|
84
|
+
});
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
export async function createContextSpace(serverUrl, companyId, name, cookie) {
|
|
88
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
89
|
+
const { data } = await requestJson(`${normalized}/api/companies/${companyId}/learning/projects`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
cookie,
|
|
92
|
+
body: { name },
|
|
93
|
+
});
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
export async function getCliSetup(serverUrl, companyId, spaceId, cookie) {
|
|
97
|
+
const normalized = trimTrailingSlash(serverUrl);
|
|
98
|
+
const suffix = spaceId ? `?projectId=${encodeURIComponent(spaceId)}` : "";
|
|
99
|
+
const { data } = await requestJson(`${normalized}/api/companies/${companyId}/learning/cli-setup${suffix}`, { cookie });
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
export function toContextSpaces(summary) {
|
|
103
|
+
return summary.projects.map((project) => ({
|
|
104
|
+
id: project.id,
|
|
105
|
+
name: project.name,
|
|
106
|
+
isPrimary: project.isPrimary,
|
|
107
|
+
}));
|
|
108
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "umbrella-context",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Umbrella Context CLI for connecting a device to company context spaces, querying saved context, and syncing MCP access.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"umbrella-context": "./dist/index.js",
|
|
8
|
+
"agent-memory": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"package.json"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
23
|
+
"cac": "^6.7.14",
|
|
24
|
+
"chalk": "^5.4.1",
|
|
25
|
+
"conf": "^13.1.0",
|
|
26
|
+
"prompts": "^2.4.2",
|
|
27
|
+
"zod": "^3.25.76"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.10.5",
|
|
31
|
+
"@types/prompts": "^2.4.9",
|
|
32
|
+
"tsx": "^4.19.2",
|
|
33
|
+
"typescript": "^5.7.3"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/buildingwithai/Umbrella.git",
|
|
38
|
+
"directory": "agent-memory/packages/cli"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/buildingwithai/Umbrella/tree/main/agent-memory/packages/cli"
|
|
41
|
+
}
|