@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 +63 -63
- package/bin/empirical.js +7 -1
- package/lib/alertBanner.js +44 -0
- package/lib/alertCommands.js +62 -0
- package/lib/constants.js +45 -1
- package/lib/doc.js +1 -1
- package/lib/env.js +49 -49
- package/lib/http.js +14 -1
- package/lib/installers/coreMemoryBlock.js +5 -0
- package/lib/installers/detect.js +47 -47
- package/lib/installers/hooks.js +1095 -1075
- package/lib/installers/skill-variants/SKILL.both.md +36 -0
- package/lib/installers/skill-variants/SKILL.cli.md +33 -0
- package/lib/installers/skill-variants/SKILL.mcp.md +34 -0
- package/lib/memoryCommands.js +111 -16
- package/lib/oauthClient.js +222 -222
- package/lib/policyCommands.js +144 -144
- package/lib/workspaceContext.js +47 -47
- package/package.json +1 -1
package/lib/policyCommands.js
CHANGED
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { log, warn } = require('./ui');
|
|
4
|
-
const { httpJson, printApiError } = require('./http');
|
|
5
|
-
const { parseOption } = require('./env');
|
|
6
|
-
const { requireAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
|
|
7
|
-
const { readPolicyCache, writePolicyCache } = require('./deviceConfig');
|
|
8
|
-
|
|
9
|
-
const POLICY_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
10
|
-
|
|
11
|
-
function policyUrl(baseUrl) {
|
|
12
|
-
return new URL('/api/v1/users/me/empirical-policy', baseUrl).toString();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function isPolicyCacheFresh(cache, now = Date.now()) {
|
|
16
|
-
return Boolean(
|
|
17
|
-
cache &&
|
|
18
|
-
cache.policy &&
|
|
19
|
-
cache.updatedAt &&
|
|
20
|
-
now - new Date(cache.cachedAt || 0).getTime() < POLICY_CACHE_TTL_MS
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function fetchPolicy({ force = false } = {}) {
|
|
25
|
-
const cached = readPolicyCache();
|
|
26
|
-
if (!force && isPolicyCacheFresh(cached)) return { ...cached, source: 'cache' };
|
|
27
|
-
|
|
28
|
-
const accessToken = requireAccessToken();
|
|
29
|
-
const response = await httpJson(
|
|
30
|
-
policyUrl(resolveApiBaseUrl()),
|
|
31
|
-
'GET',
|
|
32
|
-
null,
|
|
33
|
-
buildApiAuthHeaders(accessToken)
|
|
34
|
-
);
|
|
35
|
-
const data = response?.data || response;
|
|
36
|
-
const next = {
|
|
37
|
-
policy: data.policy || {},
|
|
38
|
-
policyVersion: data.policyVersion || 1,
|
|
39
|
-
updatedAt: data.updatedAt || null,
|
|
40
|
-
cachedAt: new Date().toISOString()
|
|
41
|
-
};
|
|
42
|
-
writePolicyCache(next);
|
|
43
|
-
return { ...next, source: 'remote' };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function policyFromInstallerOptions(options = {}) {
|
|
47
|
-
return {
|
|
48
|
-
memoryFirstWorkflows: options.requireMemoryInWorkflows === false
|
|
49
|
-
? []
|
|
50
|
-
: (options.requiredWorkflows || []),
|
|
51
|
-
allowDiscretionaryUse: options.allowDiscretionaryMemoryUsage !== false,
|
|
52
|
-
stopOnMemoryFailure: options.stopOnMemoryToolFailure !== false,
|
|
53
|
-
rememberCodingStyle: options.rememberCodingStyle !== false,
|
|
54
|
-
rememberArchitectureDecisions: options.rememberArchitectureDecisions !== false,
|
|
55
|
-
recordWorkHistory: options.recordWorkHistory !== false,
|
|
56
|
-
recordScarTissue: options.recordScarTissue !== false,
|
|
57
|
-
recordTodos: options.recordTodos === true,
|
|
58
|
-
storeInstructionArtifacts: options.storeInstructionArtifacts === true,
|
|
59
|
-
checkMemoryBeforeAskingContext: options.checkMemoryBeforeAskingContext !== false,
|
|
60
|
-
durableWritesOnly: options.durableWritesOnly !== false
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function savePolicy(policy, accessTokenOverride = null) {
|
|
65
|
-
const accessToken = accessTokenOverride || requireAccessToken();
|
|
66
|
-
const response = await httpJson(
|
|
67
|
-
policyUrl(resolveApiBaseUrl()),
|
|
68
|
-
'PATCH',
|
|
69
|
-
{ policy },
|
|
70
|
-
buildApiAuthHeaders(accessToken)
|
|
71
|
-
);
|
|
72
|
-
const data = response?.data || response;
|
|
73
|
-
writePolicyCache({
|
|
74
|
-
policy: data.policy || {},
|
|
75
|
-
policyVersion: data.policyVersion || 1,
|
|
76
|
-
updatedAt: data.updatedAt || null,
|
|
77
|
-
cachedAt: new Date().toISOString()
|
|
78
|
-
});
|
|
79
|
-
return data;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function runPolicyCommand(args = []) {
|
|
83
|
-
const subcommand = args[0] || 'show';
|
|
84
|
-
if (subcommand === 'show') {
|
|
85
|
-
try {
|
|
86
|
-
const result = await fetchPolicy({ force: args.includes('--refresh') });
|
|
87
|
-
log(JSON.stringify(result, null, 2));
|
|
88
|
-
} catch (error) {
|
|
89
|
-
printApiError(error);
|
|
90
|
-
process.exitCode = 1;
|
|
91
|
-
}
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (subcommand === 'set') {
|
|
96
|
-
const raw = parseOption(args, '--json', '');
|
|
97
|
-
if (!raw) {
|
|
98
|
-
warn('Usage: empirical policy set --json \'{"allowDiscretionaryUse":false}\'');
|
|
99
|
-
process.exitCode = 1;
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
let policy;
|
|
103
|
-
try {
|
|
104
|
-
policy = JSON.parse(raw);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
warn(`Invalid policy JSON: ${error.message}`);
|
|
107
|
-
process.exitCode = 1;
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const accessToken = requireAccessToken();
|
|
112
|
-
const response = await httpJson(
|
|
113
|
-
policyUrl(resolveApiBaseUrl()),
|
|
114
|
-
'PATCH',
|
|
115
|
-
{ policy },
|
|
116
|
-
buildApiAuthHeaders(accessToken)
|
|
117
|
-
);
|
|
118
|
-
const data = response?.data || response;
|
|
119
|
-
writePolicyCache({
|
|
120
|
-
policy: data.policy || {},
|
|
121
|
-
policyVersion: data.policyVersion || 1,
|
|
122
|
-
updatedAt: data.updatedAt || null,
|
|
123
|
-
cachedAt: new Date().toISOString()
|
|
124
|
-
});
|
|
125
|
-
log(JSON.stringify(data, null, 2));
|
|
126
|
-
} catch (error) {
|
|
127
|
-
printApiError(error);
|
|
128
|
-
process.exitCode = 1;
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
warn('Usage: empirical policy <show [--refresh]|set --json <policy>>');
|
|
134
|
-
process.exitCode = 1;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
module.exports = {
|
|
138
|
-
POLICY_CACHE_TTL_MS,
|
|
139
|
-
isPolicyCacheFresh,
|
|
140
|
-
policyFromInstallerOptions,
|
|
141
|
-
fetchPolicy,
|
|
142
|
-
savePolicy,
|
|
143
|
-
runPolicyCommand
|
|
144
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { log, warn } = require('./ui');
|
|
4
|
+
const { httpJson, printApiError } = require('./http');
|
|
5
|
+
const { parseOption } = require('./env');
|
|
6
|
+
const { requireAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
|
|
7
|
+
const { readPolicyCache, writePolicyCache } = require('./deviceConfig');
|
|
8
|
+
|
|
9
|
+
const POLICY_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
10
|
+
|
|
11
|
+
function policyUrl(baseUrl) {
|
|
12
|
+
return new URL('/api/v1/users/me/empirical-policy', baseUrl).toString();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isPolicyCacheFresh(cache, now = Date.now()) {
|
|
16
|
+
return Boolean(
|
|
17
|
+
cache &&
|
|
18
|
+
cache.policy &&
|
|
19
|
+
cache.updatedAt &&
|
|
20
|
+
now - new Date(cache.cachedAt || 0).getTime() < POLICY_CACHE_TTL_MS
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function fetchPolicy({ force = false } = {}) {
|
|
25
|
+
const cached = readPolicyCache();
|
|
26
|
+
if (!force && isPolicyCacheFresh(cached)) return { ...cached, source: 'cache' };
|
|
27
|
+
|
|
28
|
+
const accessToken = requireAccessToken();
|
|
29
|
+
const response = await httpJson(
|
|
30
|
+
policyUrl(resolveApiBaseUrl()),
|
|
31
|
+
'GET',
|
|
32
|
+
null,
|
|
33
|
+
buildApiAuthHeaders(accessToken)
|
|
34
|
+
);
|
|
35
|
+
const data = response?.data || response;
|
|
36
|
+
const next = {
|
|
37
|
+
policy: data.policy || {},
|
|
38
|
+
policyVersion: data.policyVersion || 1,
|
|
39
|
+
updatedAt: data.updatedAt || null,
|
|
40
|
+
cachedAt: new Date().toISOString()
|
|
41
|
+
};
|
|
42
|
+
writePolicyCache(next);
|
|
43
|
+
return { ...next, source: 'remote' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function policyFromInstallerOptions(options = {}) {
|
|
47
|
+
return {
|
|
48
|
+
memoryFirstWorkflows: options.requireMemoryInWorkflows === false
|
|
49
|
+
? []
|
|
50
|
+
: (options.requiredWorkflows || []),
|
|
51
|
+
allowDiscretionaryUse: options.allowDiscretionaryMemoryUsage !== false,
|
|
52
|
+
stopOnMemoryFailure: options.stopOnMemoryToolFailure !== false,
|
|
53
|
+
rememberCodingStyle: options.rememberCodingStyle !== false,
|
|
54
|
+
rememberArchitectureDecisions: options.rememberArchitectureDecisions !== false,
|
|
55
|
+
recordWorkHistory: options.recordWorkHistory !== false,
|
|
56
|
+
recordScarTissue: options.recordScarTissue !== false,
|
|
57
|
+
recordTodos: options.recordTodos === true,
|
|
58
|
+
storeInstructionArtifacts: options.storeInstructionArtifacts === true,
|
|
59
|
+
checkMemoryBeforeAskingContext: options.checkMemoryBeforeAskingContext !== false,
|
|
60
|
+
durableWritesOnly: options.durableWritesOnly !== false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function savePolicy(policy, accessTokenOverride = null) {
|
|
65
|
+
const accessToken = accessTokenOverride || requireAccessToken();
|
|
66
|
+
const response = await httpJson(
|
|
67
|
+
policyUrl(resolveApiBaseUrl()),
|
|
68
|
+
'PATCH',
|
|
69
|
+
{ policy },
|
|
70
|
+
buildApiAuthHeaders(accessToken)
|
|
71
|
+
);
|
|
72
|
+
const data = response?.data || response;
|
|
73
|
+
writePolicyCache({
|
|
74
|
+
policy: data.policy || {},
|
|
75
|
+
policyVersion: data.policyVersion || 1,
|
|
76
|
+
updatedAt: data.updatedAt || null,
|
|
77
|
+
cachedAt: new Date().toISOString()
|
|
78
|
+
});
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function runPolicyCommand(args = []) {
|
|
83
|
+
const subcommand = args[0] || 'show';
|
|
84
|
+
if (subcommand === 'show') {
|
|
85
|
+
try {
|
|
86
|
+
const result = await fetchPolicy({ force: args.includes('--refresh') });
|
|
87
|
+
log(JSON.stringify(result, null, 2));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
printApiError(error);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (subcommand === 'set') {
|
|
96
|
+
const raw = parseOption(args, '--json', '');
|
|
97
|
+
if (!raw) {
|
|
98
|
+
warn('Usage: empirical policy set --json \'{"allowDiscretionaryUse":false}\'');
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let policy;
|
|
103
|
+
try {
|
|
104
|
+
policy = JSON.parse(raw);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
warn(`Invalid policy JSON: ${error.message}`);
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const accessToken = requireAccessToken();
|
|
112
|
+
const response = await httpJson(
|
|
113
|
+
policyUrl(resolveApiBaseUrl()),
|
|
114
|
+
'PATCH',
|
|
115
|
+
{ policy },
|
|
116
|
+
buildApiAuthHeaders(accessToken)
|
|
117
|
+
);
|
|
118
|
+
const data = response?.data || response;
|
|
119
|
+
writePolicyCache({
|
|
120
|
+
policy: data.policy || {},
|
|
121
|
+
policyVersion: data.policyVersion || 1,
|
|
122
|
+
updatedAt: data.updatedAt || null,
|
|
123
|
+
cachedAt: new Date().toISOString()
|
|
124
|
+
});
|
|
125
|
+
log(JSON.stringify(data, null, 2));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
printApiError(error);
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
warn('Usage: empirical policy <show [--refresh]|set --json <policy>>');
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
POLICY_CACHE_TTL_MS,
|
|
139
|
+
isPolicyCacheFresh,
|
|
140
|
+
policyFromInstallerOptions,
|
|
141
|
+
fetchPolicy,
|
|
142
|
+
savePolicy,
|
|
143
|
+
runPolicyCommand
|
|
144
|
+
};
|
package/lib/workspaceContext.js
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { httpJson } = require('./http');
|
|
6
|
-
const { getEnv } = require('./env');
|
|
7
|
-
const { resolveAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
|
|
8
|
-
const { readStoredOAuthClient } = require('./oauthClient');
|
|
9
|
-
const { readJsonIfExists, writeFile, ensureDir } = require('./fsUtils');
|
|
10
|
-
|
|
11
|
-
const WORKSPACE_CONTEXT_TIMEOUT_MS = 2000;
|
|
12
|
-
const SELECTED_WORKSPACE_PATH = path.join(os.homedir(), '.empirical', 'workspace-selection.json');
|
|
13
|
-
|
|
14
|
-
function readSelectedWorkspace() {
|
|
15
|
-
const value = readJsonIfExists(SELECTED_WORKSPACE_PATH);
|
|
16
|
-
return value && typeof value.workspaceId === 'string' ? value : null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function writeSelectedWorkspace(workspace) {
|
|
20
|
-
ensureDir(path.dirname(SELECTED_WORKSPACE_PATH));
|
|
21
|
-
writeFile(SELECTED_WORKSPACE_PATH, `${JSON.stringify(workspace, null, 2)}\n`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function clearSelectedWorkspace() {
|
|
25
|
-
const fs = require('fs');
|
|
26
|
-
if (fs.existsSync(SELECTED_WORKSPACE_PATH)) fs.unlinkSync(SELECTED_WORKSPACE_PATH);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function withSelectedWorkspaceContext(fn) {
|
|
30
|
-
const stored = readSelectedWorkspace();
|
|
31
|
-
if (stored?.workspaceId) {
|
|
32
|
-
const previousWorkspaceId = globalThis.__empiricalCliWorkspaceId || null;
|
|
33
|
-
globalThis.__empiricalCliWorkspaceId = stored.workspaceId;
|
|
34
|
-
try {
|
|
35
|
-
return await fn();
|
|
36
|
-
} finally {
|
|
37
|
-
globalThis.__empiricalCliWorkspaceId = previousWorkspaceId;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const accessToken = resolveAccessToken();
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { httpJson } = require('./http');
|
|
6
|
+
const { getEnv } = require('./env');
|
|
7
|
+
const { resolveAccessToken, resolveApiBaseUrl, buildApiAuthHeaders } = require('./auth');
|
|
8
|
+
const { readStoredOAuthClient } = require('./oauthClient');
|
|
9
|
+
const { readJsonIfExists, writeFile, ensureDir } = require('./fsUtils');
|
|
10
|
+
|
|
11
|
+
const WORKSPACE_CONTEXT_TIMEOUT_MS = 2000;
|
|
12
|
+
const SELECTED_WORKSPACE_PATH = path.join(os.homedir(), '.empirical', 'workspace-selection.json');
|
|
13
|
+
|
|
14
|
+
function readSelectedWorkspace() {
|
|
15
|
+
const value = readJsonIfExists(SELECTED_WORKSPACE_PATH);
|
|
16
|
+
return value && typeof value.workspaceId === 'string' ? value : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function writeSelectedWorkspace(workspace) {
|
|
20
|
+
ensureDir(path.dirname(SELECTED_WORKSPACE_PATH));
|
|
21
|
+
writeFile(SELECTED_WORKSPACE_PATH, `${JSON.stringify(workspace, null, 2)}\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function clearSelectedWorkspace() {
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
if (fs.existsSync(SELECTED_WORKSPACE_PATH)) fs.unlinkSync(SELECTED_WORKSPACE_PATH);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function withSelectedWorkspaceContext(fn) {
|
|
30
|
+
const stored = readSelectedWorkspace();
|
|
31
|
+
if (stored?.workspaceId) {
|
|
32
|
+
const previousWorkspaceId = globalThis.__empiricalCliWorkspaceId || null;
|
|
33
|
+
globalThis.__empiricalCliWorkspaceId = stored.workspaceId;
|
|
34
|
+
try {
|
|
35
|
+
return await fn();
|
|
36
|
+
} finally {
|
|
37
|
+
globalThis.__empiricalCliWorkspaceId = previousWorkspaceId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const accessToken = resolveAccessToken();
|
|
42
42
|
const clientId = getEnv('EMPIRICAL_CLIENT_ID') || readStoredOAuthClient()?.clientId;
|
|
43
43
|
if (!accessToken || !clientId) return fn();
|
|
44
44
|
|
|
@@ -68,9 +68,9 @@ async function withSelectedWorkspaceContext(fn) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
module.exports = {
|
|
72
|
-
withSelectedWorkspaceContext,
|
|
73
|
-
readSelectedWorkspace,
|
|
74
|
-
writeSelectedWorkspace,
|
|
75
|
-
clearSelectedWorkspace
|
|
76
|
-
};
|
|
71
|
+
module.exports = {
|
|
72
|
+
withSelectedWorkspaceContext,
|
|
73
|
+
readSelectedWorkspace,
|
|
74
|
+
writeSelectedWorkspace,
|
|
75
|
+
clearSelectedWorkspace
|
|
76
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empirical-gauzza/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "Persistent cross-tool memory for AI agents. Give Claude Code, Codex, Copilot, Cursor, and ChatGPT one shared memory: save project context once, recall it everywhere via CLI or MCP.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"memory",
|