@empirical-gauzza/cli 1.0.26 → 1.0.28

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 CHANGED
@@ -1,63 +1,63 @@
1
- # @empirical-gauzza/cli
2
-
3
- Command-line installer and local MCP wiring helper for [Empirical](https://empirical.gauzza.com) — portable memory infrastructure for AI agents, assistants, and coding tools.
4
-
5
- Full documentation: **[empirical.gauzza.com/docs/cli-details](https://empirical.gauzza.com/docs/cli-details)**
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g @empirical-gauzza/cli
11
- ```
12
-
13
- ## Quick start
14
-
15
- ```bash
16
- empirical install # interactive setup for Claude Code, Codex, or Copilot CLI
17
- empirical auth login # browser-based OAuth login
18
- empirical doctor # verify token, API connectivity, and workspace status
19
- ```
20
-
21
- Full walkthrough: [empirical.gauzza.com/docs/install](https://empirical.gauzza.com/docs/install)
22
-
23
- ## What it does
24
-
25
- - Installs and configures the Empirical MCP plugin for supported coding agents (Claude Code, Codex CLI, Copilot CLI)
26
- - Manages OAuth login, including headless/CI bootstrap and Docker token flows
27
- - Reads, writes, and maintains memories from the command line
28
- - Manages Skills Kits — create, sync, and push authored Skills across machines
29
- - Manages workspaces, invites, and registered devices
30
- - Exposes agent-readable docs and a connectivity `doctor` check
31
-
32
- ## Commands
33
-
34
- | Command | Docs |
35
- | --- | --- |
36
- | `empirical install` | [/docs/install](https://empirical.gauzza.com/docs/install#connect) |
37
- | `empirical plugin install / check` | [/docs/cli-plugins](https://empirical.gauzza.com/docs/cli-plugins) |
38
- | `empirical auth login / logout` | [/docs/cli-auth](https://empirical.gauzza.com/docs/cli-auth) |
39
- | `empirical oauth bootstrap / refresh` | [/docs/cli-oauth](https://empirical.gauzza.com/docs/cli-oauth) |
40
- | `empirical policy show / set` | [/docs/cli-policy](https://empirical.gauzza.com/docs/cli-policy) |
41
- | `empirical memory query / record / update / delete / export ...` | [/docs/cli-memory](https://empirical.gauzza.com/docs/cli-memory) |
42
- | `empirical workspace list / create / invite ...` | [/docs/cli-workspaces](https://empirical.gauzza.com/docs/cli-workspaces) |
43
- | `empirical kit create / sync / use ...` | [/docs/cli-kit](https://empirical.gauzza.com/docs/cli-kit) |
44
- | `empirical skill push / history / rollback` | [/docs/cli-skill](https://empirical.gauzza.com/docs/cli-skill) |
45
- | `empirical device list / show` | [/docs/cli-devices](https://empirical.gauzza.com/docs/cli-devices) |
46
- | `empirical doc [topic]` | [/docs/cli-doc-command](https://empirical.gauzza.com/docs/cli-doc-command) |
47
- | `empirical doctor` | [/docs/cli-doctor](https://empirical.gauzza.com/docs/cli-doctor) |
48
-
49
- Run `empirical --help` at any time for the full, current command list, or `empirical doc` for agent-readable topic docs.
50
-
51
- ## Security model
52
-
53
- API keys and OAuth tokens are stored locally and are never printed in full after initial issuance. See [empirical.gauzza.com/docs/security-model](https://empirical.gauzza.com/docs/security-model) for details on credential handling and headless/CI usage.
54
-
55
- ## Links
56
-
57
- - [Documentation](https://empirical.gauzza.com/docs/install)
58
- - [Homepage](https://empirical.gauzza.com)
59
- - [Support](https://empirical.gauzza.com/support/)
60
-
61
- ## License
62
-
63
- MIT
1
+ # @empirical-gauzza/cli
2
+
3
+ Command-line installer and local MCP wiring helper for [Empirical](https://empirical.gauzza.com) — portable memory infrastructure for AI agents, assistants, and coding tools.
4
+
5
+ Full documentation: **[empirical.gauzza.com/docs/cli-details](https://empirical.gauzza.com/docs/cli-details)**
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @empirical-gauzza/cli
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ empirical install # interactive setup for Claude Code, Codex, or Copilot CLI
17
+ empirical auth login # browser-based OAuth login
18
+ empirical doctor # verify token, API connectivity, and workspace status
19
+ ```
20
+
21
+ Full walkthrough: [empirical.gauzza.com/docs/install](https://empirical.gauzza.com/docs/install)
22
+
23
+ ## What it does
24
+
25
+ - Installs and configures the Empirical MCP plugin for supported coding agents (Claude Code, Codex CLI, Copilot CLI)
26
+ - Manages OAuth login, including headless/CI bootstrap and Docker token flows
27
+ - Reads, writes, and maintains memories from the command line
28
+ - Manages Skills Kits — create, sync, and push authored Skills across machines
29
+ - Manages workspaces, invites, and registered devices
30
+ - Exposes agent-readable docs and a connectivity `doctor` check
31
+
32
+ ## Commands
33
+
34
+ | Command | Docs |
35
+ | --- | --- |
36
+ | `empirical install` | [/docs/install](https://empirical.gauzza.com/docs/install#connect) |
37
+ | `empirical plugin install / check` | [/docs/cli-plugins](https://empirical.gauzza.com/docs/cli-plugins) |
38
+ | `empirical auth login / logout` | [/docs/cli-auth](https://empirical.gauzza.com/docs/cli-auth) |
39
+ | `empirical oauth bootstrap / refresh` | [/docs/cli-oauth](https://empirical.gauzza.com/docs/cli-oauth) |
40
+ | `empirical policy show / set` | [/docs/cli-policy](https://empirical.gauzza.com/docs/cli-policy) |
41
+ | `empirical memory query / record / update / delete / export ...` | [/docs/cli-memory](https://empirical.gauzza.com/docs/cli-memory) |
42
+ | `empirical workspace list / create / invite ...` | [/docs/cli-workspaces](https://empirical.gauzza.com/docs/cli-workspaces) |
43
+ | `empirical kit create / sync / use ...` | [/docs/cli-kit](https://empirical.gauzza.com/docs/cli-kit) |
44
+ | `empirical skill push / history / rollback` | [/docs/cli-skill](https://empirical.gauzza.com/docs/cli-skill) |
45
+ | `empirical device list / show` | [/docs/cli-devices](https://empirical.gauzza.com/docs/cli-devices) |
46
+ | `empirical doc [topic]` | [/docs/cli-doc-command](https://empirical.gauzza.com/docs/cli-doc-command) |
47
+ | `empirical doctor` | [/docs/cli-doctor](https://empirical.gauzza.com/docs/cli-doctor) |
48
+
49
+ Run `empirical --help` at any time for the full, current command list, or `empirical doc` for agent-readable topic docs.
50
+
51
+ ## Security model
52
+
53
+ API keys and OAuth tokens are stored locally and are never printed in full after initial issuance. See [empirical.gauzza.com/docs/security-model](https://empirical.gauzza.com/docs/security-model) for details on credential handling and headless/CI usage.
54
+
55
+ ## Links
56
+
57
+ - [Documentation](https://empirical.gauzza.com/docs/install)
58
+ - [Homepage](https://empirical.gauzza.com)
59
+ - [Support](https://empirical.gauzza.com/support/)
60
+
61
+ ## License
62
+
63
+ MIT
package/bin/empirical.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  'use strict';
4
4
 
@@ -17,6 +17,7 @@ const {
17
17
  } = require('../lib/auth');
18
18
  const { isWorkspaceCommand, runWorkspaceCommand } = require('../lib/workspaceCommands');
19
19
  const { runMemoryCommand } = require('../lib/memoryCommands');
20
+ const { runAlertCommand } = require('../lib/alertCommands');
20
21
  const { runKitCommand, runSkillCommand, runDeviceCommand } = require('../lib/kitCommands');
21
22
  const { runDoctor } = require('../lib/doctor');
22
23
  const { runDoc } = require('../lib/doc');
@@ -91,6 +92,11 @@ async function main() {
91
92
  return;
92
93
  }
93
94
 
95
+ if (args[0] === 'alert') {
96
+ await runAlertCommand(args.slice(1));
97
+ return;
98
+ }
99
+
94
100
  if (args[0] === 'kit') {
95
101
  await runKitCommand(args.slice(1));
96
102
  return;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ // Plain-text rendering for a human reading stderr directly — deliberately NOT the MCP wording
4
+ // (renderAlertNotice's "NOTICE FOR THE USER - relay this verbatim"), which instructs a host
5
+ // MODEL to relay text to a person who can't see stderr at all. Here the person IS the reader.
6
+ const ANSI = {
7
+ reset: '\x1b[0m',
8
+ bold: '\x1b[1m',
9
+ yellow: '\x1b[33m',
10
+ red: '\x1b[31m',
11
+ cyan: '\x1b[36m'
12
+ };
13
+
14
+ function supportsColorStderr() {
15
+ return Boolean(process.stderr && process.stderr.isTTY) && !process.env.NO_COLOR;
16
+ }
17
+
18
+ const TONE_PREFIX = { info: 'i', success: '✓', warning: '!', error: '✗' };
19
+ const TONE_COLOR = { info: ANSI.cyan, success: ANSI.cyan, warning: ANSI.yellow, error: ANSI.red };
20
+
21
+ function colorize(code, text) {
22
+ if (!supportsColorStderr()) return text;
23
+ return `${code}${text}${ANSI.reset}`;
24
+ }
25
+
26
+ function renderOneAlert(alert) {
27
+ const prefix = TONE_PREFIX[alert.tone] || TONE_PREFIX.info;
28
+ const color = TONE_COLOR[alert.tone] || TONE_COLOR.info;
29
+ const lines = [];
30
+ lines.push(colorize(`${ANSI.bold}${color}`, `${prefix} ${alert.title}`));
31
+ lines.push(` ${alert.body}`);
32
+ for (const action of alert.actions || []) {
33
+ lines.push(` → ${action.label}: ${action.href}`);
34
+ }
35
+ lines.push(` (dismiss: empirical alert dismiss --alert-id ${alert.id})`);
36
+ return lines.join('\n');
37
+ }
38
+
39
+ function renderCliAlertBanner(alerts) {
40
+ if (!Array.isArray(alerts) || !alerts.length) return '';
41
+ return alerts.map(renderOneAlert).join('\n\n') + '\n';
42
+ }
43
+
44
+ module.exports = { renderCliAlertBanner };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const { requireAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
4
+ const { httpJson, printApiError } = require('./http');
5
+ const { log, warn } = require('./ui');
6
+
7
+ function parseOption(args, name, fallback) {
8
+ const idx = args.indexOf(name);
9
+ if (idx === -1 || idx === args.length - 1) return fallback;
10
+ return args[idx + 1];
11
+ }
12
+
13
+ async function runAlertList(_args) {
14
+ const accessToken = requireAccessToken();
15
+ const baseUrl = resolveApiBaseUrl();
16
+ const url = new URL('/api/v1/alerts', baseUrl).toString();
17
+ let response;
18
+ try {
19
+ response = await httpJson(url, 'GET', undefined, buildApiAuthHeaders(accessToken));
20
+ } catch (error) {
21
+ printApiError(error);
22
+ process.exit(1);
23
+ return;
24
+ }
25
+ const data = response && response.data ? response.data : response;
26
+ log(JSON.stringify(data.alerts !== undefined ? data.alerts : data));
27
+ }
28
+
29
+ async function runAlertDismiss(args) {
30
+ const alertId = parseOption(args, '--alert-id', '');
31
+ if (!alertId) {
32
+ warn('Usage: empirical alert dismiss --alert-id <id>');
33
+ process.exit(1);
34
+ return;
35
+ }
36
+ const accessToken = requireAccessToken();
37
+ const baseUrl = resolveApiBaseUrl();
38
+ const url = new URL(`/api/v1/alerts/${encodeURIComponent(alertId)}/dismiss`, baseUrl).toString();
39
+ let response;
40
+ try {
41
+ response = await httpJson(url, 'POST', undefined, buildApiAuthHeaders(accessToken));
42
+ } catch (error) {
43
+ printApiError(error);
44
+ process.exit(1);
45
+ return;
46
+ }
47
+ const data = response && response.data ? response.data : response;
48
+ log(JSON.stringify(data));
49
+ }
50
+
51
+ async function runAlertCommand(subArgs) {
52
+ const sub = subArgs[0] && !subArgs[0].startsWith('-') ? subArgs[0] : undefined;
53
+ const rest = subArgs.slice(1);
54
+
55
+ if (sub === 'list') return runAlertList(rest);
56
+ if (sub === 'dismiss') return runAlertDismiss(rest);
57
+
58
+ warn('Usage: empirical alert <list|dismiss> [options]');
59
+ process.exit(1);
60
+ }
61
+
62
+ module.exports = { runAlertCommand };
package/lib/constants.js CHANGED
@@ -8,12 +8,21 @@ const EMPIRICAL_MCP_LEGACY_NAMES = ['empirical-live'];
8
8
  const EMPIRICAL_MCP_URL = process.env.EMPIRICAL_MCP_URL || 'https://empirical.gauzza.com/mcp';
9
9
  const COPILOT_OAUTH_CLIENT_ID = process.env.EMPIRICAL_COPILOT_OAUTH_CLIENT_ID || 'copilot-cli-empirical';
10
10
 
11
+ // These two lists mirror the schema-level enums in packages/shared/src/memoryGraph.ts.
12
+ // They are duplicated rather than imported on purpose: this CLI publishes to npm as
13
+ // @empirical-gauzza/cli, and @empirical/shared is a private workspace package, so depending
14
+ // on it would break `npm install` for end users. tests/unit/cliMemoryEnumParity.test.js
15
+ // fails CI if the copies ever diverge.
11
16
  const MEMORY_CATEGORIES = [
12
17
  'health', 'fitness', 'food', 'supplement', 'mood', 'idea', 'business',
13
18
  'build', 'growth', 'learning', 'leadership', 'operations', 'workflow',
14
19
  'lead', 'project', 'recipe', 'preference', 'reflection', 'relationship', 'heartbeat'
15
20
  ];
16
21
 
22
+ const NODE_TYPES = [
23
+ 'event', 'entity', 'observation', 'reflection', 'goal', 'concept'
24
+ ];
25
+
17
26
  const DOC_TOPICS = {
18
27
  memory: `# Empirical Memory Commands
19
28
 
@@ -33,9 +42,26 @@ Flags:
33
42
  - \`--top-k\`: number of results, 1–100, default 3
34
43
  - \`--category\`: filter by category
35
44
  - \`--tags\`: comma-separated tags filter
45
+ - \`--tags-all\`: comma-separated tags that must ALL be present (AND match)
46
+ - \`--status\`: comma-separated status values to keep (for example open,in_progress)
47
+ - \`--path\`: pin one retrieval pathway (see below)
48
+ - \`--hops\`: 1 or 2, graph depth; only valid with \`--path graph\` or \`--path hebbian\`
36
49
  - \`--pretty\`: pretty-print JSON output
37
50
  - \`--format table\`: render results as a table instead of JSON (JSON stays the default)
38
51
 
52
+ #### Retrieval pathways
53
+
54
+ - \`--path semantic\`: ranked meaning match. Runs the embedding search and scores candidates.
55
+ Use it when you are asking a question and want the closest few answers.
56
+ - \`--path list\`: filtered browse. Skips ranking and returns the matching set in order, with
57
+ paging. Use it when you want everything carrying a tag, status, or category, not a top few.
58
+ - \`--path graph\`: relationship walk from the best entry points, out \`--hops\` steps. Use it
59
+ when what you want is connected to what you can name rather than worded like it.
60
+ - \`--path hebbian\`: co-recall walk, following the links that repeated retrieval strengthened.
61
+ Use it when you want what usually comes up alongside this, not what merely resembles it.
62
+
63
+ Omitting \`--path\` keeps the default, where the classifier decides which pathway answers.
64
+
39
65
  ### get
40
66
  Retrieve one memory by exact ID.
41
67
 
@@ -58,7 +84,12 @@ Flags:
58
84
  ### record
59
85
  Save a new memory.
60
86
 
61
- empirical memory record --category build --summary "Use ESM modules for all new packages" [--tags ts,node] [--node-type observation]
87
+ empirical memory record --category build --summary "Use ESM modules for all new packages" [--tags ts,node] [--node-type observation] [--author-type user|agent]
88
+
89
+ \`--author-type\` records WHO the fact belongs to, which the transport cannot know. Use
90
+ \`user\` for something the user said, decided or prefers; \`agent\` for your own session notes
91
+ and findings. Omitted, it is left unresolved rather than guessed. Retrieval reads it to keep
92
+ agent notes out of answers about the user's own life and work.
62
93
 
63
94
  Passing text that contains code (backticks, \${...}, $(...)): do NOT put it in a double-quoted
64
95
  \`--summary "..."\` -- the shell substitutes those away before the CLI runs and the write still
@@ -80,6 +111,18 @@ Flags:
80
111
  - \`--node-type\`: node type, default \`observation\`
81
112
  - \`--json-file\`: path to JSON file with full memory payload
82
113
 
114
+ Both \`--category\` and \`--node-type\` are closed sets. Do not guess a value: anything outside
115
+ these lists is rejected and the write does not happen.
116
+
117
+ Valid categories (20): health, fitness, food, supplement, mood, idea, business, build, growth,
118
+ learning, leadership, operations, workflow, lead, project, recipe, preference, reflection,
119
+ relationship, heartbeat.
120
+
121
+ Valid node types (6): event, entity, observation, reflection, goal, concept.
122
+
123
+ There is no \`product\` category and no \`decision\` node type. Record a decision as
124
+ \`--category project --node-type event\`.
125
+
83
126
  ### note
84
127
  Add a note to an existing memory.
85
128
 
@@ -590,6 +633,7 @@ module.exports = {
590
633
  EMPIRICAL_MCP_URL,
591
634
  COPILOT_OAUTH_CLIENT_ID,
592
635
  MEMORY_CATEGORIES,
636
+ NODE_TYPES,
593
637
  DOC_TOPICS,
594
638
  getCliVersion,
595
639
  deriveBaseUrlFromMcpUrl
package/lib/doc.js CHANGED
@@ -11,7 +11,7 @@ function runDoc(args) {
11
11
  user: 'Get current user identity (MCP: get_user; CLI: doctor --output json)',
12
12
  workspace: 'List, view, and switch workspaces',
13
13
  doctor: 'Check token, connectivity, and workspace health',
14
- agents: 'Supported agents, integration modes, and native plugin setup',
14
+ agents: 'Supported agents, integration modes, and native plugin setup',
15
15
  auth: 'Token types, bootstrap, refresh, resolution order',
16
16
  install: 'Install Empirical for Codex / Copilot / Claude Code',
17
17
  kit: 'Create, source, activate, and sync Skills Kits',
package/lib/env.js CHANGED
@@ -1,49 +1,49 @@
1
- 'use strict';
2
-
3
- const { ask } = require('./ui');
4
-
5
- function getEnv(name, fallback = '') {
6
- return String(process.env[name] ?? fallback).trim();
7
- }
8
-
9
- function requireEnv(name) {
10
- const value = getEnv(name);
11
- if (!value) {
12
- throw new Error(`Missing required env var: ${name}`);
13
- }
14
- return value;
15
- }
16
-
17
- async function resolveRequiredValueFromEnvOrPrompt(envName, promptLabel) {
18
- const value = getEnv(envName);
19
- if (value) return value;
20
-
21
- if (!process.stdin.isTTY) {
22
- throw new Error(`Missing required env var: ${envName}`);
23
- }
24
-
25
- const answer = (await ask(`${promptLabel}: `)).trim();
26
- if (!answer) {
27
- throw new Error(`${promptLabel} is required`);
28
- }
29
-
30
- return answer;
31
- }
32
-
33
- function parseFlag(args, flagName) {
34
- return args.includes(flagName);
35
- }
36
-
37
- function parseOption(args, optionName, fallback = '') {
38
- const index = args.indexOf(optionName);
39
- if (index === -1) return fallback;
40
- return String(args[index + 1] || '').trim() || fallback;
41
- }
42
-
43
- module.exports = {
44
- getEnv,
45
- requireEnv,
46
- resolveRequiredValueFromEnvOrPrompt,
47
- parseFlag,
48
- parseOption
49
- };
1
+ 'use strict';
2
+
3
+ const { ask } = require('./ui');
4
+
5
+ function getEnv(name, fallback = '') {
6
+ return String(process.env[name] ?? fallback).trim();
7
+ }
8
+
9
+ function requireEnv(name) {
10
+ const value = getEnv(name);
11
+ if (!value) {
12
+ throw new Error(`Missing required env var: ${name}`);
13
+ }
14
+ return value;
15
+ }
16
+
17
+ async function resolveRequiredValueFromEnvOrPrompt(envName, promptLabel) {
18
+ const value = getEnv(envName);
19
+ if (value) return value;
20
+
21
+ if (!process.stdin.isTTY) {
22
+ throw new Error(`Missing required env var: ${envName}`);
23
+ }
24
+
25
+ const answer = (await ask(`${promptLabel}: `)).trim();
26
+ if (!answer) {
27
+ throw new Error(`${promptLabel} is required`);
28
+ }
29
+
30
+ return answer;
31
+ }
32
+
33
+ function parseFlag(args, flagName) {
34
+ return args.includes(flagName);
35
+ }
36
+
37
+ function parseOption(args, optionName, fallback = '') {
38
+ const index = args.indexOf(optionName);
39
+ if (index === -1) return fallback;
40
+ return String(args[index + 1] || '').trim() || fallback;
41
+ }
42
+
43
+ module.exports = {
44
+ getEnv,
45
+ requireEnv,
46
+ resolveRequiredValueFromEnvOrPrompt,
47
+ parseFlag,
48
+ parseOption
49
+ };
package/lib/http.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const http = require('http');
4
4
  const https = require('https');
5
5
  const { warn } = require('./ui');
6
+ const { renderCliAlertBanner } = require('./alertBanner');
6
7
 
7
8
  function httpGet(urlString, timeoutMs = 8000) {
8
9
  return new Promise((resolve, reject) => {
@@ -13,7 +14,7 @@ function httpGet(urlString, timeoutMs = 8000) {
13
14
  {
14
15
  method: 'GET',
15
16
  headers: {
16
- 'User-Agent': 'empirical-installer/1.0'
17
+ 'User-Agent': 'empirical-cli/1.0'
17
18
  }
18
19
  },
19
20
  (res) => {
@@ -82,6 +83,10 @@ function httpJson(urlString, method, payload, headers = {}, timeoutMs = 0) {
82
83
  method,
83
84
  headers: {
84
85
  Accept: 'application/json',
86
+ // Identifies the client so the server can tell a CLI write from a portal write
87
+ // (services/memoryAuthorship.ts). Writes also send source: 'cli' in the body,
88
+ // which is authoritative; this covers every other endpoint.
89
+ 'User-Agent': 'empirical-cli/1.0',
85
90
  ...(body ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } : {}),
86
91
  ...headers
87
92
  }
@@ -100,6 +105,10 @@ function httpJson(urlString, method, payload, headers = {}, timeoutMs = 0) {
100
105
  data = { raw };
101
106
  }
102
107
 
108
+ if (data && Array.isArray(data.alerts) && data.alerts.length) {
109
+ process.stderr.write(renderCliAlertBanner(data.alerts));
110
+ }
111
+
103
112
  const statusCode = res.statusCode || 0;
104
113
  if (statusCode < 200 || statusCode >= 300) {
105
114
  reject(httpErrorFromResponse(method, urlString, statusCode, data));
@@ -134,6 +143,10 @@ function httpForm(urlString, method, params, headers = {}) {
134
143
  method,
135
144
  headers: {
136
145
  Accept: 'application/json',
146
+ // Identifies the client so the server can tell a CLI write from a portal write
147
+ // (services/memoryAuthorship.ts). Writes also send source: 'cli' in the body,
148
+ // which is authoritative; this covers every other endpoint.
149
+ 'User-Agent': 'empirical-cli/1.0',
137
150
  'Content-Type': 'application/x-www-form-urlencoded',
138
151
  'Content-Length': Buffer.byteLength(body),
139
152
  ...headers
@@ -51,6 +51,11 @@ picking up previously completed work), query Empirical first - do not assume you
51
51
  enough context. After completing meaningful work, or discovering a mistake worth not repeating,
52
52
  record it in Empirical, not in a local file.
53
53
 
54
+ When you record, say who authored the fact: \`authorType: "user"\` for something the user said,
55
+ decided or prefers, \`"agent"\` for your own work and findings. Nothing infers this - not the
56
+ transport, not the client - so an omitted value stays unknown forever, and a wrong one puts your
57
+ session notes into answers about the user's own life.
58
+
54
59
  Installed Empirical skills in this environment:
55
60
  ${skillLines}
56
61
 
@@ -1,47 +1,47 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
- const { commandExists } = require('../fsUtils');
7
- const { installCodex } = require('./codex');
8
- const { installCopilot } = require('./copilot');
9
- const { installClaude } = require('./claude');
10
-
11
- function detectInstallTargets() {
12
- const home = os.homedir();
13
- const targets = [];
14
-
15
- const codexDetected = commandExists('codex') || fs.existsSync(path.join(home, '.codex'));
16
- if (codexDetected) {
17
- targets.push({
18
- id: 'codex',
19
- label: 'Codex CLI',
20
- install: installCodex
21
- });
22
- }
23
-
24
- const copilotDetected = commandExists('copilot') || fs.existsSync(path.join(home, '.copilot'));
25
- if (copilotDetected) {
26
- targets.push({
27
- id: 'copilot',
28
- label: 'GitHub Copilot CLI',
29
- install: installCopilot
30
- });
31
- }
32
-
33
- const claudeDetected = commandExists('claude') || fs.existsSync(path.join(home, '.claude'));
34
- if (claudeDetected) {
35
- targets.push({
36
- id: 'claude',
37
- label: 'Claude CLI',
38
- install: installClaude
39
- });
40
- }
41
-
42
- return targets;
43
- }
44
-
45
- module.exports = {
46
- detectInstallTargets
47
- };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const { commandExists } = require('../fsUtils');
7
+ const { installCodex } = require('./codex');
8
+ const { installCopilot } = require('./copilot');
9
+ const { installClaude } = require('./claude');
10
+
11
+ function detectInstallTargets() {
12
+ const home = os.homedir();
13
+ const targets = [];
14
+
15
+ const codexDetected = commandExists('codex') || fs.existsSync(path.join(home, '.codex'));
16
+ if (codexDetected) {
17
+ targets.push({
18
+ id: 'codex',
19
+ label: 'Codex CLI',
20
+ install: installCodex
21
+ });
22
+ }
23
+
24
+ const copilotDetected = commandExists('copilot') || fs.existsSync(path.join(home, '.copilot'));
25
+ if (copilotDetected) {
26
+ targets.push({
27
+ id: 'copilot',
28
+ label: 'GitHub Copilot CLI',
29
+ install: installCopilot
30
+ });
31
+ }
32
+
33
+ const claudeDetected = commandExists('claude') || fs.existsSync(path.join(home, '.claude'));
34
+ if (claudeDetected) {
35
+ targets.push({
36
+ id: 'claude',
37
+ label: 'Claude CLI',
38
+ install: installClaude
39
+ });
40
+ }
41
+
42
+ return targets;
43
+ }
44
+
45
+ module.exports = {
46
+ detectInstallTargets
47
+ };