ente-cli 0.1.0 → 0.4.53

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/format.js DELETED
@@ -1,287 +0,0 @@
1
- /**
2
- * Human-readable output formatting.
3
- */
4
- const TZ = "Europe/Berlin";
5
- export function formatDate(ts) {
6
- if (!ts)
7
- return "";
8
- const d = new Date(ts);
9
- return d.toLocaleDateString("de-DE", {
10
- timeZone: TZ,
11
- year: "numeric",
12
- month: "2-digit",
13
- day: "2-digit",
14
- });
15
- }
16
- export function formatDateTime(ts) {
17
- if (!ts)
18
- return "";
19
- const d = new Date(ts);
20
- return d.toLocaleString("de-DE", {
21
- timeZone: TZ,
22
- year: "numeric",
23
- month: "2-digit",
24
- day: "2-digit",
25
- hour: "2-digit",
26
- minute: "2-digit",
27
- });
28
- }
29
- function truncate(s, max) {
30
- if (s.length <= max)
31
- return s;
32
- return s.slice(0, max - 1) + "…";
33
- }
34
- function padEnd(s, len) {
35
- if (s.length >= len)
36
- return s;
37
- return s + " ".repeat(len - s.length);
38
- }
39
- // ---------------------------------------------------------------------------
40
- // Entity list (search results)
41
- // ---------------------------------------------------------------------------
42
- function formatRelativeDate(ts) {
43
- const now = new Date();
44
- const d = new Date(ts);
45
- // Compare in Berlin timezone
46
- const fmt = (date) => date.toLocaleDateString("en-CA", { timeZone: TZ }); // YYYY-MM-DD
47
- const todayStr = fmt(now);
48
- const dateStr = fmt(d);
49
- if (dateStr === todayStr)
50
- return "today";
51
- const yesterday = new Date(now);
52
- yesterday.setDate(yesterday.getDate() - 1);
53
- if (dateStr === fmt(yesterday))
54
- return "yesterday";
55
- // Same year → "Mar 25"; different year → "Dec 2024"
56
- const nowYear = now.toLocaleDateString("en-US", { timeZone: TZ, year: "numeric" });
57
- const dYear = d.toLocaleDateString("en-US", { timeZone: TZ, year: "numeric" });
58
- if (dYear === nowYear) {
59
- return d.toLocaleDateString("en-US", { timeZone: TZ, month: "short", day: "numeric" });
60
- }
61
- return d.toLocaleDateString("en-US", { timeZone: TZ, month: "short", year: "numeric" });
62
- }
63
- export function formatEntityList(entities, spaces) {
64
- if (entities.length === 0)
65
- return "No entities found.";
66
- const rows = entities.map((e) => {
67
- let spaceNames = "";
68
- if (spaces && e.spaceIds.length > 0) {
69
- spaceNames = e.spaceIds
70
- .map((sid) => spaces[sid]?.spaceName ?? sid)
71
- .join(", ");
72
- }
73
- const aliases = e.entity.aliases?.length ? e.entity.aliases : undefined;
74
- const activityCount = e.activityCount ?? 0;
75
- const lastActivityAt = e.lastActivityAt;
76
- return {
77
- name: e.entity.name,
78
- type: `(${e.entity.type})`,
79
- space: spaceNames,
80
- id: e.entity._id,
81
- aliases,
82
- activityCount,
83
- lastActivityAt,
84
- };
85
- });
86
- const nameWidth = Math.max(...rows.map((r) => r.name.length), 4);
87
- const typeWidth = Math.max(...rows.map((r) => r.type.length), 4);
88
- const showSpaces = spaces && rows.some((r) => r.space.length > 0);
89
- const spaceWidth = showSpaces ? Math.max(...rows.map((r) => r.space.length), 5) : 0;
90
- const lines = [];
91
- for (let i = 0; i < rows.length; i++) {
92
- const r = rows[i];
93
- let line = `${padEnd(r.name, nameWidth)} ${padEnd(r.type, typeWidth)}`;
94
- if (showSpaces)
95
- line += ` ${padEnd(r.space, spaceWidth)}`;
96
- line += ` ${r.id}`;
97
- lines.push(line);
98
- // Second line: aliases, activity count, last activity date
99
- const parts = [];
100
- if (r.aliases)
101
- parts.push(`aka: ${r.aliases.join(", ")}`);
102
- if (r.activityCount > 0)
103
- parts.push(`${r.activityCount} activities`);
104
- if (r.lastActivityAt)
105
- parts.push(`last: ${formatRelativeDate(r.lastActivityAt)}`);
106
- if (parts.length > 0) {
107
- lines.push(` ${parts.join(" · ")}`);
108
- }
109
- const openTasks = entities[i].openTasks;
110
- if (openTasks && openTasks.length > 0) {
111
- for (const t of openTasks) {
112
- const label = t.shortId ? `#${t.shortId}` : t.activityId.slice(0, 10);
113
- lines.push(` Open: ${label} ${t.title || "(untitled)"}`);
114
- }
115
- }
116
- }
117
- return lines.join("\n");
118
- }
119
- // ---------------------------------------------------------------------------
120
- // Entity detail
121
- // ---------------------------------------------------------------------------
122
- export function formatEntityDetail(data) {
123
- const e = data.entity;
124
- const lines = [];
125
- lines.push(`${e.name} (${e.type})`);
126
- lines.push(`ID: ${e._id}`);
127
- if (e.aliases && e.aliases.length > 0) {
128
- lines.push(`Aliases: ${e.aliases.join(", ")}`);
129
- }
130
- if (e.tags && e.tags.length > 0) {
131
- lines.push(`Tags: ${e.tags.join(", ")}`);
132
- }
133
- if (e.attributes && Object.keys(e.attributes).length > 0) {
134
- for (const [k, v] of Object.entries(e.attributes)) {
135
- lines.push(` ${k}: ${String(v)}`);
136
- }
137
- }
138
- if (data.descriptionPlainText) {
139
- lines.push("");
140
- lines.push(truncate(data.descriptionPlainText, 200));
141
- }
142
- if (data.pins.length > 0) {
143
- lines.push("");
144
- lines.push("Pins:");
145
- for (const p of data.pins) {
146
- lines.push(` ${p.name} (${p.type})`);
147
- }
148
- }
149
- if (data.openTasks && data.openTasks.length > 0) {
150
- lines.push("");
151
- lines.push("Open tasks:");
152
- for (const t of data.openTasks) {
153
- const label = t.shortId ? `#${t.shortId}` : t.activityId.slice(0, 10);
154
- lines.push(` ${label} ${t.title || "(untitled)"}`);
155
- }
156
- }
157
- if (data.activities.length > 0) {
158
- lines.push("");
159
- lines.push("Recent activities:");
160
- for (const a of data.activities.slice(0, 10)) {
161
- const prefix = a.shortId ? `#${a.shortId}` : a.type;
162
- const title = a.title || truncate(a.contentPlainText || "", 50);
163
- const date = formatDate(a.createdAt);
164
- lines.push(` ${prefix} ${title} ${date}`);
165
- }
166
- }
167
- return lines.join("\n");
168
- }
169
- // ---------------------------------------------------------------------------
170
- // Task list
171
- // ---------------------------------------------------------------------------
172
- export function formatTaskList(tasks, users) {
173
- if (tasks.length === 0)
174
- return "No tasks found.";
175
- const rows = tasks.map((t) => {
176
- const shortId = t.activity.shortId ? `#${t.activity.shortId}` : t.activity._id.slice(0, 8);
177
- const status = t.activity.completed ? "✓" : " ";
178
- const title = t.activity.title || "(untitled)";
179
- const deadline = formatDate(t.activity.deadline);
180
- const entities = t.entities.map((e) => e.name).join(", ");
181
- return { shortId, status, title, deadline, entities };
182
- });
183
- const idWidth = Math.max(...rows.map((r) => r.shortId.length), 2);
184
- const titleWidth = Math.min(Math.max(...rows.map((r) => r.title.length), 5), 50);
185
- return rows
186
- .map((r) => `${padEnd(r.shortId, idWidth)} ${r.status} ${padEnd(truncate(r.title, 50), titleWidth)} ${padEnd(r.deadline, 10)} ${r.entities}`)
187
- .join("\n");
188
- }
189
- // ---------------------------------------------------------------------------
190
- // Task detail
191
- // ---------------------------------------------------------------------------
192
- export function formatTaskDetail(task, users) {
193
- const a = task.activity;
194
- const lines = [];
195
- const shortId = a.shortId ? `#${a.shortId}` : a._id;
196
- const status = a.completed ? "[done]" : "[open]";
197
- lines.push(`${shortId} ${status} ${a.title || "(untitled)"}`);
198
- lines.push(`ID: ${a._id}`);
199
- if (a.deadline) {
200
- lines.push(`Deadline: ${formatDateTime(a.deadline)}`);
201
- }
202
- if (task.entities.length > 0) {
203
- lines.push(`Entities: ${task.entities.map((e) => e.name).join(", ")}`);
204
- }
205
- if (a.assigneeIds && a.assigneeIds.length > 0 && users) {
206
- const names = a.assigneeIds.map((id) => users[id]?.name || users[id]?.email || id);
207
- lines.push(`Assignees: ${names.join(", ")}`);
208
- }
209
- if (a.contentPlainText) {
210
- lines.push("");
211
- lines.push(a.contentPlainText);
212
- }
213
- return lines.join("\n");
214
- }
215
- // ---------------------------------------------------------------------------
216
- // Activity list
217
- // ---------------------------------------------------------------------------
218
- export function formatActivityList(activities) {
219
- if (activities.length === 0)
220
- return "No activities found.";
221
- const rows = activities.map((a) => {
222
- const id = a.shortId ? `#${a.shortId}` : a._id.slice(0, 10);
223
- const type = a.type;
224
- const title = a.title || truncate(a.contentPlainText || "", 40);
225
- const date = formatDate(a.createdAt);
226
- return { id, type, title, date };
227
- });
228
- const idWidth = Math.max(...rows.map((r) => r.id.length), 2);
229
- const typeWidth = Math.max(...rows.map((r) => r.type.length), 4);
230
- const titleWidth = Math.min(Math.max(...rows.map((r) => r.title.length), 5), 50);
231
- return rows
232
- .map((r) => `${padEnd(r.id, idWidth)} ${padEnd(r.type, typeWidth)} ${padEnd(truncate(r.title, 50), titleWidth)} ${r.date}`)
233
- .join("\n");
234
- }
235
- // ---------------------------------------------------------------------------
236
- // Activity detail
237
- // ---------------------------------------------------------------------------
238
- export function formatActivityDetail(activity) {
239
- const a = activity;
240
- const lines = [];
241
- lines.push(`${a.type}: ${a.title || "(untitled)"}`);
242
- lines.push(`ID: ${a._id}`);
243
- lines.push(`Created: ${formatDateTime(a.createdAt)}`);
244
- if (a.deadline) {
245
- lines.push(`Deadline: ${formatDateTime(a.deadline)}`);
246
- }
247
- if (a.completed !== undefined) {
248
- lines.push(`Status: ${a.completed ? "completed" : "open"}`);
249
- }
250
- if (a.attributes && Object.keys(a.attributes).length > 0) {
251
- lines.push("Attributes:");
252
- for (const [k, v] of Object.entries(a.attributes)) {
253
- lines.push(` ${k}: ${String(v)}`);
254
- }
255
- }
256
- if (a.contentPlainText) {
257
- lines.push("");
258
- lines.push(a.contentPlainText);
259
- }
260
- return lines.join("\n");
261
- }
262
- // ---------------------------------------------------------------------------
263
- // Spaces footer
264
- // ---------------------------------------------------------------------------
265
- export function formatSpacesFooter(spaces) {
266
- const entries = Object.entries(spaces);
267
- if (entries.length === 0)
268
- return "";
269
- const lines = ["\nSpaces:"];
270
- for (const [id, s] of entries) {
271
- lines.push(` ${s.spaceName} (${id}) — members: ${s.memberEmails.join(", ")}`);
272
- }
273
- return lines.join("\n");
274
- }
275
- // ---------------------------------------------------------------------------
276
- // JSON output
277
- // ---------------------------------------------------------------------------
278
- export function jsonOutputWithSpaces(data, spaces) {
279
- if (typeof data === "object" && data !== null) {
280
- return JSON.stringify({ ...data, spaces }, null, 2);
281
- }
282
- return JSON.stringify({ data, spaces }, null, 2);
283
- }
284
- export function jsonOutput(data) {
285
- return JSON.stringify(data, null, 2);
286
- }
287
- //# sourceMappingURL=format.js.map
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare function registerGuideCommand(program: Command): void;
@@ -1,27 +0,0 @@
1
- import { TOPICS, TOPIC_NAMES } from "./topics.js";
2
- export function registerGuideCommand(program) {
3
- program
4
- .command("guide [topic]")
5
- .description("Show usage guide. Topics: " + TOPIC_NAMES.join(", "))
6
- .action((topic) => {
7
- if (!topic) {
8
- // Show overview + list of topics
9
- const overview = TOPICS.overview;
10
- console.log(`\n ${overview.title}\n`);
11
- console.log(overview.content);
12
- console.log(`\nTopics:\n${TOPIC_NAMES.filter((t) => t !== "overview")
13
- .map((t) => ` ${t.padEnd(12)} ${TOPICS[t].title}`)
14
- .join("\n")}`);
15
- return;
16
- }
17
- const key = topic.toLowerCase();
18
- const entry = TOPICS[key];
19
- if (!entry) {
20
- console.error(`Unknown topic: '${topic}'. Available: ${TOPIC_NAMES.join(", ")}`);
21
- process.exit(1);
22
- }
23
- console.log(`\n ${entry.title}\n`);
24
- console.log(entry.content);
25
- });
26
- }
27
- //# sourceMappingURL=index.js.map
@@ -1,5 +0,0 @@
1
- export declare const TOPICS: Record<string, {
2
- title: string;
3
- content: string;
4
- }>;
5
- export declare const TOPIC_NAMES: string[];
@@ -1,208 +0,0 @@
1
- export const TOPICS = {
2
- overview: {
3
- title: "ente CLI — Quick Reference",
4
- content: `ente is a shared knowledge graph for people, orgs, projects, topics, and their activities.
5
-
6
- Core concepts:
7
- • Entities (nouns): person, org, project, topic
8
- • Activities (verbs): note, conversation, call, message, task
9
- • Pins: directional links between entities ("Sarah works at Acme")
10
- • Aliases: searchable alternative names on entities
11
-
12
- Every activity belongs to one or more entities. Always link ALL relevant entities.
13
-
14
- Commands:
15
- ente search <query> Fuzzy search entities
16
- ente entity create|get|update|pin|unpin
17
- ente task list|get|create|update|done|assign|unassign
18
- ente activity create|list|get|update|delete
19
- ente guide [topic] This help
20
-
21
- Run \`ente <command> --help\` for detailed usage on any command.
22
- Run \`ente guide <topic>\` for deep dives: entities, tasks, activities, search, patterns`,
23
- },
24
- entities: {
25
- title: "Entities — People, Orgs, Projects, Topics",
26
- content: `Types: person | org | project | topic
27
-
28
- Creating entities:
29
- ente entity create "Jane Doe" --type person --pin "Acme Corp" --alias "JD"
30
-
31
- ⚠ ALWAYS search before creating:
32
- ente search "Jane Doe"
33
- Duplicates are hard to clean up. Even if you think an entity doesn't exist, search first.
34
-
35
- Aliases:
36
- Searchable alternative names. Add them when entities have common abbreviations:
37
- ente entity update "Helmholtz-Zentrum" --alias-add "HZI"
38
-
39
- Proactively add aliases when you notice someone being referred to by a nickname,
40
- abbreviation, or alternate name. This prevents duplicate entity creation.
41
-
42
- Examples of good aliases:
43
- • "TiVA" → alias for "Tierversuchsantrags-Software"
44
- • "Val" → alias for "Valentin Döring"
45
- • "KI" → alias for "AI Strategy" topic
46
-
47
- Pins (relationships):
48
- ente entity pin "Jane Doe" "Acme Corp" # Jane works at Acme
49
-
50
- When creating a new person, immediately pin them to their org + relevant projects.
51
- Don't leave unlinked people.
52
-
53
- Attributes:
54
- Free key-value pairs on entities (email, phone, role, etc.):
55
- ente entity update "Jane" --attr email=jane@acme.com --attr role=CEO
56
-
57
- Attributes are merged on update — existing keys are preserved.`,
58
- },
59
- tasks: {
60
- title: "Tasks — Deadlines, Assignments, Proposals",
61
- content: `Tasks are activities with type "task". They have deadline, completed, assigneeIds.
62
-
63
- Creating tasks:
64
- ente task create "Reply to Ashley" --entity "Ashley" --entity "TiVA" --deadline "tomorrow"
65
- ente task create "Fix bug" --deadline "+3 days" --attr github-issue=https://...
66
-
67
- Every task needs a deadline. If not specified, defaults to tomorrow 22:00 Berlin.
68
-
69
- Deadlines (human-friendly):
70
- "tomorrow" → tomorrow 22:00 Berlin
71
- "tomorrow 14:00" → tomorrow 14:00 Berlin
72
- "today" → today 22:00 Berlin
73
- "+3 days" / "+1 week" → relative, 22:00 Berlin
74
- "2026-03-15" → that date 22:00 Berlin
75
- "2026-03-15 14:00" → that date+time Berlin
76
-
77
- ShortIds:
78
- Tasks get a #N number when they get a deadline. Use it everywhere:
79
- ente task get 5
80
- ente task done 5
81
- ente task update 5 --content "new proposal"
82
-
83
- Task content convention:
84
- When creating tasks from conversations or brain dumps:
85
- • Put context under a "Context" heading
86
- • Do NOT add an "Actions" section — the heartbeat pipeline writes that
87
- • Do NOT set proposal-ready=true — only the heartbeat sets this
88
-
89
- Example:
90
- ente task create "Reply to Louisa" --entity "Louisa" --entity "Preclinics" \\
91
- --deadline "tomorrow" --attr gmail-thread-id=abc123 \\
92
- --content "Context: Louisa reported a crash in Predator when creating a substance."
93
-
94
- Tasks as running lists:
95
- Shopping lists, checklists, idea collections — model these as tasks, not notes.
96
- Why: tasks have shortIds (#N), show in the active working set, and can be
97
- completed when done. "Add X to the shopping list" → find the open task, append.
98
-
99
- Example:
100
- ente task create "Einkaufsliste Foudrais" --entity "Foudrais" --deadline "tomorrow" \\
101
- --content "- Hausschuhe\\n- Wasser\\n- Lebensmittel"
102
-
103
- # Later: append items
104
- ente task update 25 --content "$(ente task get 25 --json | jq -r .content)\\n- Milch"
105
-
106
- # Done shopping:
107
- ente task done 25
108
-
109
- Assignees:
110
- ente task assign 5 <userId> # add assignee (must be org member)
111
- ente task unassign 5 <userId> # remove assignee
112
-
113
- Default: unassigned. In personal orgs, all tasks are implicitly yours.
114
- Use assignees in shared orgs for explicit responsibility.
115
-
116
- Completing:
117
- ente task done 5 # marks completed, frees shortId`,
118
- },
119
- activities: {
120
- title: "Activities — Notes, Conversations, Calls, Messages",
121
- content: `Activity types: note | conversation | call | message | task
122
-
123
- Creating activities:
124
- ente activity create --type conversation --title "Call with Erik" \\
125
- --entity "Erik Stahl" --entity "Preclinics" \\
126
- --content "Discussed QM system. Follow up on export feature."
127
-
128
- Golden rule:
129
- When logging a meeting with a person who works at a company, link BOTH.
130
- If Sarah is pinned to Acme, the activity gets Sarah + Acme as entityIds.
131
-
132
- Think: who was involved? What org? What project? Link them all.
133
-
134
- Listing:
135
- ente activity list --entity "Erik" --hours 168 # last 7 days
136
- ente activity list --limit 20 # most recent
137
-
138
- Title rules:
139
- • Never put a date in the title — activities have timestamps
140
- • Keep titles concise and descriptive
141
-
142
- Content:
143
- Use markdown. "content" is the preferred field.
144
- For activities logging reflections or thoughts, always link to the person's
145
- diary entity + all relevant topic/org/person entities.`,
146
- },
147
- search: {
148
- title: "Search — Fuzzy, Multi-Strategy",
149
- content: `Search is fuzzy and multi-strategy. A single query searches across:
150
-
151
- 1. Name (exact > prefix > Levenshtein fuzzy)
152
- 2. Aliases (same fuzzy matching, slightly lower rank than name)
153
- 3. Attributes (email, phone — use local part only, not full address)
154
- 4. Description text
155
-
156
- Usage:
157
- ente search "Ashley" # fuzzy name search
158
- ente search "Ashley" --type person # filter by type
159
- ente search "HZI" # finds via alias
160
- ente search "lernst" # finds via email attribute
161
-
162
- Tips:
163
- • Typos work: "Scharkowsky" finds "Scharkowski"
164
- • Name stripping: "Haschke" finds "Guido Haschke - Tierschutzbeauftragter"
165
- • For email search, use local part only (e.g. "lernst" not "lernst@ukaachen.de")
166
- • Use --json for full API response with all entity details`,
167
- },
168
- patterns: {
169
- title: "Common Patterns",
170
- content: `Log a meeting:
171
- 1. ente search "person name" # find entity, check pins
172
- 2. ente activity create --type conversation --title "..." \\
173
- --entity "person" --entity "their org" --content "..."
174
- 3. If follow-ups: ente task create "..." --entity "..." --deadline "..."
175
-
176
- Prepare for a meeting:
177
- 1. ente entity get "person name" # see recent activities + pins
178
- 2. ente task list --entity "person" # open tasks related to them
179
- 3. Summarize: recent context + open actions + relationships
180
-
181
- Brain dump → tasks:
182
- 1. For each actionable item:
183
- ente task create "title" --entity "..." --deadline "..." --content "Context: ..."
184
- 2. Don't add Actions section — heartbeat pipeline does that
185
-
186
- Email → task:
187
- ente task create "Re: Subject" --entity "sender" --entity "their org" \\
188
- --deadline "tomorrow" --attr gmail-thread-id=<threadId> \\
189
- --content "Context: sender asked about X. Need to reply with Y."
190
-
191
- Find-or-create entity:
192
- 1. ente search "name"
193
- 2. If not found: ente entity create "name" --type person --pin "org" --alias "nick"
194
- 3. Always search first — duplicates are painful
195
-
196
- Memory split:
197
- ┌─────────────────────────────┬──────────────────────────────┐
198
- │ Belongs in ente │ Belongs in MEMORY.md │
199
- ├─────────────────────────────┼──────────────────────────────┤
200
- │ People, meetings, orgs │ How I (the agent) work │
201
- │ Tasks and follow-ups │ System config, env setup │
202
- │ Project status │ Agent preferences │
203
- │ Relationship history │ Dev environment details │
204
- └─────────────────────────────┴──────────────────────────────┘`,
205
- },
206
- };
207
- export const TOPIC_NAMES = Object.keys(TOPICS);
208
- //# sourceMappingURL=topics.js.map
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/index.js DELETED
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from "commander";
3
- import { EnteClient, getConfig } from "./api.js";
4
- import { registerSearchCommand } from "./commands/search.js";
5
- import { registerEntityCommands } from "./commands/entity.js";
6
- import { registerTaskCommands } from "./commands/task.js";
7
- import { registerActivityCommands, runActivityList } from "./commands/activity.js";
8
- import { registerGuideCommand } from "./guide/index.js";
9
- import { registerAuth } from "./commands/auth.js";
10
- import { registerUpdate, checkForUpdates } from "./commands/update.js";
11
- const program = new Command();
12
- program
13
- .name("ente")
14
- .description("CLI for ente — relationship manager")
15
- .version("0.1.0");
16
- let clientInstance;
17
- function getClient() {
18
- if (!clientInstance) {
19
- clientInstance = new EnteClient(getConfig());
20
- }
21
- return clientInstance;
22
- }
23
- registerAuth(program);
24
- registerUpdate(program);
25
- registerSearchCommand(program, getClient);
26
- registerEntityCommands(program, getClient);
27
- registerTaskCommands(program, getClient);
28
- registerActivityCommands(program, getClient);
29
- registerGuideCommand(program);
30
- // -- Top-level convenience alias: `ente tasks` ----------------------------
31
- program
32
- .command("tasks")
33
- .description("List open tasks sorted by deadline (alias for activity list --type task --only-open --sort deadline)")
34
- .option("--json", "Output raw JSON")
35
- .action(async (opts) => {
36
- const client = getClient();
37
- await runActivityList(client, {
38
- type: "task",
39
- onlyOpen: true,
40
- sort: "deadline",
41
- json: opts.json,
42
- });
43
- });
44
- // Check for updates in background (non-blocking, cached 24h)
45
- // Skip for auth/update commands to avoid noise
46
- const subcommand = process.argv[2];
47
- if (subcommand && !["auth", "update", "--help", "-h", "--version", "-V"].includes(subcommand)) {
48
- checkForUpdates().catch(() => { }); // fire and forget
49
- }
50
- program.parseAsync(process.argv).catch((err) => {
51
- console.error(`Error: ${err.message}`);
52
- process.exit(1);
53
- });
54
- //# sourceMappingURL=index.js.map
package/dist/resolve.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Smart entity resolution: ID pass-through or fuzzy search.
3
- */
4
- import { EnteClient } from "./api.js";
5
- export declare function resolveEntity(client: EnteClient, input: string): Promise<string>;
6
- export declare function resolveEntities(client: EnteClient, inputs: string[]): Promise<string[]>;
7
- /**
8
- * Resolve a space name or ID to a spaceId.
9
- * Searches the spaces map from the entities API.
10
- */
11
- export declare function resolveSpaceByName(client: EnteClient, input: string): Promise<string>;
package/dist/resolve.js DELETED
@@ -1,66 +0,0 @@
1
- /**
2
- * Smart entity resolution: ID pass-through or fuzzy search.
3
- */
4
- /** Convex IDs typically start with a table-hash prefix and are 32+ chars */
5
- function looksLikeConvexId(input) {
6
- return /^[a-z]\w{0,3}[0-9a-z]{28,}$/i.test(input) && input.length >= 20;
7
- }
8
- export async function resolveEntity(client, input) {
9
- if (looksLikeConvexId(input)) {
10
- return input;
11
- }
12
- const res = await client.searchEntities(input, { limit: 10 });
13
- const matches = res.data.entities;
14
- if (matches.length === 0) {
15
- throw new Error(`No entity found matching '${input}'.`);
16
- }
17
- if (matches.length === 1) {
18
- return matches[0].entity._id;
19
- }
20
- // Check for exact name match (case-insensitive)
21
- const exact = matches.filter((m) => m.entity.name.toLowerCase() === input.toLowerCase());
22
- if (exact.length === 1) {
23
- return exact[0].entity._id;
24
- }
25
- const list = matches
26
- .slice(0, 5)
27
- .map((m) => ` ${m.entity.name} (${m.entity.type}) ${m.entity._id}`)
28
- .join("\n");
29
- throw new Error(`Ambiguous match for '${input}':\n${list}\nUse --entity <ID> to be specific.`);
30
- }
31
- export async function resolveEntities(client, inputs) {
32
- return Promise.all(inputs.map((input) => resolveEntity(client, input)));
33
- }
34
- /**
35
- * Resolve a space name or ID to a spaceId.
36
- * Searches the spaces map from the entities API.
37
- */
38
- export async function resolveSpaceByName(client, input) {
39
- if (looksLikeConvexId(input)) {
40
- return input;
41
- }
42
- const res = await client.searchEntities(undefined, { limit: 200 });
43
- const spaces = res.data.spaces;
44
- const lowerInput = input.toLowerCase();
45
- // Exact match first
46
- for (const [spaceId, space] of Object.entries(spaces)) {
47
- if (space.spaceName.toLowerCase() === lowerInput) {
48
- return spaceId;
49
- }
50
- }
51
- // Partial match — collect all and require unambiguous result
52
- const partialMatches = [];
53
- for (const [spaceId, space] of Object.entries(spaces)) {
54
- if (space.spaceName.toLowerCase().includes(lowerInput)) {
55
- partialMatches.push([spaceId, space.spaceName]);
56
- }
57
- }
58
- if (partialMatches.length === 1)
59
- return partialMatches[0][0];
60
- if (partialMatches.length > 1) {
61
- const list = partialMatches.map(([id, name]) => ` ${name} ${id}`).join("\n");
62
- throw new Error(`Ambiguous space match for '${input}':\n${list}\nUse --space <ID> to be specific.`);
63
- }
64
- throw new Error(`No space found matching '${input}'.`);
65
- }
66
- //# sourceMappingURL=resolve.js.map
package/dist/utils.d.ts DELETED
@@ -1,4 +0,0 @@
1
- /**
2
- * Shared utilities.
3
- */
4
- export declare function parseAttrs(pairs?: string[]): Record<string, string>;