@lumoai/cli 1.5.0 → 1.5.1

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.
Files changed (81) hide show
  1. package/assets/skill.md +159 -15
  2. package/dist/cli/src/commands/auth-login.js +4 -3
  3. package/dist/cli/src/commands/auth-logout.js +2 -1
  4. package/dist/cli/src/commands/doc-bind.js +3 -3
  5. package/dist/cli/src/commands/doc-create.js +5 -5
  6. package/dist/cli/src/commands/doc-delete.js +4 -4
  7. package/dist/cli/src/commands/doc-import-gdoc.js +82 -0
  8. package/dist/cli/src/commands/doc-list.js +7 -7
  9. package/dist/cli/src/commands/doc-move.js +8 -8
  10. package/dist/cli/src/commands/doc-share-list.js +11 -8
  11. package/dist/cli/src/commands/doc-share.js +7 -5
  12. package/dist/cli/src/commands/doc-show.js +6 -6
  13. package/dist/cli/src/commands/doc-sync.js +44 -0
  14. package/dist/cli/src/commands/doc-unbind.js +4 -4
  15. package/dist/cli/src/commands/doc-unshare.js +9 -7
  16. package/dist/cli/src/commands/doc-update.js +5 -5
  17. package/dist/cli/src/commands/hook.js +2 -2
  18. package/dist/cli/src/commands/memory-project-add.js +19 -4
  19. package/dist/cli/src/commands/memory-project-list.js +1 -2
  20. package/dist/cli/src/commands/memory-promote.js +3 -3
  21. package/dist/cli/src/commands/memory-rm.js +1 -2
  22. package/dist/cli/src/commands/memory-task-add.js +19 -4
  23. package/dist/cli/src/commands/memory-task-list.js +1 -2
  24. package/dist/cli/src/commands/milestone-create.js +4 -4
  25. package/dist/cli/src/commands/milestone-delete.js +5 -5
  26. package/dist/cli/src/commands/milestone-list.js +3 -3
  27. package/dist/cli/src/commands/milestone-show.js +5 -5
  28. package/dist/cli/src/commands/milestone-update.js +6 -5
  29. package/dist/cli/src/commands/project-list.js +3 -3
  30. package/dist/cli/src/commands/session-attach.js +5 -5
  31. package/dist/cli/src/commands/session-detach.js +3 -3
  32. package/dist/cli/src/commands/session-status.js +3 -3
  33. package/dist/cli/src/commands/setup.js +33 -7
  34. package/dist/cli/src/commands/sprint-add.js +3 -3
  35. package/dist/cli/src/commands/sprint-close.js +5 -5
  36. package/dist/cli/src/commands/sprint-create.js +4 -4
  37. package/dist/cli/src/commands/sprint-delete.js +5 -5
  38. package/dist/cli/src/commands/sprint-list.js +3 -3
  39. package/dist/cli/src/commands/sprint-remove.js +3 -3
  40. package/dist/cli/src/commands/sprint-show.js +4 -4
  41. package/dist/cli/src/commands/sprint-start.js +4 -4
  42. package/dist/cli/src/commands/sprint-summary.js +7 -7
  43. package/dist/cli/src/commands/sprint-update.js +6 -5
  44. package/dist/cli/src/commands/task-artifact-add.js +17 -5
  45. package/dist/cli/src/commands/task-artifact-list.js +4 -4
  46. package/dist/cli/src/commands/task-artifact-rm.js +4 -4
  47. package/dist/cli/src/commands/task-artifact-show.js +8 -8
  48. package/dist/cli/src/commands/task-artifact-update.js +5 -5
  49. package/dist/cli/src/commands/task-comment-list.js +111 -0
  50. package/dist/cli/src/commands/task-comment.js +3 -3
  51. package/dist/cli/src/commands/task-context.js +24 -12
  52. package/dist/cli/src/commands/task-create.js +7 -7
  53. package/dist/cli/src/commands/task-figma-add.js +3 -2
  54. package/dist/cli/src/commands/task-figma-context.js +61 -0
  55. package/dist/cli/src/commands/task-figma-list.js +3 -2
  56. package/dist/cli/src/commands/task-figma-refresh.js +4 -3
  57. package/dist/cli/src/commands/task-figma-rm.js +3 -2
  58. package/dist/cli/src/commands/task-list.js +1 -2
  59. package/dist/cli/src/commands/task-pr-show.js +66 -0
  60. package/dist/cli/src/commands/task-show.js +8 -7
  61. package/dist/cli/src/commands/task-slack-show.js +59 -0
  62. package/dist/cli/src/commands/task-update.js +7 -7
  63. package/dist/cli/src/commands/task-web-show.js +64 -0
  64. package/dist/cli/src/commands/whoami.js +4 -3
  65. package/dist/cli/src/index.js +167 -102
  66. package/dist/cli/src/lib/agent.js +10 -1
  67. package/dist/cli/src/lib/api.js +81 -1
  68. package/dist/cli/src/lib/config.js +2 -1
  69. package/dist/cli/src/lib/doc-input.js +12 -1
  70. package/dist/cli/src/lib/figma-api.js +1 -1
  71. package/dist/cli/src/lib/format.js +3 -2
  72. package/dist/cli/src/lib/hook-runner.js +26 -10
  73. package/dist/cli/src/lib/hooks-template.js +52 -7
  74. package/dist/cli/src/lib/memory-content.js +4 -3
  75. package/dist/cli/src/lib/path-guard.js +125 -0
  76. package/dist/cli/src/lib/resolve-doc-id.js +2 -1
  77. package/dist/cli/src/lib/resolve-member.js +2 -1
  78. package/dist/cli/src/lib/sanitize.js +17 -0
  79. package/dist/cli/src/lib/tag-resolver.js +2 -1
  80. package/dist/cli/src/lib/update-check.js +2 -2
  81. package/package.json +1 -1
@@ -6,6 +6,7 @@ const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const resolve_doc_id_1 = require("../lib/resolve-doc-id");
8
8
  const resolve_member_1 = require("../lib/resolve-member");
9
+ const sanitize_1 = require("../lib/sanitize");
9
10
  const ALLOWED_ROLES = ['VIEWER', 'EDITOR', 'MANAGER'];
10
11
  function normalizeRole(value) {
11
12
  if (!value)
@@ -34,8 +35,7 @@ async function docShare(docRef, memberRef, opts) {
34
35
  console.error('Error: not logged in. Run `lumo auth login` first.');
35
36
  return 1;
36
37
  }
37
- const envUrl = process.env.LUMO_API_URL?.trim();
38
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
38
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
39
39
  const docId = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, docRef);
40
40
  if (!docId) {
41
41
  console.error(`Error: Document not found: ${docRef}`);
@@ -55,7 +55,9 @@ async function docShare(docRef, memberRef, opts) {
55
55
  return 1;
56
56
  }
57
57
  if (result.kind === 'ambiguous') {
58
- const list = result.candidates.map(c => c.email ?? c.displayName).join(', ');
58
+ const list = result.candidates
59
+ .map(c => (0, sanitize_1.sanitizeField)(c.email ?? c.displayName))
60
+ .join(', ');
59
61
  console.error(`Error: multiple members match "${result.query}": ${list}`);
60
62
  return 1;
61
63
  }
@@ -70,8 +72,8 @@ async function docShare(docRef, memberRef, opts) {
70
72
  });
71
73
  if (!res.ok) {
72
74
  const text = await res.text();
73
- console.error(`Error: ${res.status} ${res.statusText}: ${text}`);
75
+ console.error(`Error: ${res.status} ${res.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
74
76
  return 1;
75
77
  }
76
- console.log(`Shared ${docId} ↔ ${result.member.displayName} (${role})`);
78
+ console.log(`Shared ${docId} ↔ ${(0, sanitize_1.sanitizeField)(result.member.displayName)} (${role})`);
77
79
  }
@@ -6,6 +6,7 @@ const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const markdown_tiptap_1 = require("../lib/markdown-tiptap");
8
8
  const resolve_doc_id_1 = require("../lib/resolve-doc-id");
9
+ const sanitize_1 = require("../lib/sanitize");
9
10
  function scopeLabel(s) {
10
11
  if (s === 'PRIVATE')
11
12
  return 'personal';
@@ -16,15 +17,15 @@ function scopeLabel(s) {
16
17
  function formatShowOutput(vm) {
17
18
  const lines = [
18
19
  `ID: ${vm.id}`,
19
- `Title: ${vm.title}`,
20
+ `Title: ${(0, sanitize_1.sanitizeField)(vm.title)}`,
20
21
  `Scope: ${scopeLabel(vm.scope)}`,
21
- `Project: ${vm.projectName ?? '-'}`,
22
+ `Project: ${vm.projectName ? (0, sanitize_1.sanitizeField)(vm.projectName) : '-'}`,
22
23
  `Created: ${vm.createdAt}`,
23
24
  `Updated: ${vm.updatedAt}`,
24
25
  `Mentioned tasks: ${vm.mentionedTasks.length ? vm.mentionedTasks.join(', ') : '-'}`,
25
26
  ];
26
27
  const header = lines.join('\n');
27
- return vm.bodyMarkdown ? `${header}\n\n${vm.bodyMarkdown}` : header;
28
+ return vm.bodyMarkdown ? `${header}\n\n${(0, sanitize_1.sanitizeField)(vm.bodyMarkdown)}` : header;
28
29
  }
29
30
  async function docShow(reference) {
30
31
  if (!reference) {
@@ -36,8 +37,7 @@ async function docShow(reference) {
36
37
  console.error('Error: not logged in. Run `lumo auth login` first.');
37
38
  return 1;
38
39
  }
39
- const envUrl = process.env.LUMO_API_URL?.trim();
40
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
40
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
41
41
  const id = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, reference);
42
42
  if (!id) {
43
43
  console.error(`Error: Document not found: ${reference}`);
@@ -48,7 +48,7 @@ async function docShow(reference) {
48
48
  });
49
49
  if (!res.ok) {
50
50
  const text = await res.text();
51
- console.error(`Error: ${res.status} ${res.statusText}: ${text}`);
51
+ console.error(`Error: ${res.status} ${res.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
52
52
  return 1;
53
53
  }
54
54
  // The exact shape of the GET response is not guaranteed; defensively unwrap.
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSyncedLine = formatSyncedLine;
4
+ exports.docSync = docSync;
5
+ const config_1 = require("../lib/config");
6
+ const api_1 = require("../lib/api");
7
+ const resolve_doc_id_1 = require("../lib/resolve-doc-id");
8
+ const sanitize_1 = require("../lib/sanitize");
9
+ function formatSyncedLine(doc) {
10
+ const escaped = (0, sanitize_1.sanitizeField)(doc.title).replace(/"/g, '\\"');
11
+ return `Synced ${doc.id} "${escaped}" from Google`;
12
+ }
13
+ async function docSync(reference) {
14
+ const creds = (0, config_1.readCredentials)();
15
+ if (!creds) {
16
+ console.error('Error: not logged in. Run `lumo auth login` first.');
17
+ return 1;
18
+ }
19
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
20
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
21
+ const docId = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, reference);
22
+ if (!docId) {
23
+ console.error(`Error: doc not found: ${reference}`);
24
+ return 1;
25
+ }
26
+ let res;
27
+ try {
28
+ res = await fetch(`${base}/api/documents/${docId}/sync-google`, {
29
+ method: 'POST',
30
+ headers: { Authorization: `Bearer ${creds.token}` },
31
+ });
32
+ }
33
+ catch (err) {
34
+ console.error(`Error: network failure: ${err.message}`);
35
+ return 1;
36
+ }
37
+ if (!res.ok) {
38
+ const text = await res.text();
39
+ console.error(`Error: ${res.status} ${res.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
40
+ return 1;
41
+ }
42
+ const { document } = (await res.json());
43
+ console.log(formatSyncedLine(document));
44
+ }
@@ -4,6 +4,7 @@ exports.docUnbind = docUnbind;
4
4
  const config_1 = require("../lib/config");
5
5
  const api_1 = require("../lib/api");
6
6
  const resolve_doc_id_1 = require("../lib/resolve-doc-id");
7
+ const sanitize_1 = require("../lib/sanitize");
7
8
  async function docUnbind(docRef, task) {
8
9
  if (!docRef || !task) {
9
10
  console.error('Error: usage: lumo doc unbind <doc> <task>');
@@ -14,8 +15,7 @@ async function docUnbind(docRef, task) {
14
15
  console.error('Error: not logged in. Run `lumo auth login` first.');
15
16
  return 1;
16
17
  }
17
- const envUrl = process.env.LUMO_API_URL?.trim();
18
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
18
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
19
19
  const docId = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, docRef);
20
20
  if (!docId) {
21
21
  console.error(`Error: Document not found: ${docRef}`);
@@ -29,12 +29,12 @@ async function docUnbind(docRef, task) {
29
29
  });
30
30
  if (res.status === 409) {
31
31
  const { error } = (await res.json());
32
- console.error(`Error: ${error}`);
32
+ console.error(`Error: ${(0, sanitize_1.sanitizeField)(error)}`);
33
33
  return 1;
34
34
  }
35
35
  if (!res.ok) {
36
36
  const text = await res.text();
37
- console.error(`Error: ${res.status} ${res.statusText}: ${text}`);
37
+ console.error(`Error: ${res.status} ${res.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
38
38
  return 1;
39
39
  }
40
40
  const data = (await res.json());
@@ -5,6 +5,7 @@ const config_1 = require("../lib/config");
5
5
  const api_1 = require("../lib/api");
6
6
  const resolve_doc_id_1 = require("../lib/resolve-doc-id");
7
7
  const resolve_member_1 = require("../lib/resolve-member");
8
+ const sanitize_1 = require("../lib/sanitize");
8
9
  async function docUnshare(docRef, memberRef) {
9
10
  if (!docRef || !memberRef) {
10
11
  console.error('Error: usage: lumo doc unshare <doc> <member>');
@@ -15,8 +16,7 @@ async function docUnshare(docRef, memberRef) {
15
16
  console.error('Error: not logged in. Run `lumo auth login` first.');
16
17
  return 1;
17
18
  }
18
- const envUrl = process.env.LUMO_API_URL?.trim();
19
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
19
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
20
20
  const docId = await (0, resolve_doc_id_1.lookupDocId)(apiUrl, creds.token, docRef);
21
21
  if (!docId) {
22
22
  console.error(`Error: Document not found: ${docRef}`);
@@ -36,7 +36,9 @@ async function docUnshare(docRef, memberRef) {
36
36
  return 1;
37
37
  }
38
38
  if (result.kind === 'ambiguous') {
39
- const list = result.candidates.map(c => c.email ?? c.displayName).join(', ');
39
+ const list = result.candidates
40
+ .map(c => (0, sanitize_1.sanitizeField)(c.email ?? c.displayName))
41
+ .join(', ');
40
42
  console.error(`Error: multiple members match "${result.query}": ${list}`);
41
43
  return 1;
42
44
  }
@@ -48,13 +50,13 @@ async function docUnshare(docRef, memberRef) {
48
50
  });
49
51
  if (!listRes.ok) {
50
52
  const text = await listRes.text();
51
- console.error(`Error: ${listRes.status} ${listRes.statusText}: ${text}`);
53
+ console.error(`Error: ${listRes.status} ${listRes.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
52
54
  return 1;
53
55
  }
54
56
  const { shares } = (await listRes.json());
55
57
  const row = shares.find(s => s.member.id === result.member.memberId);
56
58
  if (!row) {
57
- console.log(`Not shared with ${result.member.displayName}`);
59
+ console.log(`Not shared with ${(0, sanitize_1.sanitizeField)(result.member.displayName)}`);
58
60
  return;
59
61
  }
60
62
  const delUrl = `${(0, api_1.trimTrailingSlash)(apiUrl)}/api/documents/${docId}/shares/${row.id}`;
@@ -64,8 +66,8 @@ async function docUnshare(docRef, memberRef) {
64
66
  });
65
67
  if (!delRes.ok) {
66
68
  const text = await delRes.text();
67
- console.error(`Error: ${delRes.status} ${delRes.statusText}: ${text}`);
69
+ console.error(`Error: ${delRes.status} ${delRes.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
68
70
  return 1;
69
71
  }
70
- console.log(`Unshared ${docId} ↔ ${result.member.displayName}`);
72
+ console.log(`Unshared ${docId} ↔ ${(0, sanitize_1.sanitizeField)(result.member.displayName)}`);
71
73
  }
@@ -8,11 +8,12 @@ const doc_input_1 = require("../lib/doc-input");
8
8
  const doc_create_1 = require("./doc-create");
9
9
  const resolve_doc_id_1 = require("../lib/resolve-doc-id");
10
10
  const tag_resolver_1 = require("../lib/tag-resolver");
11
+ const sanitize_1 = require("../lib/sanitize");
11
12
  function formatUpdatedDocLine(doc) {
12
- const escaped = doc.title.replace(/"/g, '\\"');
13
+ const escaped = (0, sanitize_1.sanitizeField)(doc.title).replace(/"/g, '\\"');
13
14
  const head = `Updated ${doc.id} "${escaped}" ${doc.url}`;
14
15
  if (doc.tags && doc.tags.length > 0) {
15
- return `${head}\nTags: ${doc.tags.join(', ')}`;
16
+ return `${head}\nTags: ${doc.tags.map(sanitize_1.sanitizeField).join(', ')}`;
16
17
  }
17
18
  return head;
18
19
  }
@@ -26,8 +27,7 @@ async function docUpdate(reference, opts) {
26
27
  console.error('Error: not logged in. Run `lumo auth login` first.');
27
28
  return 1;
28
29
  }
29
- const envUrl = process.env.LUMO_API_URL?.trim();
30
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
30
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
31
31
  // CLI-side mutex check: --tag/--tag-id (bulk replace) vs --add-tag/--add-tag-id/--remove-tag/--remove-tag-id (incremental)
32
32
  const hasBulk = (opts.tag && opts.tag.length > 0) || (opts.tagId && opts.tagId.length > 0);
33
33
  const hasIncremental = (opts.addTag && opts.addTag.length > 0) ||
@@ -122,7 +122,7 @@ async function docUpdate(reference, opts) {
122
122
  });
123
123
  if (!res.ok) {
124
124
  const text = await res.text();
125
- console.error(`Error: ${res.status} ${res.statusText}: ${text}`);
125
+ console.error(`Error: ${res.status} ${res.statusText}: ${(0, sanitize_1.sanitizeField)(text)}`);
126
126
  return 1;
127
127
  }
128
128
  const { document } = (await res.json());
@@ -11,9 +11,9 @@ const hook_log_1 = require("../lib/hook-log");
11
11
  * if `runHook` someday throws (it currently never does), Claude Code is
12
12
  * not disrupted.
13
13
  */
14
- async function hookCommand(path) {
14
+ async function hookCommand(path, agentToken) {
15
15
  try {
16
- await (0, hook_runner_1.runHook)(path);
16
+ await (0, hook_runner_1.runHook)(path, agentToken);
17
17
  }
18
18
  catch (err) {
19
19
  (0, hook_log_1.logHookError)(`[${path}] dispatcher`, err);
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.memoryProjectAdd = memoryProjectAdd;
4
4
  const config_1 = require("../lib/config");
5
5
  const api_1 = require("../lib/api");
6
+ const sanitize_1 = require("../lib/sanitize");
6
7
  const memory_content_1 = require("../lib/memory-content");
7
8
  const resolve_1 = require("../lib/resolve");
8
9
  const resolve_bound_task_1 = require("../lib/resolve-bound-task");
9
10
  const resolve_project_1 = require("../lib/resolve-project");
11
+ const agent_1 = require("../lib/agent");
10
12
  async function memoryProjectAdd(refArg, options) {
11
13
  if (!options.category) {
12
14
  console.error('Error: --category <trap|decision|convention|procedural> is required.');
@@ -17,13 +19,22 @@ async function memoryProjectAdd(refArg, options) {
17
19
  console.error(`Error: ${built.error}`);
18
20
  return 1;
19
21
  }
22
+ let agent;
23
+ const agentRaw = options.agent?.trim();
24
+ if (agentRaw) {
25
+ const normalized = (0, agent_1.normalizeAgent)(agentRaw);
26
+ if (!normalized) {
27
+ console.error(`Error: invalid --agent "${agentRaw}". Valid values: ${agent_1.VALID_AGENT_TOKENS.join(', ')}.`);
28
+ return 1;
29
+ }
30
+ agent = normalized;
31
+ }
20
32
  const creds = (0, config_1.readCredentials)();
21
33
  if (!creds) {
22
34
  console.error('Error: not logged in. Run `lumo auth login` first.');
23
35
  return 1;
24
36
  }
25
- const envUrl = process.env.LUMO_API_URL?.trim();
26
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
37
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
27
38
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
28
39
  let projectId;
29
40
  let echoSuffix = '';
@@ -49,7 +60,11 @@ async function memoryProjectAdd(refArg, options) {
49
60
  res = await fetch(`${base}/api/projects/${encodeURIComponent(projectId)}/memories`, {
50
61
  method: 'POST',
51
62
  headers: { Authorization: `Bearer ${creds.token}`, 'Content-Type': 'application/json' },
52
- body: JSON.stringify({ category: built.category, content: built.content }),
63
+ body: JSON.stringify({
64
+ category: built.category,
65
+ content: built.content,
66
+ ...(agent ? { agent } : {}),
67
+ }),
53
68
  });
54
69
  }
55
70
  catch (err) {
@@ -64,7 +79,7 @@ async function memoryProjectAdd(refArg, options) {
64
79
  m = b.error;
65
80
  }
66
81
  catch { /* */ }
67
- console.error(m ? `Error: ${m}` : `Error: memory add failed (HTTP ${res.status})`);
82
+ console.error(m ? `Error: ${(0, sanitize_1.sanitizeField)(m)}` : `Error: memory add failed (HTTP ${res.status})`);
68
83
  return 1;
69
84
  }
70
85
  process.stdout.write(`Added ${built.category} PROJECT memory${echoSuffix}\n`);
@@ -18,8 +18,7 @@ async function memoryProjectList(refArg, options) {
18
18
  console.error('Error: not logged in. Run `lumo auth login` first.');
19
19
  return 1;
20
20
  }
21
- const envUrl = process.env.LUMO_API_URL?.trim();
22
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
21
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
23
22
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
24
23
  let projectId;
25
24
  if (refArg) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.memoryPromote = memoryPromote;
4
4
  const config_1 = require("../lib/config");
5
5
  const api_1 = require("../lib/api");
6
+ const sanitize_1 = require("../lib/sanitize");
6
7
  async function memoryPromote(memoryId) {
7
8
  if (!memoryId) {
8
9
  console.error('Error: missing <memoryId>. Usage: lumo memory promote <memoryId>');
@@ -13,8 +14,7 @@ async function memoryPromote(memoryId) {
13
14
  console.error('Error: not logged in. Run `lumo auth login` first.');
14
15
  return 1;
15
16
  }
16
- const envUrl = process.env.LUMO_API_URL?.trim();
17
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
17
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
18
18
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
19
19
  let res;
20
20
  try {
@@ -44,7 +44,7 @@ async function memoryPromote(memoryId) {
44
44
  m = b.error;
45
45
  }
46
46
  catch { /* */ }
47
- console.error(m ? `Error: ${m}` : `Error: promote failed (HTTP ${res.status})`);
47
+ console.error(m ? `Error: ${(0, sanitize_1.sanitizeField)(m)}` : `Error: promote failed (HTTP ${res.status})`);
48
48
  return 1;
49
49
  }
50
50
  process.stdout.write(`Promoted ${memoryId} to PROJECT — every agent on this project now sees it.\n` +
@@ -17,8 +17,7 @@ async function memoryRm(memoryId, options) {
17
17
  console.error('Error: not logged in. Run `lumo auth login` first.');
18
18
  return 1;
19
19
  }
20
- const envUrl = process.env.LUMO_API_URL?.trim();
21
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
20
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
22
21
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
23
22
  let res;
24
23
  try {
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.memoryTaskAdd = memoryTaskAdd;
4
4
  const config_1 = require("../lib/config");
5
5
  const api_1 = require("../lib/api");
6
+ const sanitize_1 = require("../lib/sanitize");
6
7
  const memory_content_1 = require("../lib/memory-content");
7
8
  const resolve_bound_task_1 = require("../lib/resolve-bound-task");
9
+ const agent_1 = require("../lib/agent");
8
10
  async function memoryTaskAdd(identifierArg, options) {
9
11
  if (!options.category) {
10
12
  console.error('Error: --category <trap|decision|convention|procedural> is required.');
@@ -15,13 +17,22 @@ async function memoryTaskAdd(identifierArg, options) {
15
17
  console.error(`Error: ${built.error}`);
16
18
  return 1;
17
19
  }
20
+ let agent;
21
+ const agentRaw = options.agent?.trim();
22
+ if (agentRaw) {
23
+ const normalized = (0, agent_1.normalizeAgent)(agentRaw);
24
+ if (!normalized) {
25
+ console.error(`Error: invalid --agent "${agentRaw}". Valid values: ${agent_1.VALID_AGENT_TOKENS.join(', ')}.`);
26
+ return 1;
27
+ }
28
+ agent = normalized;
29
+ }
18
30
  const creds = (0, config_1.readCredentials)();
19
31
  if (!creds) {
20
32
  console.error('Error: not logged in. Run `lumo auth login` first.');
21
33
  return 1;
22
34
  }
23
- const envUrl = process.env.LUMO_API_URL?.trim();
24
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
35
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
25
36
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
26
37
  // Resolve the target: explicit arg > session-bound task.
27
38
  const identifier = identifierArg ?? (await (0, resolve_bound_task_1.resolveBoundTaskIdentifier)(apiUrl, creds.token));
@@ -60,7 +71,11 @@ async function memoryTaskAdd(identifierArg, options) {
60
71
  res = await fetch(`${base}/api/tasks/${encodeURIComponent(taskId)}/memories`, {
61
72
  method: 'POST',
62
73
  headers: { Authorization: `Bearer ${creds.token}`, 'Content-Type': 'application/json' },
63
- body: JSON.stringify({ category: built.category, content: built.content }),
74
+ body: JSON.stringify({
75
+ category: built.category,
76
+ content: built.content,
77
+ ...(agent ? { agent } : {}),
78
+ }),
64
79
  });
65
80
  }
66
81
  catch (err) {
@@ -75,7 +90,7 @@ async function memoryTaskAdd(identifierArg, options) {
75
90
  m = b.error;
76
91
  }
77
92
  catch { /* */ }
78
- console.error(m ? `Error: ${m}` : `Error: memory add failed (HTTP ${res.status})`);
93
+ console.error(m ? `Error: ${(0, sanitize_1.sanitizeField)(m)}` : `Error: memory add failed (HTTP ${res.status})`);
79
94
  return 1;
80
95
  }
81
96
  process.stdout.write(`Added ${built.category} memory to ${identifier}` +
@@ -16,8 +16,7 @@ async function memoryTaskList(identifierArg, options) {
16
16
  console.error('Error: not logged in. Run `lumo auth login` first.');
17
17
  return 1;
18
18
  }
19
- const envUrl = process.env.LUMO_API_URL?.trim();
20
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
19
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
21
20
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
22
21
  const identifier = identifierArg ?? (await (0, resolve_bound_task_1.resolveBoundTaskIdentifier)(apiUrl, creds.token));
23
22
  if (!identifier) {
@@ -5,6 +5,7 @@ exports.formatCreatedMilestoneLine = formatCreatedMilestoneLine;
5
5
  exports.milestoneCreate = milestoneCreate;
6
6
  const config_1 = require("../lib/config");
7
7
  const api_1 = require("../lib/api");
8
+ const sanitize_1 = require("../lib/sanitize");
8
9
  const resolve_1 = require("../lib/resolve");
9
10
  function buildCreatePayload(name, opts) {
10
11
  const payload = { name };
@@ -17,7 +18,7 @@ function buildCreatePayload(name, opts) {
17
18
  return payload;
18
19
  }
19
20
  function formatCreatedMilestoneLine(milestone) {
20
- return `Created milestone "${milestone.name}" ${milestone.id}`;
21
+ return `Created milestone "${(0, sanitize_1.sanitizeField)(milestone.name)}" ${milestone.id}`;
21
22
  }
22
23
  async function milestoneCreate(name, opts) {
23
24
  if (!name || !name.trim()) {
@@ -29,8 +30,7 @@ async function milestoneCreate(name, opts) {
29
30
  console.error('Error: not logged in. Run `lumo auth login` first.');
30
31
  return 1;
31
32
  }
32
- const envUrl = process.env.LUMO_API_URL?.trim();
33
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
33
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
34
34
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
35
35
  let projectId;
36
36
  try {
@@ -72,7 +72,7 @@ async function milestoneCreate(name, opts) {
72
72
  // Body wasn't JSON; fall through to status-only message
73
73
  }
74
74
  if (serverMsg) {
75
- console.error(`Error: ${serverMsg}`);
75
+ console.error(`Error: ${(0, sanitize_1.sanitizeField)(serverMsg)}`);
76
76
  }
77
77
  else {
78
78
  console.error(`Error: milestone create failed (HTTP ${res.status})`);
@@ -5,8 +5,9 @@ exports.milestoneDelete = milestoneDelete;
5
5
  const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const resolve_1 = require("../lib/resolve");
8
+ const sanitize_1 = require("../lib/sanitize");
8
9
  function formatDeleteRefusal(name, taskCount) {
9
- const head = `Refusing to delete milestone "${name}" without --yes.`;
10
+ const head = `Refusing to delete milestone "${(0, sanitize_1.sanitizeField)(name)}" without --yes.`;
10
11
  const tail = `Re-run with --yes to confirm.`;
11
12
  if (taskCount === 0)
12
13
  return `${head}\n${tail}`;
@@ -21,8 +22,7 @@ async function milestoneDelete(identifier, opts) {
21
22
  console.error('Error: not logged in. Run `lumo auth login` first.');
22
23
  return 1;
23
24
  }
24
- const envUrl = process.env.LUMO_API_URL?.trim();
25
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
25
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
26
26
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
27
27
  let milestoneId;
28
28
  let resolvedName;
@@ -89,8 +89,8 @@ async function milestoneDelete(identifier, opts) {
89
89
  catch {
90
90
  // body wasn't JSON
91
91
  }
92
- console.error(`Error: ${errMsg}`);
92
+ console.error(`Error: ${(0, sanitize_1.sanitizeField)(errMsg)}`);
93
93
  return 1;
94
94
  }
95
- process.stdout.write(`Deleted milestone "${name}"\n`);
95
+ process.stdout.write(`Deleted milestone "${(0, sanitize_1.sanitizeField)(name)}"\n`);
96
96
  }
@@ -5,6 +5,7 @@ exports.milestoneList = milestoneList;
5
5
  const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const resolve_1 = require("../lib/resolve");
8
+ const sanitize_1 = require("../lib/sanitize");
8
9
  function formatDate(iso) {
9
10
  if (!iso)
10
11
  return '-';
@@ -22,7 +23,7 @@ function formatMilestoneList(rows) {
22
23
  const statusW = Math.max(...rows.map(r => r.status.length));
23
24
  const dateW = Math.max(...rows.map(r => formatDate(r.targetDate).length));
24
25
  return rows
25
- .map(r => `${r.status.padEnd(statusW)} ${formatDate(r.targetDate).padEnd(dateW)} ${r.name}`)
26
+ .map(r => `${r.status.padEnd(statusW)} ${formatDate(r.targetDate).padEnd(dateW)} ${(0, sanitize_1.sanitizeField)(r.name)}`)
26
27
  .join('\n');
27
28
  }
28
29
  async function milestoneList(options) {
@@ -31,8 +32,7 @@ async function milestoneList(options) {
31
32
  console.error('Error: not logged in. Run `lumo auth login` first.');
32
33
  return 1;
33
34
  }
34
- const envUrl = process.env.LUMO_API_URL?.trim();
35
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
35
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
36
36
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
37
37
  let projectId;
38
38
  try {
@@ -6,6 +6,7 @@ const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const resolve_1 = require("../lib/resolve");
8
8
  const format_1 = require("../lib/format");
9
+ const sanitize_1 = require("../lib/sanitize");
9
10
  function fmtDate(iso) {
10
11
  return iso ? iso.slice(0, 10) : '-';
11
12
  }
@@ -15,13 +16,13 @@ function formatMilestoneShow(m, tasks) {
15
16
  m.taskCounts.IN_REVIEW +
16
17
  m.taskCounts.DONE;
17
18
  const lines = [
18
- `Milestone: ${m.name}`,
19
+ `Milestone: ${(0, sanitize_1.sanitizeField)(m.name)}`,
19
20
  `Status: ${m.status}`,
20
21
  `Start: ${fmtDate(m.startDate)}`,
21
22
  `Target: ${fmtDate(m.targetDate)}`,
22
- `Project: ${m.projectName}`,
23
+ `Project: ${(0, sanitize_1.sanitizeField)(m.projectName)}`,
23
24
  `Description:`,
24
- ` ${m.description && m.description.length > 0 ? m.description : '-'}`,
25
+ ` ${m.description && m.description.length > 0 ? (0, sanitize_1.sanitizeField)(m.description) : '-'}`,
25
26
  ``,
26
27
  `Tasks: ${total} total (TODO ${m.taskCounts.TODO} / IN_PROGRESS ${m.taskCounts.IN_PROGRESS} / IN_REVIEW ${m.taskCounts.IN_REVIEW} / DONE ${m.taskCounts.DONE})`,
27
28
  ];
@@ -36,8 +37,7 @@ async function milestoneShow(identifier, opts) {
36
37
  console.error('Error: not logged in. Run `lumo auth login` first.');
37
38
  return 1;
38
39
  }
39
- const envUrl = process.env.LUMO_API_URL?.trim();
40
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
40
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
41
41
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
42
42
  let milestoneId;
43
43
  try {
@@ -7,6 +7,7 @@ exports.milestoneUpdate = milestoneUpdate;
7
7
  const config_1 = require("../lib/config");
8
8
  const api_1 = require("../lib/api");
9
9
  const resolve_1 = require("../lib/resolve");
10
+ const sanitize_1 = require("../lib/sanitize");
10
11
  const ALLOWED_STATUSES = [
11
12
  'PLANNED',
12
13
  'ACTIVE',
@@ -54,9 +55,10 @@ function fmtDate(v) {
54
55
  return v.slice(0, 10);
55
56
  }
56
57
  function formatMilestoneUpdateSummary(before, after) {
58
+ const beforeName = (0, sanitize_1.sanitizeField)(before.name);
57
59
  const changes = [];
58
60
  if (after.name !== undefined && after.name !== before.name) {
59
- changes.push(`name "${before.name}" → "${after.name}"`);
61
+ changes.push(`name "${beforeName}" → "${(0, sanitize_1.sanitizeField)(after.name)}"`);
60
62
  }
61
63
  if (after.description !== undefined) {
62
64
  changes.push(after.description === null ? 'description → ∅' : 'description updated');
@@ -70,7 +72,7 @@ function formatMilestoneUpdateSummary(before, after) {
70
72
  if (after.targetDate !== undefined) {
71
73
  changes.push(`target → ${fmtDate(after.targetDate)}`);
72
74
  }
73
- return `Updated milestone "${before.name}": ${changes.join(', ')}`;
75
+ return `Updated milestone "${beforeName}": ${changes.join(', ')}`;
74
76
  }
75
77
  async function milestoneUpdate(identifier, opts) {
76
78
  // Validate status flag eagerly.
@@ -96,8 +98,7 @@ async function milestoneUpdate(identifier, opts) {
96
98
  console.error('Error: not logged in. Run `lumo auth login` first.');
97
99
  return 1;
98
100
  }
99
- const envUrl = process.env.LUMO_API_URL?.trim();
100
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
101
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
101
102
  const base = (0, api_1.trimTrailingSlash)(apiUrl);
102
103
  let milestoneId;
103
104
  try {
@@ -160,7 +161,7 @@ async function milestoneUpdate(identifier, opts) {
160
161
  catch {
161
162
  // body wasn't JSON; keep the status-only message
162
163
  }
163
- console.error(`Error: ${errMsg}`);
164
+ console.error(`Error: ${(0, sanitize_1.sanitizeField)(errMsg)}`);
164
165
  return 1;
165
166
  }
166
167
  process.stdout.write(formatMilestoneUpdateSummary(before, payload) + '\n');
@@ -5,6 +5,7 @@ exports.projectList = projectList;
5
5
  const config_1 = require("../lib/config");
6
6
  const api_1 = require("../lib/api");
7
7
  const resolve_1 = require("../lib/resolve");
8
+ const sanitize_1 = require("../lib/sanitize");
8
9
  /**
9
10
  * Render projects as `slug-style-name Display Name` lines. The slug form
10
11
  * matches what `lumo task create --project <ref>` accepts (slugify(name)),
@@ -22,7 +23,7 @@ function formatProjectList(projects) {
22
23
  if (rows.length === 0)
23
24
  return 'No projects.';
24
25
  const w = Math.max(...rows.map(r => r.slug.length));
25
- return rows.map(r => `${r.slug.padEnd(w)} ${r.name}`).join('\n');
26
+ return rows.map(r => `${r.slug.padEnd(w)} ${(0, sanitize_1.sanitizeField)(r.name)}`).join('\n');
26
27
  }
27
28
  async function projectList() {
28
29
  const creds = (0, config_1.readCredentials)();
@@ -30,8 +31,7 @@ async function projectList() {
30
31
  console.error('Error: not logged in. Run `lumo auth login` first.');
31
32
  return 1;
32
33
  }
33
- const envUrl = process.env.LUMO_API_URL?.trim();
34
- const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
34
+ const apiUrl = (0, api_1.resolveAuthedApiUrl)(creds.apiUrl);
35
35
  const url = `${(0, api_1.trimTrailingSlash)(apiUrl)}/api/projects`;
36
36
  let res;
37
37
  try {