@empirical-gauzza/cli 1.0.22 → 1.0.24
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 +63 -63
- package/bin/empirical.js +1 -1
- package/lib/constants.js +14 -1
- package/lib/doc.js +1 -1
- package/lib/env.js +49 -49
- package/lib/installers/detect.js +47 -47
- package/lib/installers/hooks.js +3 -3
- package/lib/installers/skill-variants/SKILL.both.md +17 -1
- package/lib/installers/skill-variants/SKILL.cli.md +17 -1
- package/lib/installers/skill-variants/SKILL.mcp.md +13 -2
- package/lib/memoryCommands.js +42 -3
- package/lib/oauthClient.js +222 -222
- package/lib/policyCommands.js +144 -144
- package/lib/workspaceContext.js +47 -47
- package/package.json +1 -1
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
package/lib/constants.js
CHANGED
|
@@ -60,9 +60,22 @@ Save a new memory.
|
|
|
60
60
|
|
|
61
61
|
empirical memory record --category build --summary "Use ESM modules for all new packages" [--tags ts,node] [--node-type observation]
|
|
62
62
|
|
|
63
|
+
Passing text that contains code (backticks, \${...}, $(...)): do NOT put it in a double-quoted
|
|
64
|
+
\`--summary "..."\` -- the shell substitutes those away before the CLI runs and the write still
|
|
65
|
+
succeeds with the code silently missing. Use one of:
|
|
66
|
+
|
|
67
|
+
empirical memory record --category build --summary-file ./note.md --tags ts
|
|
68
|
+
empirical memory record --category build --summary - --tags ts <<'EOF'
|
|
69
|
+
Use \`createChat()\`; raw calls leak \${stage} and 400.
|
|
70
|
+
EOF
|
|
71
|
+
|
|
72
|
+
(a single-quoted heredoc delimiter, 'EOF', disables substitution). From an MCP-connected agent,
|
|
73
|
+
prefer the record_graph_memory tool, which takes JSON and never touches a shell.
|
|
74
|
+
|
|
63
75
|
Flags:
|
|
64
76
|
- \`--category\` (required unless --json-file): memory category
|
|
65
|
-
- \`--summary\` (required unless --json-file): memory summary text
|
|
77
|
+
- \`--summary\` (required unless --summary-file/--json-file): memory summary text; \`-\` reads stdin
|
|
78
|
+
- \`--summary-file\`: path to a file whose contents become the summary (safe for code)
|
|
66
79
|
- \`--tags\`: comma-separated tags
|
|
67
80
|
- \`--node-type\`: node type, default \`observation\`
|
|
68
81
|
- \`--json-file\`: path to JSON file with full memory payload
|
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/installers/detect.js
CHANGED
|
@@ -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
|
+
};
|
package/lib/installers/hooks.js
CHANGED
|
@@ -477,7 +477,7 @@ function main() {
|
|
|
477
477
|
appendLog('stop hook fired (pid=' + process.pid + ', platform=' + process.platform + ')');
|
|
478
478
|
|
|
479
479
|
console.log(JSON.stringify({
|
|
480
|
-
systemMessage: '[Empirical reminder] Before finishing: if this turn included a completed decision, a corrected mistake, or meaningful work worth remembering, record it now via the empirical-memory skill or \`empirical memory record
|
|
480
|
+
systemMessage: '[Empirical reminder] Before finishing: if this turn included a completed decision, a corrected mistake, or meaningful work worth remembering, record it now, one memory per finding (a fix, a root cause, a decision, a lesson), via the empirical-memory, empirical-work-history, or empirical-scar-tissue skill or \`empirical memory record\`. Local notes are not recorded. Do not wait to be asked.'
|
|
481
481
|
}));
|
|
482
482
|
process.exit(0);
|
|
483
483
|
}
|
|
@@ -545,7 +545,7 @@ function main() {
|
|
|
545
545
|
|
|
546
546
|
console.log(JSON.stringify({
|
|
547
547
|
decision: 'block',
|
|
548
|
-
reason: '[Empirical reminder] Before finishing: if this turn included a completed decision, a corrected mistake, or meaningful work worth remembering, record it now via the empirical-memory skill or \`empirical memory record
|
|
548
|
+
reason: '[Empirical reminder] Before finishing: if this turn included a completed decision, a corrected mistake, or meaningful work worth remembering, record it now, one memory per finding (a fix, a root cause, a decision, a lesson), via the empirical-memory, empirical-work-history, or empirical-scar-tissue skill or \`empirical memory record\`. Local notes are not recorded. Do not wait to be asked.'
|
|
549
549
|
}));
|
|
550
550
|
process.exit(0);
|
|
551
551
|
}
|
|
@@ -618,7 +618,7 @@ function main() {
|
|
|
618
618
|
process.stdout.write(JSON.stringify({
|
|
619
619
|
hookSpecificOutput: {
|
|
620
620
|
hookEventName: 'Stop',
|
|
621
|
-
additionalContext: '[Empirical] Record memory-worthy work now via empirical-memory skill.'
|
|
621
|
+
additionalContext: '[Empirical] Record memory-worthy work now: one memory per finding (a fix, a root cause, a decision, a lesson) via the empirical-memory, empirical-work-history, or empirical-scar-tissue skill. Local notes are not recorded.'
|
|
622
622
|
}
|
|
623
623
|
}) + '\\n');
|
|
624
624
|
process.exit(0);
|
|
@@ -49,7 +49,13 @@ If you are not sure whether a task qualifies, treat it as if it does.
|
|
|
49
49
|
results can be large. A slow response alone is API latency, not an outage; report an outage
|
|
50
50
|
only when the CLI returns an actual error, and use `empirical doctor` to verify connectivity.
|
|
51
51
|
|
|
52
|
-
3.
|
|
52
|
+
3. Scope recall to the project you are in. Derive a project tag from the repository name
|
|
53
|
+
(`git rev-parse --show-toplevel`, then the folder name) or, outside a repo, the working
|
|
54
|
+
directory name, lowercased with spaces as hyphens. Pass it as `tags: ["<project>"]` on every
|
|
55
|
+
`query_memories` call and add it to `tags` on every `record_graph_memory` call. If a scoped
|
|
56
|
+
query comes back with a `retrievalNote`, nothing from this project matched: the results are
|
|
57
|
+
from other projects, so use them as background only and say so if you rely on one.
|
|
58
|
+
4. If the CLI is unavailable, use the connected MCP `query_memories` tool. Use filters such as
|
|
53
59
|
`category`, `tags`, `nodeType`, and `monthKey` when they improve recall. For "recent/latest/
|
|
54
60
|
today" requests, prefer `list_memories` (CLI: `empirical memory list --recent`, see `empirical
|
|
55
61
|
doc memory`) over a match query. For exhaustive topic retrieval, use the topic as a tag and
|
|
@@ -86,6 +92,11 @@ mistake, a completed unit of meaningful work, or a convention the user stated? I
|
|
|
86
92
|
now — do not wait to be asked, and do not defer it because the current task feels done. An agent
|
|
87
93
|
that only records memory when explicitly told to defeats the purpose of this skill.
|
|
88
94
|
|
|
95
|
+
Two rules that are not optional:
|
|
96
|
+
|
|
97
|
+
- Record one memory per finding. A session that fixed three things records three memories, each with the fix phrase and the date in its summary. One combined summary is not recorded: it cannot be found by any question about one of its parts.
|
|
98
|
+
- Empirical first. A note in a local file, a scratchpad, or a host memory directory is not recorded; it is invisible to every other session, tool, and agent. Record in Empirical, then keep a local pointer if you want one.
|
|
99
|
+
|
|
89
100
|
- Record durable decisions, project conventions, preferences, plans, and meaningful reflections
|
|
90
101
|
with `record_graph_memory` or:
|
|
91
102
|
|
|
@@ -93,6 +104,11 @@ that only records memory when explicitly told to defeats the purpose of this ski
|
|
|
93
104
|
empirical memory record --category <category> --summary "<concise fact>" --tags <tag1,tag2>
|
|
94
105
|
```
|
|
95
106
|
|
|
107
|
+
**Code in the summary?** Never pass backticks or `${...}` inside a double-quoted `--summary`; the
|
|
108
|
+
shell substitutes them away before the CLI runs and the write still succeeds with the code gone.
|
|
109
|
+
Use `--summary-file <path>`, or `--summary -` with a single-quoted heredoc (`<<'EOF'`), or the MCP
|
|
110
|
+
`record_graph_memory` tool, which takes JSON and never touches a shell.
|
|
111
|
+
|
|
96
112
|
Both dedupe against near-duplicate existing memories and auto-link related ones by default; pass
|
|
97
113
|
`--no-dedupe` / `--no-auto-link` on the CLI only when you deliberately want to skip one of those.
|
|
98
114
|
|
|
@@ -44,7 +44,13 @@ If you are not sure whether a task qualifies, treat it as if it does.
|
|
|
44
44
|
Add `--category` or `--tags` when known. Omit `--pretty` for routine recall because graph-rich
|
|
45
45
|
results can be large. Use `empirical memory list --recent` (see `empirical doc memory` for the
|
|
46
46
|
exact flag) for "recent/latest/today" requests instead of a match query.
|
|
47
|
-
3.
|
|
47
|
+
3. Scope recall to the project you are in. Derive a project tag from the repository name
|
|
48
|
+
(`git rev-parse --show-toplevel`, then the folder name) or, outside a repo, the working
|
|
49
|
+
directory name, lowercased with spaces as hyphens. Add `--tags <project>` to every
|
|
50
|
+
`empirical memory query` and `empirical memory record`. If a scoped query returns nothing,
|
|
51
|
+
retry without `--tags` and treat what comes back as background from other projects, not as
|
|
52
|
+
facts about this one.
|
|
53
|
+
4. A slow response alone is API latency, not an outage; report an outage only when the CLI returns
|
|
48
54
|
an actual error, and use `empirical doctor` to verify connectivity.
|
|
49
55
|
|
|
50
56
|
## Follow the current user policy
|
|
@@ -76,6 +82,11 @@ mistake, a completed unit of meaningful work, or a convention the user stated? I
|
|
|
76
82
|
now — do not wait to be asked, and do not defer it because the current task feels done. An agent
|
|
77
83
|
that only records memory when explicitly told to defeats the purpose of this skill.
|
|
78
84
|
|
|
85
|
+
Two rules that are not optional:
|
|
86
|
+
|
|
87
|
+
- Record one memory per finding. A session that fixed three things records three memories, each with the fix phrase and the date in its summary. One combined summary is not recorded: it cannot be found by any question about one of its parts.
|
|
88
|
+
- Empirical first. A note in a local file, a scratchpad, or a host memory directory is not recorded; it is invisible to every other session, tool, and agent. Record in Empirical, then keep a local pointer if you want one.
|
|
89
|
+
|
|
79
90
|
- Record durable decisions, project conventions, preferences, plans, and meaningful reflections
|
|
80
91
|
with:
|
|
81
92
|
|
|
@@ -83,6 +94,11 @@ that only records memory when explicitly told to defeats the purpose of this ski
|
|
|
83
94
|
empirical memory record --category <category> --summary "<concise fact>" --tags <tag1,tag2>
|
|
84
95
|
```
|
|
85
96
|
|
|
97
|
+
**Code in the summary?** Never pass backticks or `${...}` inside a double-quoted `--summary`; the
|
|
98
|
+
shell substitutes them away before the CLI runs and the write still succeeds with the code gone.
|
|
99
|
+
Use `--summary-file <path>`, or `--summary -` with a single-quoted heredoc (`<<'EOF'`), or the MCP
|
|
100
|
+
`record_graph_memory` tool, which takes JSON and never touches a shell.
|
|
101
|
+
|
|
86
102
|
This command dedupes against near-duplicate existing memories and auto-links related ones by
|
|
87
103
|
default, the same as any other Empirical client — you do not need a separate duplicate check
|
|
88
104
|
first. Pass `--no-dedupe` or `--no-auto-link` only when you specifically want to bypass one of
|
|
@@ -29,11 +29,17 @@ If you are not sure whether a task qualifies, treat it as if it does.
|
|
|
29
29
|
("query when it seems useful") the right frame.
|
|
30
30
|
2. If you have not used Empirical's MCP tools yet this session, call `memory_usage_guide` once to
|
|
31
31
|
confirm current tool names, filters, and pagination behavior before relying on memory.
|
|
32
|
-
3.
|
|
32
|
+
3. Scope recall to the project you are in. Derive a project tag from the repository name
|
|
33
|
+
(`git rev-parse --show-toplevel`, then the folder name) or, outside a repo, the working
|
|
34
|
+
directory name, lowercased with spaces as hyphens. Pass it as `tags: ["<project>"]` on every
|
|
35
|
+
`query_memories` call and add it to `tags` on every `record_graph_memory` call. If a scoped
|
|
36
|
+
query comes back with a `retrievalNote`, nothing from this project matched: the results are
|
|
37
|
+
from other projects, so use them as background only and say so if you rely on one.
|
|
38
|
+
4. Use the `query_memories` MCP tool for recall. Use filters such as `category`, `tags`,
|
|
33
39
|
`nodeType`, and `monthKey` when they improve recall. For "recent/latest/today" requests, prefer
|
|
34
40
|
`list_memories` instead — it is the tool built for that shape of request. For exhaustive topic
|
|
35
41
|
retrieval, use the topic as a tag and follow pagination.
|
|
36
|
-
|
|
42
|
+
5. A slow response alone is API latency, not an outage; report an outage only when a tool call
|
|
37
43
|
returns an actual error, and use `get_empirical_policy` or a retry to verify connectivity before
|
|
38
44
|
escalating.
|
|
39
45
|
|
|
@@ -66,6 +72,11 @@ mistake, a completed unit of meaningful work, or a convention the user stated? I
|
|
|
66
72
|
now — do not wait to be asked, and do not defer it because the current task feels done. An agent
|
|
67
73
|
that only records memory when explicitly told to defeats the purpose of this skill.
|
|
68
74
|
|
|
75
|
+
Two rules that are not optional:
|
|
76
|
+
|
|
77
|
+
- Record one memory per finding. A session that fixed three things records three memories, each with the fix phrase and the date in its summary. One combined summary is not recorded: it cannot be found by any question about one of its parts.
|
|
78
|
+
- Empirical first. A note in a local file, a scratchpad, or a host memory directory is not recorded; it is invisible to every other session, tool, and agent. Record in Empirical, then keep a local pointer if you want one.
|
|
79
|
+
|
|
69
80
|
- Record durable decisions, project conventions, preferences, plans, and meaningful reflections
|
|
70
81
|
with the `record_graph_memory` MCP tool. It runs write-intent classification, dedupe, and
|
|
71
82
|
auto-linking automatically — you do not need to check for duplicates yourself first.
|
package/lib/memoryCommands.js
CHANGED
|
@@ -47,7 +47,7 @@ const MEMORY_SUBCOMMAND_USAGE = {
|
|
|
47
47
|
query: 'Usage: empirical memory query --match <query> [--top-k 3] [--category <cat>] [--tags <t1,t2>] [--pretty] [--format table]',
|
|
48
48
|
get: 'Usage: empirical memory get --memory-id <id> [--pretty]',
|
|
49
49
|
list: 'Usage: empirical memory list [--category <cat>] [--tags <t1,t2>] [--start-date <ISO>] [--end-date <ISO>] [--limit 20] [--cursor <cursor>] [--oldest] [--pretty] [--format table]',
|
|
50
|
-
record: 'Usage: empirical memory record --category <cat> --summary <text> [--tags t1,t2] [--node-type observation] [--json-file <path>] [--no-dedupe] [--no-auto-link]',
|
|
50
|
+
record: 'Usage: empirical memory record --category <cat> (--summary <text> | --summary - | --summary-file <path>) [--tags t1,t2] [--node-type observation] [--json-file <path>] [--no-dedupe] [--no-auto-link]',
|
|
51
51
|
note: 'Usage: empirical memory note --text <note> (--match <query> | --memory-id <id>) [--min-score 0.35]',
|
|
52
52
|
update: 'Usage: empirical memory update (--match <query> | --memory-id <id>) (--patch-json <json> | --json-file <path>) [--min-score 0.35]',
|
|
53
53
|
delete: 'Usage: empirical memory delete (--match <query> | --memory-id <id>) [--confirm] [--min-score 0.35]\nUsage: empirical memory delete (--tag <t> | --category <c> | --all) --confirm',
|
|
@@ -290,6 +290,33 @@ async function runMemoryList(args) {
|
|
|
290
290
|
log(pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// Shell substitution silently eats code out of a double-quoted --summary: backticks become
|
|
294
|
+
// command substitution and ${...} becomes variable expansion BEFORE the CLI ever sees the
|
|
295
|
+
// text, and the write still succeeds, so the memory looks fine with the code gone. Two
|
|
296
|
+
// defenses: (1) inputs that never pass through shell quoting -- `--summary-file <path>` and
|
|
297
|
+
// `--summary -` (stdin, pair it with a single-quoted heredoc); (2) a warning when an inline
|
|
298
|
+
// summary carries the residue of an eaten substitution.
|
|
299
|
+
function resolveSummaryInput(args, { readFile = (p) => fs.readFileSync(path.resolve(p), 'utf8'), readStdin = () => fs.readFileSync(0, 'utf8') } = {}) {
|
|
300
|
+
const summaryFile = parseOption(args, '--summary-file', '');
|
|
301
|
+
if (summaryFile) {
|
|
302
|
+
return { summary: String(readFile(summaryFile)).replace(/\r?\n$/, ''), source: 'file' };
|
|
303
|
+
}
|
|
304
|
+
const inline = parseOption(args, '--summary', '');
|
|
305
|
+
if (inline === '-') {
|
|
306
|
+
return { summary: String(readStdin()).replace(/\r?\n$/, ''), source: 'stdin' };
|
|
307
|
+
}
|
|
308
|
+
return { summary: inline, source: inline ? 'inline' : 'none' };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function detectShellMangling(summary) {
|
|
312
|
+
const text = String(summary || '');
|
|
313
|
+
const reasons = [];
|
|
314
|
+
if (/``/.test(text)) reasons.push('an empty backtick pair (a `command` was probably substituted away)');
|
|
315
|
+
if (/\$(\s|$|[^A-Za-z0-9_{(])/.test(text) && !/\$\d/.test(text)) reasons.push('a lone "$" (a ${VAR} or $(cmd) was probably expanded to nothing)');
|
|
316
|
+
if (/\$\{\s*\}/.test(text)) reasons.push('an empty ${} placeholder');
|
|
317
|
+
return reasons;
|
|
318
|
+
}
|
|
319
|
+
|
|
293
320
|
async function runMemoryRecord(args) {
|
|
294
321
|
const jsonFile = parseOption(args, '--json-file', '');
|
|
295
322
|
let payload;
|
|
@@ -303,10 +330,20 @@ async function runMemoryRecord(args) {
|
|
|
303
330
|
}
|
|
304
331
|
} else {
|
|
305
332
|
const category = parseOption(args, '--category', '');
|
|
306
|
-
const summary =
|
|
333
|
+
const { summary, source: summarySource } = resolveSummaryInput(args);
|
|
334
|
+
|
|
335
|
+
if (summarySource === 'inline') {
|
|
336
|
+
const mangled = detectShellMangling(summary);
|
|
337
|
+
if (mangled.length) {
|
|
338
|
+
warn(JSON.stringify({
|
|
339
|
+
warning: 'possible_shell_substitution',
|
|
340
|
+
message: 'The summary looks like the shell altered it before the CLI saw it: ' + mangled.join('; ') + '. Recording anyway. For text containing backticks or ${...}, use --summary-file <path>, or --summary - with a single-quoted heredoc, or the MCP record_graph_memory tool.'
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
307
344
|
|
|
308
345
|
if (!category || !summary) {
|
|
309
|
-
warn('Usage: empirical memory record --category <cat> --summary <text> [--tags t1,t2] [--node-type observation] [--no-dedupe] [--no-auto-link]');
|
|
346
|
+
warn('Usage: empirical memory record --category <cat> (--summary <text> | --summary - | --summary-file <path>) [--tags t1,t2] [--node-type observation] [--json-file <path>] [--no-dedupe] [--no-auto-link]');
|
|
310
347
|
warn('Valid categories: ' + MEMORY_CATEGORIES.join(', '));
|
|
311
348
|
process.exit(1);
|
|
312
349
|
}
|
|
@@ -902,6 +939,8 @@ async function runMemoryCommand(subArgs) {
|
|
|
902
939
|
}
|
|
903
940
|
|
|
904
941
|
module.exports = {
|
|
942
|
+
resolveSummaryInput,
|
|
943
|
+
detectShellMangling,
|
|
905
944
|
withCliAnalyticsContext,
|
|
906
945
|
resolveMemoryByQuery,
|
|
907
946
|
printMemoryResolveError,
|