Skip to content

Commit 95f8706

Browse files
committed
chore: remove enrichment leftovers after protocol 28.0.0
Drop dead EnrichmentGenerator, enrichment_tool_runs table, and retired user_context MCP/CLI surfaces; update docs to prefill-only enrichment model.
1 parent ca54bb5 commit 95f8706

8 files changed

Lines changed: 39 additions & 197 deletions

File tree

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,7 @@ View user profiles and synchronously enrich your public identity, social, and av
140140
```bash
141141
index profile # Show your own profile
142142
index profile show <user-id> # Show another user's profile
143-
index profile sync # Enrich your profile now and return the resolved identity
144-
index profile search <query> # Search profiles by name or keyword
145-
index profile create # Generate profile from social links
146-
index profile update <action> [--details <text>] # Update profile (action is the verb-phrase, e.g. "add interests")
143+
index profile sync # Run public profile research prefill (does not persist)
147144
```
148145

149146
### `index scrape`

src/api.client.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -443,25 +443,6 @@ export class ApiClient {
443443
return await res.json() as EnrichmentResult;
444444
}
445445

446-
async readUserContexts(
447-
query: { userId?: string; networkId?: string; query?: string } = {},
448-
): Promise<ToolResult> {
449-
return this.callTool("read_user_contexts", query);
450-
}
451-
452-
async createUserContext(query: {
453-
confirm?: boolean;
454-
linkedinUrl?: string;
455-
githubUrl?: string;
456-
twitterUrl?: string;
457-
}): Promise<ToolResult> {
458-
return this.callTool("create_user_context", query);
459-
}
460-
461-
async updateUserContext(query: { action: string; details?: string }): Promise<ToolResult> {
462-
return this.callTool("update_user_context", query);
463-
}
464-
465446
/**
466447
* Invoke a tool by name via the HTTP tool API.
467448
*

src/main.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ function renderHelp(): void {
9090
const formsLefts = [
9191
"index conversation list",
9292
"index sync --json",
93-
"index profile create",
94-
"index profile update",
93+
"index profile sync",
9594
"index intent list",
9695
"index negotiation list",
9796
"index opportunity list",
@@ -150,8 +149,7 @@ function renderHelp(): void {
150149
helpRowCmd(formsLW, "index conversation list", "list your conversations"),
151150
helpRowCmd(formsLW, "index sync --json", "print synced context as JSON"),
152151
"",
153-
helpRowCmd(formsLW, "index profile create", "use social URL flags"),
154-
helpRowCmd(formsLW, "index profile update", "use action text or --details"),
152+
helpRowCmd(formsLW, "index profile sync", "public research prefill"),
155153
helpRowCmd(formsLW, "index intent list", "supports --archived and --limit"),
156154
helpRowCmd(formsLW, "index negotiation list", "supports --since and --limit"),
157155
helpRowCmd(formsLW, "index opportunity list", "supports --status and --limit"),
@@ -165,7 +163,7 @@ function renderHelp(): void {
165163
helpRowCont(cmdLW, "link · unlink · links"),
166164
helpRowCmd(cmdLW, "negotiation", "list · show"),
167165
"",
168-
helpRowCmd(cmdLW, "profile", "show · sync · search · create · update"),
166+
helpRowCmd(cmdLW, "profile", "show · sync"),
169167
helpRowCmd(cmdLW, "conversation", "list · with · show · send · stream"),
170168
helpRowCmd(cmdLW, "network", "list · create · show · update · delete"),
171169
helpRowCont(cmdLW, "join · leave · invite"),
@@ -184,11 +182,7 @@ function renderHelp(): void {
184182
helpRowDim(optLW, "--since <date>", "filter by ISO date or duration"),
185183
helpRowDim(optLW, "--json", "output raw JSON"),
186184
helpRowDim(optLW, "--objective <text>", "objective for scrape command"),
187-
helpRowDim(optLW, "--linkedin <url>", "LinkedIn URL for profile create"),
188-
helpRowDim(optLW, "--github <url>", "GitHub URL for profile create"),
189-
helpRowDim(optLW, "--twitter <url>", "Twitter URL for profile create"),
190185
helpRowDim(optLW, "--title <text>", "title for network update"),
191-
helpRowDim(optLW, "--details <text>", "details for profile update"),
192186
helpRowDim(optLW, "--help", "show help for any command"),
193187
helpRowDim(optLW, "--version", "show version"),
194188
]);

src/output/base.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ export function toolActivity(description: string): void {
103103

104104
/** Human-friendly descriptions for protocol tools (mirrors frontend). */
105105
const TOOL_DESCRIPTIONS: Record<string, string> = {
106-
read_user_contexts: "Reading your profile...",
107-
create_user_context: "Creating your profile...",
108-
update_user_context: "Updating your profile...",
106+
research_profile: "Researching your public profile...",
109107
read_intents: "Fetching your active signals...",
110108
create_intent: "Creating a new signal...",
111109
update_intent: "Updating signal...",

src/profile.command.ts

Lines changed: 11 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,26 @@
11
/**
22
* Profile command handlers for the Index CLI.
33
*
4-
* Implements: (default), show, sync, create, update subcommands.
5-
* Follows the same handleX(client, subcommand, positionals, options)
6-
* pattern as network.command.ts and conversation.command.ts.
4+
* Implements: (default), show, sync subcommands.
75
*/
86

97
import type { ApiClient } from "./api.client";
108
import * as output from "./output";
119

1210
/**
1311
* Route a profile subcommand to the appropriate handler.
14-
*
15-
* @param client - Authenticated API client.
16-
* @param subcommand - The subcommand (show, sync, create, update, or undefined for self).
17-
* @param positionals - Positional arguments after the subcommand.
18-
* @param options - Additional options (json, linkedin, github, twitter).
1912
*/
2013
export async function handleProfile(
2114
client: ApiClient,
2215
subcommand: string | undefined,
2316
positionals: string[],
24-
options: { json?: boolean; linkedin?: string; github?: string; twitter?: string; details?: string } = {},
17+
options: { json?: boolean } = {},
2518
): Promise<void> {
26-
if (subcommand === "create") {
27-
await profileCreate(client, options, options?.json);
28-
return;
29-
}
30-
31-
if (subcommand === "update") {
32-
const action = positionals.join(" ");
33-
if (!action) {
34-
output.error("Usage: index profile update <action> [--details <text>]", 1);
35-
return;
36-
}
37-
await profileUpdate(client, action, options.details, options?.json);
19+
if (subcommand === "create" || subcommand === "update" || subcommand === "search") {
20+
output.error(
21+
`profile ${subcommand} was removed; use "index profile sync" for public research prefill`,
22+
1,
23+
);
3824
return;
3925
}
4026

@@ -53,33 +39,10 @@ export async function handleProfile(
5339
return;
5440
}
5541

56-
if (subcommand === "search") {
57-
const query = positionals.join(" ");
58-
if (!query) { output.error("Usage: index profile search <query>", 1); return; }
59-
const result = await client.readUserContexts({ query });
60-
if (options.json) { console.log(JSON.stringify(result)); return; }
61-
if (!result.success) { output.error(result.error ?? "Search failed", 1); return; }
62-
const data = result.data as { profiles: Array<{ userId: string; name: string; bio?: string; location?: string }> };
63-
output.heading("Search Results");
64-
if (!data.profiles?.length) {
65-
output.dim(" No profiles found.");
66-
} else {
67-
for (const p of data.profiles) {
68-
console.log(` ${p.name} (${p.userId.slice(0, 8)})`);
69-
if (p.bio) output.dim(` ${p.bio.slice(0, 100)}`);
70-
}
71-
}
72-
console.log();
73-
return;
74-
}
75-
7642
// Default: show own profile
7743
await profileMe(client, options?.json);
7844
}
7945

80-
/**
81-
* Show the authenticated user's own profile.
82-
*/
8346
async function profileMe(client: ApiClient, json?: boolean): Promise<void> {
8447
if (!json) {
8548
output.info("Loading your profile...");
@@ -93,9 +56,6 @@ async function profileMe(client: ApiClient, json?: boolean): Promise<void> {
9356
output.profileCard(user);
9457
}
9558

96-
/**
97-
* Show another user's profile by ID.
98-
*/
9959
async function profileShow(client: ApiClient, userId: string, json?: boolean): Promise<void> {
10060
if (!json) {
10161
output.info("Loading profile...");
@@ -108,7 +68,7 @@ async function profileShow(client: ApiClient, userId: string, json?: boolean): P
10868
output.profileCard(user);
10969
}
11070

111-
/** Trigger synchronous profile enrichment for the authenticated user. */
71+
/** Trigger synchronous public profile research for the authenticated user. */
11272
async function profileSync(client: ApiClient, json?: boolean): Promise<void> {
11373
if (!json) output.info("Enriching profile...");
11474
const result = await client.enrichProfile();
@@ -117,64 +77,7 @@ async function profileSync(client: ApiClient, json?: boolean): Promise<void> {
11777
return;
11878
}
11979
output.success("Profile enriched.");
120-
if (result.profile.name) output.dim(` Name: ${result.profile.name}`);
121-
if (result.profile.location) output.dim(` Location: ${result.profile.location}`);
122-
output.dim(` Social links: ${result.profile.socials.length}`);
123-
}
124-
125-
/**
126-
* Create a user profile from social URLs.
127-
*
128-
* @param client - Authenticated API client.
129-
* @param options - Social URL options (linkedin, github, twitter).
130-
*/
131-
async function profileCreate(
132-
client: ApiClient,
133-
options: { linkedin?: string; github?: string; twitter?: string },
134-
json?: boolean,
135-
): Promise<void> {
136-
if (!json) output.info("Creating profile...");
137-
const query: {
138-
confirm?: boolean;
139-
linkedinUrl?: string;
140-
githubUrl?: string;
141-
twitterUrl?: string;
142-
} = { confirm: true };
143-
if (options.linkedin) query.linkedinUrl = options.linkedin;
144-
if (options.github) query.githubUrl = options.github;
145-
if (options.twitter) query.twitterUrl = options.twitter;
146-
147-
const result = await client.createUserContext(query);
148-
if (json) { console.log(JSON.stringify(result)); return; }
149-
if (!result.success) {
150-
output.error(result.error ?? "Profile creation failed", 1);
151-
return;
152-
}
153-
output.success("Profile created.");
154-
}
155-
156-
/**
157-
* Update the user's profile with a natural-language action.
158-
*
159-
* @param client - Authenticated API client.
160-
* @param action - The update action description.
161-
* @param details - Optional additional details.
162-
*/
163-
async function profileUpdate(
164-
client: ApiClient,
165-
action: string,
166-
details?: string,
167-
json?: boolean,
168-
): Promise<void> {
169-
if (!json) output.info("Updating profile...");
170-
const query: { action: string; details?: string } = { action };
171-
if (details) query.details = details;
172-
173-
const result = await client.updateUserContext(query);
174-
if (json) { console.log(JSON.stringify(result)); return; }
175-
if (!result.success) {
176-
output.error(result.error ?? "Profile update failed", 1);
177-
return;
178-
}
179-
output.success("Profile updated.");
80+
if (result.profile?.name) output.dim(` Name: ${result.profile.name}`);
81+
if (result.profile?.location) output.dim(` Location: ${result.profile.location}`);
82+
output.dim(` Social links: ${result.profile?.socials?.length ?? 0}`);
18083
}

src/sync.command.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,23 @@ import * as output from "./output";
1414

1515
/**
1616
* Sync all user context to a local file or stdout.
17-
*
18-
* @param client - Authenticated API client.
19-
* @param options - Options: json outputs to stdout instead of file.
2017
*/
2118
export async function handleSync(
2219
client: ApiClient,
2320
options: { json?: boolean },
2421
): Promise<void> {
2522
if (!options.json) output.info("Syncing context...");
2623

24+
const me = await client.getMe();
2725
const [profile, networks, intents] = await Promise.all([
28-
client.readUserContexts(),
26+
client.getUser(me.id),
2927
client.callTool("read_networks", {}),
3028
client.callTool("read_intents", {}),
3129
]);
3230

3331
const context = {
3432
syncedAt: new Date().toISOString(),
35-
profile: profile.success ? profile.data : null,
33+
profile,
3634
networks: networks.success ? networks.data : null,
3735
intents: intents.success ? intents.data : null,
3836
};

tests/output.base.spec.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,7 @@ describe("heading", () => {
109109

110110
describe("humanizeToolName", () => {
111111
it("returns known tool descriptions", () => {
112-
expect(humanizeToolName("read_user_contexts")).toBe("Reading your profile...");
113-
expect(humanizeToolName("create_user_context")).toBe("Creating your profile...");
114-
expect(humanizeToolName("update_user_context")).toBe("Updating your profile...");
112+
expect(humanizeToolName("research_profile")).toBe("Researching your public profile...");
115113
expect(humanizeToolName("read_intents")).toBe("Fetching your active signals...");
116114
expect(humanizeToolName("create_intent")).toBe("Creating a new signal...");
117115
});

tests/tool-calls.spec.ts

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -96,44 +96,17 @@ describe("CLI tool call contracts", () => {
9696
// ── Profile ──────────────────────────────────────────────────────
9797

9898
describe("profile", () => {
99-
it("search calls read_user_contexts with query", async () => {
100-
mock.setToolResponse("read_user_contexts", {
101-
success: true,
102-
data: { profiles: [], matchCount: 0 },
103-
});
104-
105-
await handleProfile(client, "search", ["Jane Smith"], { json: true });
106-
107-
expect(mock.toolCalls).toHaveLength(1);
108-
expect(mock.toolCalls[0].toolName).toBe("read_user_contexts");
109-
expect(mock.toolCalls[0].query).toEqual({ query: "Jane Smith" });
110-
});
111-
112-
it("create calls create_user_context with confirm and social URLs", async () => {
113-
mock.setToolResponse("create_user_context", { success: true, data: {} });
114-
115-
await handleProfile(client, "create", [], {
116-
linkedin: "https://linkedin.com/in/jane",
117-
github: "https://github.com/jane",
118-
});
119-
120-
expect(mock.toolCalls).toHaveLength(1);
121-
expect(mock.toolCalls[0].toolName).toBe("create_user_context");
122-
expect(mock.toolCalls[0].query).toEqual({
123-
confirm: true,
124-
linkedinUrl: "https://linkedin.com/in/jane",
125-
githubUrl: "https://github.com/jane",
126-
});
127-
});
128-
129-
it("update calls update_user_context with action", async () => {
130-
mock.setToolResponse("update_user_context", { success: true, data: {} });
131-
132-
await handleProfile(client, "update", ["add Python to skills"], { details: "expert level" });
133-
134-
expect(mock.toolCalls).toHaveLength(1);
135-
expect(mock.toolCalls[0].toolName).toBe("update_user_context");
136-
expect(mock.toolCalls[0].query).toEqual({ action: "add Python to skills", details: "expert level" });
99+
it("rejects removed create subcommand", async () => {
100+
let exitCode: number | undefined;
101+
const originalExit = process.exit;
102+
process.exit = ((code?: number) => { exitCode = code; }) as typeof process.exit;
103+
try {
104+
await handleProfile(client, "create", [], { linkedin: "https://linkedin.com/in/jane" } as never);
105+
} finally {
106+
process.exit = originalExit;
107+
}
108+
expect(exitCode).toBe(1);
109+
expect(mock.toolCalls).toHaveLength(0);
137110
});
138111

139112
it("sync uses the synchronous enrichment endpoint", async () => {
@@ -359,20 +332,20 @@ describe("CLI tool call contracts", () => {
359332
// ── Sync ─────────────────────────────────────────────────────────
360333

361334
describe("sync", () => {
362-
it("calls the profile, network, and intent tools in parallel", async () => {
363-
mock.setToolResponse("read_user_contexts", { success: true, data: { profile: {} } });
335+
it("loads profile via REST and tool reads in parallel", async () => {
336+
mock.onRest("GET", "/api/auth/me", () =>
337+
Response.json({ user: { id: "user-1", name: "Test", email: "t@example.com" } }),
338+
);
339+
mock.onRest("GET", "/api/users/user-1", () =>
340+
Response.json({ user: { id: "user-1", name: "Test", intro: "Builder", createdAt: "2026-01-01" } }),
341+
);
364342
mock.setToolResponse("read_networks", { success: true, data: { networks: [] } });
365343
mock.setToolResponse("read_intents", { success: true, data: { intents: [] } });
366344

367345
await handleSync(client, { json: true });
368346

369347
const toolNames = mock.toolCalls.map((c) => c.toolName).sort();
370-
expect(toolNames).toEqual(["read_intents", "read_networks", "read_user_contexts"]);
371-
372-
// All should send empty queries
373-
for (const call of mock.toolCalls) {
374-
expect(call.query).toEqual({});
375-
}
348+
expect(toolNames).toEqual(["read_intents", "read_networks"]);
376349
});
377350
});
378351
});

0 commit comments

Comments
 (0)