@empirical-gauzza/cli 1.0.9

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.
@@ -0,0 +1,491 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const EMPIRICAL_MCP_NAME = 'empirical';
7
+ const EMPIRICAL_MCP_LEGACY_NAMES = ['empirical-live'];
8
+ const EMPIRICAL_MCP_URL = process.env.EMPIRICAL_MCP_URL || 'https://empirical.gauzza.com/mcp';
9
+ const COPILOT_OAUTH_CLIENT_ID = process.env.EMPIRICAL_COPILOT_OAUTH_CLIENT_ID || 'copilot-cli-empirical';
10
+
11
+ const MEMORY_CATEGORIES = [
12
+ 'health', 'fitness', 'food', 'supplement', 'mood', 'idea', 'business',
13
+ 'build', 'growth', 'learning', 'leadership', 'operations', 'workflow',
14
+ 'lead', 'project', 'recipe', 'preference', 'reflection', 'relationship', 'heartbeat'
15
+ ];
16
+
17
+ const DOC_TOPICS = {
18
+ memory: `# Empirical Memory Commands
19
+
20
+ Use the \`empirical memory\` subcommands to query and manage memories via the REST API.
21
+ All commands require a valid access token (see \`empirical doc auth\`).
22
+ Errors are written to stderr as JSON. Successful output is written to stdout as JSON.
23
+
24
+ ## Subcommands
25
+
26
+ ### query
27
+ Search memories by natural language.
28
+
29
+ empirical memory query --match "coding style preferences" [--top-k 3] [--category build] [--tags ts,node] [--pretty]
30
+
31
+ Flags:
32
+ - \`--match\` (required): natural language search string
33
+ - \`--top-k\`: number of results, 1–100, default 3
34
+ - \`--category\`: filter by category
35
+ - \`--tags\`: comma-separated tags filter
36
+ - \`--pretty\`: pretty-print JSON output
37
+
38
+ ### get
39
+ Retrieve one memory by exact ID.
40
+
41
+ empirical memory get --memory-id <id> [--pretty]
42
+
43
+ ### list
44
+ Browse memories deterministically. Use \`--cursor\` from the previous response while \`pagination.hasMore\` is true.
45
+
46
+ empirical memory list [--category project] [--tags canonical,launch] [--start-date <ISO>] [--end-date <ISO>] [--limit 20] [--cursor <cursor>] [--oldest] [--pretty]
47
+
48
+ Flags:
49
+ - \`--category\`, \`--tags\`: optional filters
50
+ - \`--start-date\`, \`--end-date\`: createdAt bounds
51
+ - \`--limit\`: page size, 1–100, default 20
52
+ - \`--cursor\`: continuation cursor from a previous page
53
+ - \`--oldest\`: sort oldest first; default is newest first
54
+ - \`--pretty\`: pretty-print JSON output
55
+
56
+ ### record
57
+ Save a new memory.
58
+
59
+ empirical memory record --category build --summary "Use ESM modules for all new packages" [--tags ts,node] [--node-type observation]
60
+
61
+ Flags:
62
+ - \`--category\` (required unless --json-file): memory category
63
+ - \`--summary\` (required unless --json-file): memory summary text
64
+ - \`--tags\`: comma-separated tags
65
+ - \`--node-type\`: node type, default \`observation\`
66
+ - \`--json-file\`: path to JSON file with full memory payload
67
+
68
+ ### note
69
+ Add a note to an existing memory.
70
+
71
+ empirical memory note --match "ESM modules" --text "confirmed in monorepo migration" [--min-score 0.35]
72
+
73
+ Flags:
74
+ - \`--match\` (required unless --memory-id): search string to find the memory
75
+ - \`--text\` (required): note text to append
76
+ - \`--memory-id\`: use exact memory ID (skips fuzzy search)
77
+ - \`--min-score\`: minimum similarity score, default 0.35
78
+
79
+ ### update
80
+ Update an existing memory.
81
+
82
+ empirical memory update --match "ESM modules" --patch-json '{"summary":"Updated summary"}' [--min-score 0.35]
83
+
84
+ Flags:
85
+ - \`--match\` (required unless --memory-id): search string
86
+ - \`--patch-json\`: JSON string with fields to update
87
+ - \`--json-file\`: path to JSON patch file
88
+ - \`--memory-id\`: use exact memory ID
89
+ - \`--min-score\`: minimum similarity score, default 0.35
90
+
91
+ ### delete
92
+ Delete a memory. Without --confirm, runs a dry-run and prints what would be deleted.
93
+
94
+ empirical memory delete --match "old preference" --confirm [--min-score 0.35]
95
+
96
+ Flags:
97
+ - \`--match\` (required unless --memory-id): search string
98
+ - \`--confirm\`: actually delete (without this, dry-run only)
99
+ - \`--memory-id\`: use exact memory ID
100
+ - \`--min-score\`: minimum similarity score, default 0.35
101
+
102
+ ## Error output contract
103
+ - Errors → stderr as JSON: \`{ "error": true, "code": "...", "message": "..." }\`
104
+ - Success → stdout as JSON (compact by default)
105
+ `,
106
+
107
+ install: `# Empirical Install
108
+
109
+ empirical install
110
+
111
+ Runs the interactive installer to connect Empirical memory to your AI coding CLI(s).
112
+
113
+ ## What it does
114
+ 1. Detects installed CLIs: Codex, GitHub Copilot, Claude Code
115
+ 2. Prompts for CLI target selection
116
+ 3. Asks how agents should interact with Empirical:
117
+ - Mode 1: Via MCP tool (recommended) — registers Empirical as an MCP server
118
+ - Mode 2: Via CLI commands — writes CLI-direct instruction block, no MCP registration
119
+ - Mode 3: Both — MCP primary with CLI fallback
120
+ 4. Stores the personalized behavior policy for the native Empirical Memory Skill; global
121
+ instruction files are left unchanged
122
+ 5. Offers optional browser OAuth login for CLI use, optional headless/API-key bootstrap for
123
+ automation, containers, and external runtimes, and optional work-history/scar-tissue guidance
124
+ 6. For Claude, installs a \`SessionStart\` hook in \`~/.claude/settings.json\` that calls the
125
+ \`empirical\` CLI directly once per session — deterministic baseline context that doesn't
126
+ depend on the model deciding to call a tool. Codex/Copilot have no equivalent hook mechanism.
127
+ 7. Installs the native \`empirical-memory\` plugin for each selected client. The plugin owns
128
+ the Empirical MCP connection and all agent-facing Skills; the CLI does not generate duplicate
129
+ \`SKILL.md\` or \`EMPIRICAL_SKILL.md\` files.
130
+
131
+ ## Native plugin only
132
+
133
+ empirical plugin install <codex|copilot|claude|all> [--all-optional]
134
+
135
+ empirical plugin check [--force]
136
+
137
+ Installs or refreshes only the native marketplace plugin. It does not run the MCP/auth questionnaire
138
+ or modify global instruction files. With no target, the command installs for detected CLIs.
139
+
140
+ The CLI performs a best-effort native plugin version check during interactive use (at most once per
141
+ day). It is silent for noninteractive/headless commands. Use \`empirical plugin check --force\` to
142
+ check immediately.
143
+
144
+ Use \`empirical policy show\` to inspect the current native-skill behavior policy and
145
+ \`empirical policy set --json '{"allowDiscretionaryUse":false}'\` to change it without rerunning
146
+ the installer. The policy is cached locally and refreshed when stale.
147
+ `,
148
+
149
+ workspace: `# Empirical Workspace Commands
150
+
151
+ Manage disposable team workspaces and which Empirical workspace is active for this OAuth client.
152
+
153
+ ## Commands
154
+
155
+ ### List workspaces
156
+
157
+ empirical workspace list
158
+
159
+ Shows all available workspaces and which one is currently selected.
160
+
161
+ ### Show current workspace
162
+
163
+ empirical workspace current
164
+
165
+ Prints the currently selected workspace name.
166
+
167
+ ### Select a workspace
168
+
169
+ empirical workspace select [<number|name>]
170
+
171
+ Interactive workspace selector (prompts if no argument given).
172
+ Pass a number (from \`list\`) or an exact workspace name.
173
+
174
+ empirical workspace select 2
175
+ empirical workspace select "My Workspace"
176
+
177
+ Note: workspace selection may be admin-locked for some OAuth clients.
178
+
179
+ ### Create a demo workspace
180
+
181
+ empirical workspace create "Demo Workspace" --description "Temporary shared context"
182
+
183
+ Creates a team workspace using your existing plan quota and prints its workspace ID.
184
+
185
+ ### Use a workspace
186
+
187
+ empirical workspace use <number|name|id>
188
+
189
+ Friendly alias for \`workspace select\`. It accepts the list number, exact case-insensitive name, or workspace ID.
190
+
191
+ ### Share an active workspace
192
+
193
+ empirical workspace invite create --role writer --expires-in-days 7 --max-uses 3
194
+ empirical workspace invite list
195
+ empirical workspace invite revoke <token>
196
+
197
+ Invite commands operate on the active team workspace. Create prints a shareable URL.
198
+
199
+ ### Soft-delete a demo workspace
200
+
201
+ empirical workspace delete <number|name|id>
202
+ empirical workspace delete <number|name|id> --confirm
203
+
204
+ Deletion dry-runs by default. \`--confirm\` calls the existing soft-delete endpoint; the Default Brain cannot be deleted. If the active workspace is deleted, the CLI selects the Default Brain.
205
+ `,
206
+
207
+ doctor: `# Empirical Doctor
208
+
209
+ empirical doctor [--output json]
210
+
211
+ Checks the health of your Empirical CLI setup. Useful for diagnosing auth and connectivity issues.
212
+
213
+ ## Checks performed (in order)
214
+
215
+ 1. **Token presence** — verifies a token exists via EMPIRICAL_ACCESS_TOKEN env, apiKey field,
216
+ or accessToken field in the token file
217
+ 2. **Token type** — detects API key (long alphanumeric, no dots) vs JWT (3 dot-separated parts)
218
+ 3. **Token expiry** — if JWT, decodes the payload and checks the \`exp\` field; warns if no \`exp\`
219
+ 4. **API connectivity** — calls GET /api/users/me; 200 = OK, anything else = FAIL
220
+ 5. **Workspace** — reads workspaceId from the /api/users/me response; warns if null
221
+
222
+ ## Output format
223
+
224
+ Default: human-readable lines prefixed with [OK], [WARN], or [FAIL].
225
+
226
+ empirical doctor --output json
227
+
228
+ JSON output: \`{ "checks": [{ "name", "status", "message" }], "overall": "ok"|"warn"|"fail" }\`
229
+ Useful for agent/automation use.
230
+
231
+ ## Exit codes
232
+ - 0: all checks passed (ok or warn)
233
+ - 1: one or more checks failed
234
+ `,
235
+
236
+ agents: `# Empirical Agent Integration
237
+
238
+ Empirical memory integrates with three AI coding CLIs.
239
+
240
+ ## Supported agents
241
+
242
+ | Agent | Config dir | Instruction file | MCP config |
243
+ |-------------|---------------------|-------------------------------|-------------------------------------|
244
+ | Claude Code | ~/.claude/ | CLAUDE.md | ~/.claude.json (mcpServers) |
245
+ | Codex CLI | ~/.codex/ | AGENTS.md | codex mcp add |
246
+ | Copilot CLI | ~/.copilot/ | copilot-instructions.md | ~/.copilot/mcp-config.json |
247
+
248
+ ## Integration modes
249
+
250
+ **Mode 1: MCP tool (recommended)**
251
+ Empirical is registered as an MCP server. Agents call memory tools (query_memories,
252
+ record_graph_memory, etc.) directly via the MCP protocol. Best for interactive sessions.
253
+
254
+ **Mode 2: CLI commands**
255
+ Agents run \`empirical memory <subcommand>\` shell commands. No MCP registration required.
256
+ Suitable for headless/scripted environments or when MCP is unavailable.
257
+
258
+ **Mode 3: Both**
259
+ MCP is primary; CLI commands are available as fallback. Maximizes reliability.
260
+
261
+ ## Inventory commands
262
+
263
+ empirical skill list
264
+ empirical device list
265
+ empirical device show <deviceId>
266
+
267
+ Use these to inspect Skills across Kits and the effective Skills installed for a device.
268
+
269
+ The native \`empirical-memory\` plugin owns its Skills and MCP connection. Run
270
+ \`empirical install\` to install it and configure detected agents.
271
+ `,
272
+
273
+ kit: `# Empirical Skills Kits
274
+
275
+ Kits are named collections of agent Skills. A Kit can contain Skills authored in Empirical
276
+ or discovered from a git repository, then sync the resolved set to your local agents.
277
+
278
+ ## Commands
279
+
280
+ ### Create and list Kits
281
+
282
+ empirical kit create "My Team Kit"
283
+ empirical kit list
284
+
285
+ ### Add or remove a git source
286
+
287
+ empirical kit add-source "My Team Kit" https://github.com/example/skills.git
288
+ empirical kit add-source "My Team Kit" https://github.com/example/skills.git --ref main --path skills
289
+ empirical kit remove-source "My Team Kit" <source-id>
290
+
291
+ Each source is scanned for valid \`SKILL.md\` files. A Skill file must begin with YAML
292
+ frontmatter containing both \`name\` and \`description\`, for example:
293
+
294
+ ---
295
+ name: my-skill
296
+ description: Explain when this skill should be used.
297
+ ---
298
+
299
+ ### Activate and sync
300
+
301
+ empirical kit use "My Team Kit"
302
+ empirical kit sync
303
+ empirical kit sync --dry-run
304
+
305
+ \`kit use\` activates a Kit on this device and syncs its Skills into detected agent
306
+ directories. \`kit sync\` refreshes the active Kit. Use \`--quiet\` for unattended syncs
307
+ and \`--force\` only when you intentionally want to bypass the local-edit guard.
308
+
309
+ ### Inventory
310
+
311
+ empirical skill list
312
+ empirical kit list
313
+ empirical kit devices "My Team Kit"
314
+ empirical device list
315
+ empirical device show <deviceId>
316
+
317
+ \`skill list\` shows Skills resolved from owned Kits. \`device list\` shows registered devices and
318
+ their active Kits; \`device show\` shows the effective resolved Skills for one device.
319
+
320
+ ### Manage devices and autosync
321
+
322
+ empirical kit autosync "My Team Kit" on
323
+ empirical kit devices "My Team Kit"
324
+ empirical kit deactivate "My Team Kit" <device-id>
325
+
326
+ Autosync keeps a registered device current. Interactive syncs review new or changed external
327
+ Skills; unattended syncs apply them and leave a review marker for the next interactive sync.
328
+ `,
329
+
330
+ skill: `# Empirical Skills
331
+
332
+ Skills are agent-facing \`SKILL.md\` instruction files. Authored Skills can be pushed into a
333
+ Kit so they follow you across machines and projects.
334
+
335
+ ## Push an authored Skill
336
+
337
+ empirical skill list
338
+
339
+ empirical skill push path/to/SKILL.md
340
+ empirical skill push path/to/SKILL.md --kit "My Team Kit"
341
+
342
+ The file must contain YAML frontmatter at the top with both required fields:
343
+
344
+ ---
345
+ name: my-skill
346
+ description: Explain when this skill should be used.
347
+ ---
348
+
349
+ The remainder of the file is the Skill body. The CLI validates the frontmatter before upload.
350
+ After pushing, activate or sync the target Kit with \`empirical kit use\` or \`empirical kit sync\`.
351
+
352
+ ## Review and rollback
353
+
354
+ empirical skill history <authored-skill-id>
355
+ empirical skill rollback <authored-skill-id> --to-version 2
356
+
357
+ Use history to inspect versions and rollback to a known-good version when a Skill needs to be
358
+ reverted. External git-sourced Skills are reviewed during Kit sync and are not authored with
359
+ this command.
360
+ `,
361
+
362
+ auth: `# Empirical Auth
363
+
364
+ ## Token types
365
+
366
+ **API key**: Long alphanumeric string, no dots. Never expires. Preferred for automation.
367
+ **JWT**: Three dot-separated base64 parts. Has an expiry (\`exp\` claim). Used for browser OAuth flows.
368
+
369
+ ## Browser OAuth login
370
+
371
+ empirical auth login
372
+
373
+ Opens the browser, completes OAuth with the Empirical account, and saves a CLI session at
374
+ \`~/.empirical/oauth-session.json\`. Use this for normal interactive CLI use when you do not
375
+ want to provision a permanent API key.
376
+
377
+ ## Bootstrap command (headless / non-interactive)
378
+
379
+ empirical oauth bootstrap headless --write-env
380
+
381
+ Exchanges account credentials (client ID + secret, i.e. your userId + API key) for an access
382
+ token via \`grant_type=client_credentials\` — this is NOT a dynamically-registered OAuth client;
383
+ the server treats client_id/client_secret as userId/apiKey directly.
384
+ Writes token to \`.empirical/headless-token.json\` and \`.empirical/headless.env\`.
385
+
386
+ No portal visit or manual copy/paste needed: if no client ID/secret is found via
387
+ \`EMPIRICAL_CLIENT_ID\`/\`EMPIRICAL_CLIENT_SECRET\` env vars or \`~/.empirical/oauth-client.json\`
388
+ (set via \`empirical oauth client set\`), the CLI self-provisions your account's API key via
389
+ \`POST /api/v1/users/me/credentials\`, authenticated with whatever bearer token it already has
390
+ (existing token file, or — only if nothing exists yet — one browser login). If you already have
391
+ an API key on the account, the CLI will never silently rotate it (that would break other
392
+ integrations using it); it asks for explicit confirmation in a TTY, or gives up quietly in
393
+ non-interactive contexts. If a stored client is rejected by the server ("Invalid client
394
+ credentials"), the CLI discards it and re-provisions automatically on the same run — unless
395
+ \`--client-id\`/\`--client-secret\` was passed explicitly, in which case the error is surfaced
396
+ as-is.
397
+
398
+ ## Refresh command
399
+
400
+ empirical oauth refresh headless
401
+
402
+ Exchanges a stored refresh token for a new access token. Token file must exist.
403
+
404
+ ## Token resolution priority (resolveAccessToken)
405
+
406
+ 1. \`EMPIRICAL_ACCESS_TOKEN\` environment variable (skipped if it's an expired JWT, so a
407
+ stale session token can never permanently shadow a valid credential below it)
408
+ 2. \`apiKey\` field in token file
409
+ 3. \`accessToken\` field in token file
410
+ 4. \`~/.empirical/oauth-session.json\` browser OAuth session
411
+ 5. \`EMPIRICAL_API_KEY\` or \`EMPIRICAL_CLIENT_SECRET\` environment variable, used directly
412
+ as a permanent Bearer credential — lets a stateless agent environment authenticate from
413
+ client_id/secret alone with no token file and no bootstrap step
414
+
415
+ Token file lookup order:
416
+ 1. Path in \`EMPIRICAL_TOKEN_OUTPUT\` env var
417
+ 2. \`.empirical/headless-token.json\` (current working directory)
418
+ 3. \`~/.empirical/headless-token.json\` (home directory)
419
+
420
+ The browser OAuth session is stored separately at \`~/.empirical/oauth-session.json\`.
421
+
422
+ ## Verify auth status
423
+
424
+ empirical doctor
425
+
426
+ Checks token presence, type, expiry, and API connectivity.
427
+ `,
428
+
429
+ user: `# Empirical User Identity
430
+
431
+ ## Via MCP (when Empirical MCP server is connected)
432
+
433
+ Use the \`get_user\` MCP tool. Returns: id, email, name, workspaceId, tier, and plan details.
434
+
435
+ ## Via CLI
436
+
437
+ empirical doctor --output json
438
+
439
+ Returns a JSON object with a \`checks\` array. The connectivity check (name: "api")
440
+ includes the user record from /api/users/me when successful.
441
+
442
+ empirical workspace current
443
+
444
+ Prints the active workspace name.
445
+
446
+ ## Notes
447
+
448
+ - \`get_user\` has no standalone CLI subcommand; use \`empirical doctor --output json\` to
449
+ inspect identity from the shell.
450
+ - If MCP is unavailable, parse the \`api\` check from doctor JSON output for user context.
451
+ `
452
+ };
453
+
454
+ let cachedCliVersion = null;
455
+
456
+ function getCliVersion() {
457
+ if (cachedCliVersion) return cachedCliVersion;
458
+ try {
459
+ const cliPackagePath = path.join(__dirname, '..', 'package.json');
460
+ const parsed = JSON.parse(fs.readFileSync(cliPackagePath, 'utf8'));
461
+ if (parsed && typeof parsed.version === 'string' && parsed.version.trim()) {
462
+ cachedCliVersion = parsed.version.trim();
463
+ return cachedCliVersion;
464
+ }
465
+ } catch (_error) {
466
+ // ignore and use fallback
467
+ }
468
+ cachedCliVersion = 'unknown';
469
+ return cachedCliVersion;
470
+ }
471
+
472
+ function deriveBaseUrlFromMcpUrl() {
473
+ try {
474
+ const parsed = new URL(EMPIRICAL_MCP_URL);
475
+ parsed.pathname = parsed.pathname.replace(/\/mcp\/?$/, '');
476
+ return parsed.origin + parsed.pathname.replace(/\/$/, '');
477
+ } catch (_error) {
478
+ return '';
479
+ }
480
+ }
481
+
482
+ module.exports = {
483
+ EMPIRICAL_MCP_NAME,
484
+ EMPIRICAL_MCP_LEGACY_NAMES,
485
+ EMPIRICAL_MCP_URL,
486
+ COPILOT_OAUTH_CLIENT_ID,
487
+ MEMORY_CATEGORIES,
488
+ DOC_TOPICS,
489
+ getCliVersion,
490
+ deriveBaseUrlFromMcpUrl
491
+ };
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ // US-011: local device config (~/.empirical/config.json) — deviceId bootstrap/persistence,
4
+ // activeKit, and lastSyncedVersion. Follows the same `os.homedir()/.empirical/` convention
5
+ // as the headless-token.json file in packages/cli/lib/auth.js.
6
+ const crypto = require('crypto');
7
+ const os = require('os');
8
+ const path = require('path');
9
+ const { readJsonIfExists, writeFile } = require('./fsUtils');
10
+
11
+ function getEmpiricalDir() {
12
+ return path.join(os.homedir(), '.empirical');
13
+ }
14
+
15
+ function getDeviceConfigPath() {
16
+ return path.join(getEmpiricalDir(), 'config.json');
17
+ }
18
+
19
+ function readDeviceConfig() {
20
+ return readJsonIfExists(getDeviceConfigPath()) || {};
21
+ }
22
+
23
+ function writeDeviceConfig(config) {
24
+ writeFile(getDeviceConfigPath(), `${JSON.stringify(config, null, 2)}\n`);
25
+ }
26
+
27
+ function readPolicyCache() {
28
+ const config = readDeviceConfig();
29
+ return config.empiricalPolicy || null;
30
+ }
31
+
32
+ function writePolicyCache(policyCache) {
33
+ const config = readDeviceConfig();
34
+ config.empiricalPolicy = policyCache;
35
+ writeDeviceConfig(config);
36
+ }
37
+
38
+ // Ensures a deviceId exists in the local config, generating+persisting one on first use.
39
+ // Reused across all future CLI invocations on this machine.
40
+ function ensureDeviceId() {
41
+ const config = readDeviceConfig();
42
+ if (config.deviceId) {
43
+ return config.deviceId;
44
+ }
45
+ const deviceId = crypto.randomUUID();
46
+ config.deviceId = deviceId;
47
+ writeDeviceConfig(config);
48
+ return deviceId;
49
+ }
50
+
51
+ function getSyncManifestPath() {
52
+ return path.join(getEmpiricalDir(), 'sync-manifest.json');
53
+ }
54
+
55
+ function readSyncManifest() {
56
+ return readJsonIfExists(getSyncManifestPath()) || {};
57
+ }
58
+
59
+ function writeSyncManifest(manifest) {
60
+ writeFile(getSyncManifestPath(), `${JSON.stringify(manifest, null, 2)}\n`);
61
+ }
62
+
63
+ // US-016: local record of git_custom-sourced skills whose upstream content changed and was
64
+ // applied unattended (during a `--quiet` sync) without a human reviewing it first. Shape:
65
+ // { [skillName]: { from, firstDetectedAt, source, status } } — see skillSync.js for the gate
66
+ // that populates/clears this. Same `os.homedir()/.empirical/` convention as config.json and
67
+ // sync-manifest.json.
68
+ function getPendingReviewPath() {
69
+ return path.join(getEmpiricalDir(), 'pending-review.json');
70
+ }
71
+
72
+ function readPendingReview() {
73
+ return readJsonIfExists(getPendingReviewPath()) || {};
74
+ }
75
+
76
+ function writePendingReview(pendingReview) {
77
+ writeFile(getPendingReviewPath(), `${JSON.stringify(pendingReview, null, 2)}\n`);
78
+ }
79
+
80
+ module.exports = {
81
+ getEmpiricalDir,
82
+ getDeviceConfigPath,
83
+ readDeviceConfig,
84
+ writeDeviceConfig,
85
+ readPolicyCache,
86
+ writePolicyCache,
87
+ ensureDeviceId,
88
+ getSyncManifestPath,
89
+ readSyncManifest,
90
+ writeSyncManifest,
91
+ getPendingReviewPath,
92
+ readPendingReview,
93
+ writePendingReview
94
+ };
package/lib/doc.js ADDED
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ const { log, warn, ANSI, colorize, supportsColor } = require('./ui');
4
+ const { DOC_TOPICS } = require('./constants');
5
+
6
+ function runDoc(args) {
7
+ const topic = (args[0] || '').trim().toLowerCase();
8
+
9
+ const descriptions = {
10
+ memory: 'Query, record, update, delete memories via CLI',
11
+ user: 'Get current user identity (MCP: get_user; CLI: doctor --output json)',
12
+ workspace: 'List, view, and switch workspaces',
13
+ doctor: 'Check token, connectivity, and workspace health',
14
+ agents: 'Supported agents, integration modes, and native plugin setup',
15
+ auth: 'Token types, bootstrap, refresh, resolution order',
16
+ install: 'Install Empirical for Codex / Copilot / Claude Code',
17
+ kit: 'Create, source, activate, and sync Skills Kits',
18
+ skill: 'Push, inspect, and roll back authored Skills'
19
+ };
20
+
21
+ if (!topic) {
22
+ log('Available documentation topics:');
23
+ log('');
24
+ for (const [key, desc] of Object.entries(descriptions)) {
25
+ const padded = key.padEnd(12);
26
+ if (supportsColor()) {
27
+ log(` ${colorize(ANSI.cyan, padded)} ${desc}`);
28
+ } else {
29
+ log(` ${padded} ${desc}`);
30
+ }
31
+ }
32
+ log('');
33
+ log('Run: empirical doc <topic>');
34
+ return;
35
+ }
36
+
37
+ if (!DOC_TOPICS[topic]) {
38
+ warn(`Unknown topic: "${topic}"`);
39
+ log('');
40
+ log('Available topics: ' + Object.keys(descriptions).join(', '));
41
+ process.exitCode = 1;
42
+ return;
43
+ }
44
+
45
+ log(DOC_TOPICS[topic]);
46
+ }
47
+
48
+ module.exports = {
49
+ runDoc
50
+ };