@empirical-gauzza/cli 1.0.36 → 1.0.38
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/bin/empirical.js +6 -0
- package/lib/attachmentCommands.js +388 -0
- package/lib/auth.js +16 -2
- package/lib/blueprintCommands.js +23 -3
- package/lib/constants.js +722 -670
- package/lib/empiricalTokensFormat.js +68 -0
- package/lib/help.js +23 -1
- package/lib/http.js +171 -3
- package/lib/installWizard.js +3 -1
- package/lib/installers/coreMemoryBlock.js +2 -1
- package/lib/installers/hooks.js +1312 -1312
- package/lib/installers/nativePlugin.js +1 -1
- package/lib/installers/promptBlocks.js +9 -0
- package/lib/installers/skill-variants/SKILL.both.md +20 -0
- package/lib/installers/skill-variants/SKILL.cli.md +231 -221
- package/lib/installers/skill-variants/SKILL.mcp.md +18 -0
- package/lib/memoryCommands.js +1267 -1220
- package/lib/memoryResolve.js +112 -0
- package/lib/pluginCommands.js +3 -2
- package/lib/setupCommands.js +751 -31
- package/lib/usageCommands.js +52 -0
- package/lib/workspaceCommands.js +72 -5
- package/lib/workspaceContext.js +176 -176
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Resolving "which memory did the user mean" from a --match query, shared by every memory
|
|
4
|
+
// subcommand that accepts --match. It lives in its own module so attachmentCommands.js can use
|
|
5
|
+
// it without a require cycle back through memoryCommands.js; memoryCommands.js re-exports the
|
|
6
|
+
// three names so existing importers and tests are unaffected.
|
|
7
|
+
|
|
8
|
+
const { warn } = require('./ui');
|
|
9
|
+
const { httpJson } = require('./http');
|
|
10
|
+
const { parseOption } = require('./env');
|
|
11
|
+
const { buildApiAuthHeaders } = require('./auth');
|
|
12
|
+
|
|
13
|
+
async function resolveMemoryByQuery(match, minScore, accessToken, baseUrl, filters) {
|
|
14
|
+
const queryUrl = new URL('/api/v1/memories/query', baseUrl).toString();
|
|
15
|
+
const body = { query: match, topK: 5 };
|
|
16
|
+
if (filters && Object.keys(filters).length) body.filters = filters;
|
|
17
|
+
let response;
|
|
18
|
+
try {
|
|
19
|
+
response = await httpJson(queryUrl, 'POST', body, buildApiAuthHeaders(accessToken));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return { found: false, reason: 'error', message: error.message };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const results = (response && response.data && Array.isArray(response.data.results))
|
|
25
|
+
? response.data.results
|
|
26
|
+
: (Array.isArray(response) ? response : []);
|
|
27
|
+
|
|
28
|
+
if (!results.length) {
|
|
29
|
+
return { found: false, reason: 'no_results' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const scored = results.map((r) => ({
|
|
33
|
+
id: r.id,
|
|
34
|
+
summary: r.summary,
|
|
35
|
+
score: r.rankScore != null ? r.rankScore : (r.similarityScore != null ? r.similarityScore : 0)
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
scored.sort((a, b) => b.score - a.score);
|
|
39
|
+
|
|
40
|
+
// A caller-supplied filter (e.g. --tags) that narrows the server-side result set to exactly
|
|
41
|
+
// one memory is already a stronger disambiguation signal than the semantic score - trust it
|
|
42
|
+
// outright rather than falling through to the score check below. This matters because Atlas
|
|
43
|
+
// vector search has a real async indexing lag: a memory created moments ago can come back
|
|
44
|
+
// with score 0 (unindexed, not "not this one") even when a tag filter already proves it is
|
|
45
|
+
// the only candidate. Fixes a real bug found 2026-08-25 where delete --match failed on a
|
|
46
|
+
// memory `query` found instantly, because delete had no way to trust a filtered, unambiguous
|
|
47
|
+
// result over a stale/zero semantic score.
|
|
48
|
+
if (filters && Object.keys(filters).length && results.length === 1) {
|
|
49
|
+
const only = scored[0];
|
|
50
|
+
return { found: true, memory: { id: only.id, summary: only.summary, score: only.score } };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const top = scored[0];
|
|
54
|
+
const second = scored[1];
|
|
55
|
+
|
|
56
|
+
if (top.score < minScore) {
|
|
57
|
+
return { found: false, reason: 'low_score', candidates: scored.slice(0, 3) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (second && (top.score - second.score) <= 0.05) {
|
|
61
|
+
return { found: false, reason: 'ambiguous', candidates: scored.slice(0, 3) };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { found: true, memory: { id: top.id, summary: top.summary, score: top.score } };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --match resolution depends on Atlas vector search, which has a real async indexing lag:
|
|
68
|
+
// a memory created moments ago can be invisible to $vectorSearch even though it is already
|
|
69
|
+
// findable by tag. Letting --match commands pass --tags narrows the candidate pool directly
|
|
70
|
+
// instead of hoping semantic ranking surfaces the right one - the fix for a real bug found
|
|
71
|
+
// 2026-08-25 where delete --match returned unrelated score-0 candidates for a memory that
|
|
72
|
+
// `query` found instantly. Deliberately --tags (plural) only, not --category: `delete` already
|
|
73
|
+
// uses singular --tag/--category to mean "bulk delete everything in this category", so adding
|
|
74
|
+
// --category here would silently reroute a --match delete into a bulk delete instead of
|
|
75
|
+
// narrowing it - --tags never collides with that bulk-mode gate.
|
|
76
|
+
function parseMatchFilters(args) {
|
|
77
|
+
const tagsRaw = parseOption(args, '--tags', '');
|
|
78
|
+
const tags = tagsRaw ? tagsRaw.split(',').map((t) => t.trim()).filter(Boolean) : [];
|
|
79
|
+
const filters = {};
|
|
80
|
+
if (tags.length) filters.tags = tags;
|
|
81
|
+
return filters;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function printMemoryResolveError(result) {
|
|
85
|
+
if (result.reason === 'no_results') {
|
|
86
|
+
warn(JSON.stringify({ error: true, code: 'no_results', message: 'No memories matched the query.' }));
|
|
87
|
+
} else if (result.reason === 'low_score') {
|
|
88
|
+
warn(JSON.stringify({
|
|
89
|
+
error: true,
|
|
90
|
+
code: 'low_score',
|
|
91
|
+
message: 'Top result score was below --min-score threshold.',
|
|
92
|
+
candidates: result.candidates
|
|
93
|
+
}));
|
|
94
|
+
} else if (result.reason === 'ambiguous') {
|
|
95
|
+
warn(JSON.stringify({
|
|
96
|
+
error: true,
|
|
97
|
+
code: 'ambiguous',
|
|
98
|
+
message: 'Multiple memories matched with similar scores. Use --memory-id to target one directly.',
|
|
99
|
+
candidates: result.candidates
|
|
100
|
+
}));
|
|
101
|
+
} else if (result.reason === 'error') {
|
|
102
|
+
warn(JSON.stringify({ error: true, code: 'query_error', message: result.message }));
|
|
103
|
+
} else {
|
|
104
|
+
warn(JSON.stringify({ error: true, code: 'resolve_failed', message: 'Could not resolve memory.' }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
resolveMemoryByQuery,
|
|
110
|
+
parseMatchFilters,
|
|
111
|
+
printMemoryResolveError
|
|
112
|
+
};
|
package/lib/pluginCommands.js
CHANGED
|
@@ -4,7 +4,7 @@ const { log, warn, uiHeader, uiSuccess } = require('./ui');
|
|
|
4
4
|
const { detectInstallTargets } = require('./installers/detect');
|
|
5
5
|
const {
|
|
6
6
|
installNativePlugin,
|
|
7
|
-
OPTIONAL_PLUGIN_NAMES = ['empirical-work-history', 'empirical-scar-tissue', 'empirical-todo']
|
|
7
|
+
OPTIONAL_PLUGIN_NAMES = ['empirical-work-history', 'empirical-scar-tissue', 'empirical-todo', 'empirical-voice']
|
|
8
8
|
} = require('./installers/nativePlugin');
|
|
9
9
|
const { checkNativePluginUpdates } = require('./pluginUpdateCheck');
|
|
10
10
|
const { addInstalledHosts } = require('./deviceConfig');
|
|
@@ -35,6 +35,7 @@ function resolvePluginNames(args = []) {
|
|
|
35
35
|
if (args.includes('--all-optional') || args.includes('--work-history')) names.push('empirical-work-history');
|
|
36
36
|
if (args.includes('--all-optional') || args.includes('--scar-tissue')) names.push('empirical-scar-tissue');
|
|
37
37
|
if (args.includes('--all-optional') || args.includes('--todo')) names.push('empirical-todo');
|
|
38
|
+
if (args.includes('--all-optional') || args.includes('--voice')) names.push('empirical-voice');
|
|
38
39
|
return names.filter((name) => name === 'empirical-memory' || OPTIONAL_PLUGIN_NAMES.includes(name));
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -82,7 +83,7 @@ async function runPluginCommand(args = []) {
|
|
|
82
83
|
}
|
|
83
84
|
log('');
|
|
84
85
|
log('Native plugin installation does not configure MCP or credentials.');
|
|
85
|
-
log('Optional packages: --work-history, --scar-tissue, --todo, or --all-optional.');
|
|
86
|
+
log('Optional packages: --work-history, --scar-tissue, --todo, --voice, or --all-optional.');
|
|
86
87
|
log('Use `empirical auth login` for normal CLI OAuth, or the full `empirical install` flow for MCP setup.');
|
|
87
88
|
}
|
|
88
89
|
|