@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,128 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { log } = require('../ui');
6
+ const { ensureDir, writeFile, readJsonIfExists } = require('../fsUtils');
7
+
8
+ const CLAUDE_HOOK_SESSION_START_SOURCE = `#!/usr/bin/env node
9
+ 'use strict';
10
+
11
+ var spawnSync = require('child_process').spawnSync;
12
+
13
+ function commandExists(cmd) {
14
+ var probe = process.platform === 'win32'
15
+ ? spawnSync('where', [cmd], { stdio: 'ignore', shell: true })
16
+ : spawnSync('which', [cmd], { stdio: 'ignore' });
17
+ return probe.status === 0;
18
+ }
19
+
20
+ function main() {
21
+ if (!commandExists('empirical')) {
22
+ process.exit(0);
23
+ }
24
+
25
+ var result = spawnSync('empirical', [
26
+ 'memory', 'query',
27
+ '--match', 'project context, conventions, recent decisions, user preferences',
28
+ '--top-k', '8'
29
+ ], {
30
+ encoding: 'utf8',
31
+ timeout: 10000,
32
+ shell: process.platform === 'win32'
33
+ });
34
+
35
+ if (!result || result.status !== 0 || !result.stdout) {
36
+ process.exit(0);
37
+ }
38
+
39
+ var results;
40
+ try {
41
+ results = JSON.parse(result.stdout);
42
+ } catch (e) {
43
+ process.exit(0);
44
+ }
45
+
46
+ if (!Array.isArray(results) || results.length === 0) {
47
+ process.exit(0);
48
+ }
49
+
50
+ var lines = results.slice(0, 8).map(function (r) {
51
+ var category = r && r.category ? r.category : 'memory';
52
+ var summary = r && r.summary ? r.summary : '';
53
+ return '- [' + category + '] ' + summary;
54
+ });
55
+
56
+ console.log('Empirical memory context for this session:\\n' + lines.join('\\n'));
57
+ process.exit(0);
58
+ }
59
+
60
+ main();
61
+ `;
62
+
63
+ function getEmpiricalHooksDir() {
64
+ return path.join(os.homedir(), '.empirical', 'hooks');
65
+ }
66
+
67
+ function buildHookCommand(scriptPath) {
68
+ return `"${process.execPath}" "${scriptPath}"`;
69
+ }
70
+
71
+ function removeEmpiricalHookEntries(entries, scriptPath) {
72
+ if (!Array.isArray(entries)) return [];
73
+ return entries
74
+ .map((entry) => {
75
+ if (!entry || !Array.isArray(entry.hooks)) return entry;
76
+ const filteredHooks = entry.hooks.filter(
77
+ (h) => !(h && typeof h.command === 'string' && h.command.includes(scriptPath))
78
+ );
79
+ return { ...entry, hooks: filteredHooks };
80
+ })
81
+ .filter((entry) => entry && (!Array.isArray(entry.hooks) || entry.hooks.length > 0));
82
+ }
83
+
84
+ function installClaudeHooks() {
85
+ const hooksDir = getEmpiricalHooksDir();
86
+ ensureDir(hooksDir);
87
+
88
+ // Older installs registered a per-turn UserPromptSubmit hook here too. That traded an
89
+ // unconditional network call on every single prompt for determinism, which is wasteful —
90
+ // the native empirical-memory plugin now covers ad-hoc reads/writes via
91
+ // Bash without that per-turn tax. SessionStart remains: one cheap call per session boundary.
92
+ const userPromptScriptPath = path.join(hooksDir, 'claude-user-prompt-submit.js');
93
+ const sessionStartScriptPath = path.join(hooksDir, 'claude-session-start.js');
94
+ writeFile(sessionStartScriptPath, CLAUDE_HOOK_SESSION_START_SOURCE);
95
+
96
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
97
+ const settings = readJsonIfExists(settingsPath) || {};
98
+ if (!settings.hooks || typeof settings.hooks !== 'object') {
99
+ settings.hooks = {};
100
+ }
101
+
102
+ // Clean up the old per-turn hook entry if a prior install left one behind.
103
+ if (Array.isArray(settings.hooks.UserPromptSubmit)) {
104
+ settings.hooks.UserPromptSubmit = removeEmpiricalHookEntries(settings.hooks.UserPromptSubmit, userPromptScriptPath);
105
+ if (settings.hooks.UserPromptSubmit.length === 0) {
106
+ delete settings.hooks.UserPromptSubmit;
107
+ }
108
+ }
109
+
110
+ settings.hooks.SessionStart = removeEmpiricalHookEntries(settings.hooks.SessionStart, sessionStartScriptPath);
111
+ for (const matcher of ['startup', 'resume', 'compact']) {
112
+ settings.hooks.SessionStart.push({
113
+ matcher,
114
+ hooks: [{ type: 'command', command: buildHookCommand(sessionStartScriptPath), timeout: 15 }]
115
+ });
116
+ }
117
+
118
+ writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
119
+ log(' - Claude SessionStart hook installed for deterministic memory lookups.');
120
+ }
121
+
122
+ module.exports = {
123
+ CLAUDE_HOOK_SESSION_START_SOURCE,
124
+ getEmpiricalHooksDir,
125
+ buildHookCommand,
126
+ removeEmpiricalHookEntries,
127
+ installClaudeHooks
128
+ };
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ const { runResolvedCommand, summarizeProcessOutput } = require('../fsUtils');
4
+
5
+ const EMPIRICAL_PLUGIN_SOURCE = process.env.EMPIRICAL_PLUGIN_SOURCE || 'mattgauzza/empirical-memory-plugin';
6
+ const CORE_PLUGIN_NAME = 'empirical-memory';
7
+ const OPTIONAL_PLUGIN_NAMES = ['empirical-work-history', 'empirical-scar-tissue', 'empirical-todo'];
8
+
9
+ function runPluginCommand(command, args, options = {}) {
10
+ return runResolvedCommand(command, args, { stdio: 'inherit', ...options });
11
+ }
12
+
13
+ function assertPluginInstallSucceeded(agentId, result) {
14
+ if (result && result.status === 0) return;
15
+ const detail = result ? summarizeProcessOutput(result) : 'no process result';
16
+ throw new Error(`${agentId} plugin installation failed.${detail ? ` Details: ${detail}` : ''}`);
17
+ }
18
+
19
+ function updateOrInstall(agentId, command, updateArgs, installArgs) {
20
+ const updateResult = runPluginCommand(command, updateArgs);
21
+ if (updateResult && updateResult.status === 0) return updateResult;
22
+
23
+ const installResult = runPluginCommand(command, installArgs);
24
+ assertPluginInstallSucceeded(agentId, installResult);
25
+ return installResult;
26
+ }
27
+
28
+ function normalizePluginNames(pluginNames = [CORE_PLUGIN_NAME]) {
29
+ const names = Array.isArray(pluginNames) ? pluginNames : [pluginNames];
30
+ const normalized = [...new Set(names.map(String))];
31
+ if (!normalized.includes(CORE_PLUGIN_NAME)) normalized.unshift(CORE_PLUGIN_NAME);
32
+ const invalid = normalized.filter((name) => name !== CORE_PLUGIN_NAME && !OPTIONAL_PLUGIN_NAMES.includes(name));
33
+ if (invalid.length) throw new Error(`Unsupported Empirical plugin(s): ${invalid.join(', ')}`);
34
+ return normalized;
35
+ }
36
+
37
+ function installNativePlugin(agentId, pluginNames = [CORE_PLUGIN_NAME]) {
38
+ const names = normalizePluginNames(pluginNames);
39
+ if (agentId === 'claude') {
40
+ // Marketplace registration is intentionally best-effort: native Claude returns non-zero
41
+ // when the same Git marketplace is already registered, but the install command is still
42
+ // authoritative and idempotent.
43
+ runPluginCommand('claude', ['plugin', 'marketplace', 'add', EMPIRICAL_PLUGIN_SOURCE], { stdio: 'pipe' });
44
+ for (const name of names) {
45
+ updateOrInstall(
46
+ agentId,
47
+ 'claude',
48
+ ['plugin', 'update', `${name}@empirical-memory`, '--scope', 'user'],
49
+ ['plugin', 'install', name, '--scope', 'user']
50
+ );
51
+ }
52
+ return;
53
+ }
54
+
55
+ if (agentId === 'codex') {
56
+ runPluginCommand('codex', ['plugin', 'marketplace', 'add', EMPIRICAL_PLUGIN_SOURCE], { stdio: 'pipe' });
57
+ // Refresh the Git marketplace snapshot before reinstalling an existing plugin.
58
+ runPluginCommand('codex', ['plugin', 'marketplace', 'upgrade', 'empirical-memory'], { stdio: 'pipe' });
59
+ for (const name of names) {
60
+ const result = runPluginCommand('codex', ['plugin', 'add', `${name}@empirical-memory`]);
61
+ assertPluginInstallSucceeded(agentId, result);
62
+ }
63
+ return;
64
+ }
65
+
66
+ if (agentId === 'copilot') {
67
+ // Copilot is deprecating direct repository installs. Register the GitHub
68
+ // marketplace first, then use the marketplace-qualified plugin name.
69
+ runPluginCommand('copilot', ['plugin', 'marketplace', 'add', EMPIRICAL_PLUGIN_SOURCE], { stdio: 'pipe' });
70
+ runPluginCommand('copilot', ['plugin', 'marketplace', 'update', 'empirical-memory'], { stdio: 'pipe' });
71
+ for (const name of names) {
72
+ const result = runPluginCommand('copilot', ['plugin', 'install', `${name}@empirical-memory`]);
73
+ assertPluginInstallSucceeded(agentId, result);
74
+ }
75
+ return;
76
+ }
77
+
78
+ throw new Error(`Unsupported plugin target: ${agentId}`);
79
+ }
80
+
81
+ module.exports = {
82
+ EMPIRICAL_PLUGIN_SOURCE,
83
+ CORE_PLUGIN_NAME,
84
+ OPTIONAL_PLUGIN_NAMES,
85
+ normalizePluginNames,
86
+ installNativePlugin
87
+ };
@@ -0,0 +1,403 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { log, warn, uiHeader, uiSuccess, uiDim, ask, askYesNo, askWorkflowMultiSelect } = require('../ui');
6
+ const INSTALL_PROMPT_OPTIONS_DEFAULTS = Object.freeze({
7
+ queryBeforeCodingTask: false,
8
+ requireMemoryInWorkflows: true,
9
+ requiredWorkflows: [
10
+ 'research',
11
+ 'debugging',
12
+ 'spec_prd',
13
+ 'status',
14
+ 'handoff',
15
+ 'personal_health',
16
+ 'lead_management',
17
+ 'assistant_ops'
18
+ ],
19
+ allowDiscretionaryMemoryUsage: true,
20
+ stopOnMemoryToolFailure: true,
21
+ rememberCodingStyle: true,
22
+ rememberArchitectureDecisions: true,
23
+ recordWorkHistory: true,
24
+ recordScarTissue: true,
25
+ recordTodos: false,
26
+ storeInstructionArtifacts: false,
27
+ checkMemoryBeforeAskingContext: true,
28
+ durableWritesOnly: true
29
+ });
30
+
31
+ const WORKFLOW_POLICY_OPTIONS = Object.freeze([
32
+ { id: 'research', label: 'research' },
33
+ { id: 'debugging', label: 'debugging' },
34
+ { id: 'spec_prd', label: 'spec/prd' },
35
+ { id: 'status', label: 'status' },
36
+ { id: 'handoff', label: 'handoff' },
37
+ { id: 'personal_health', label: 'personal health' },
38
+ { id: 'lead_management', label: 'lead management' },
39
+ { id: 'assistant_ops', label: 'assistant ops' }
40
+ ]);
41
+
42
+ function normalizeRequiredWorkflows(value) {
43
+ const valid = new Set(WORKFLOW_POLICY_OPTIONS.map((option) => option.id));
44
+ if (!Array.isArray(value)) {
45
+ return [...INSTALL_PROMPT_OPTIONS_DEFAULTS.requiredWorkflows];
46
+ }
47
+ const normalized = value.filter((entry) => valid.has(entry));
48
+ return normalized.length ? normalized : [...INSTALL_PROMPT_OPTIONS_DEFAULTS.requiredWorkflows];
49
+ }
50
+
51
+ function workflowIdsToLabels(workflowIds = []) {
52
+ const byId = new Map(WORKFLOW_POLICY_OPTIONS.map((option) => [option.id, option.label]));
53
+ return workflowIds.map((id) => byId.get(id) || id);
54
+ }
55
+
56
+ function normalizeInstallPromptOptions(options = {}) {
57
+ return {
58
+ queryBeforeCodingTask:
59
+ options.queryBeforeCodingTask !== false,
60
+ requireMemoryInWorkflows:
61
+ options.requireMemoryInWorkflows !== false,
62
+ requiredWorkflows:
63
+ normalizeRequiredWorkflows(options.requiredWorkflows),
64
+ allowDiscretionaryMemoryUsage:
65
+ options.allowDiscretionaryMemoryUsage !== false,
66
+ stopOnMemoryToolFailure:
67
+ options.stopOnMemoryToolFailure !== false,
68
+ rememberCodingStyle:
69
+ options.rememberCodingStyle !== false,
70
+ rememberArchitectureDecisions:
71
+ options.rememberArchitectureDecisions !== false,
72
+ recordWorkHistory:
73
+ options.recordWorkHistory !== false,
74
+ recordScarTissue:
75
+ options.recordScarTissue !== false,
76
+ recordTodos:
77
+ options.recordTodos === true,
78
+ storeInstructionArtifacts:
79
+ options.storeInstructionArtifacts === true,
80
+ checkMemoryBeforeAskingContext:
81
+ options.checkMemoryBeforeAskingContext !== false,
82
+ durableWritesOnly:
83
+ options.durableWritesOnly !== false
84
+ };
85
+ }
86
+
87
+ function buildSharedEmpiricalPolicyLines(options = {}) {
88
+ const policy = normalizeInstallPromptOptions(options);
89
+ const lines = [];
90
+
91
+ if (policy.queryBeforeCodingTask) {
92
+ lines.push('- Before coding, call `query_memories` once for relevant repo/user context.');
93
+ }
94
+ if (policy.requireMemoryInWorkflows) {
95
+ const workflowLabels = workflowIdsToLabels(policy.requiredWorkflows);
96
+ lines.push(`- For these workflows (${workflowLabels.join(', ')}), call \`query_memories\` before substantive response.`);
97
+ }
98
+ if (policy.allowDiscretionaryMemoryUsage) {
99
+ lines.push('- Outside required workflows, use Empirical proactively when prior context is likely useful.');
100
+ } else {
101
+ lines.push('- Outside required workflows, use Empirical only when explicitly requested by the user.');
102
+ }
103
+ if (policy.stopOnMemoryToolFailure) {
104
+ lines.push('- If Empirical tools fail, stop and report the failure before making code changes.');
105
+ }
106
+ lines.push('- If a memory call returns auth/session errors (`Auth required`, `invalid_token`, `Authentication required`, `Transport send error`), treat it as expired MCP auth.');
107
+ lines.push('- Retry the same MCP tool call once before taking any other action.');
108
+ lines.push('- If retry fails, refresh Empirical MCP auth for the active client before continuing memory-dependent work:');
109
+ lines.push('- Codex CLI: run `codex mcp logout empirical` then `codex mcp login empirical`.');
110
+ lines.push('- Claude Code: run `/mcp`, reconnect/re-authenticate the Empirical server, then retry the failed tool call.');
111
+ lines.push('- Copilot CLI: complete the MCP OAuth reconnect/sign-in flow for Empirical in Copilot, then retry the failed tool call.');
112
+ lines.push('- After re-authentication, retry the original tool call once; if it still fails, stop and ask the user to repair MCP auth/connectivity.');
113
+ lines.push('- If memory conflicts with the latest repo/user instruction, follow repo/user instruction.');
114
+ if (policy.rememberCodingStyle) {
115
+ lines.push('- Persist durable coding style preferences and conventions when they are established.');
116
+ }
117
+ if (policy.rememberArchitectureDecisions) {
118
+ lines.push('- Persist durable architecture/design decisions for future sessions.');
119
+ }
120
+ if (policy.recordWorkHistory) {
121
+ lines.push('- After meaningful completed work, record a concise timestamped work-history summary with what changed, why, verification, and any next action.');
122
+ }
123
+ if (policy.recordScarTissue) {
124
+ lines.push('- When a recurring failure, sharp edge, or corrected misconception is discovered, record concise scar-tissue guidance with the symptom, cause, prevention, and verification.');
125
+ } else {
126
+ lines.push('- Do not create scar-tissue guidance unless the user explicitly asks for it.');
127
+ }
128
+ if (policy.storeInstructionArtifacts) {
129
+ lines.push('- Query Empirical first for relevant instruction artifacts (AGENTS.md/CLAUDE.md/skills-style guidance).');
130
+ lines.push('- If task needs exact syntax or implementation details, open the source file directly.');
131
+ lines.push('- Persist this type of durable instruction context to Empirical for recall: concise rules, conventions, decision summaries, source path, and optional version/hash pointer.');
132
+ lines.push('- Use update-first writes: when an artifact/topic already exists, update it instead of creating a new memory.');
133
+ lines.push('- Create a new memory only when materially new (new decision, new version/hash, or new scope).');
134
+ lines.push('- Apply a soft cap of 1-2 instruction-artifact writes per task; prefer merge/update over additional creates.');
135
+ lines.push('- Do not store full raw file dumps by default; store concise, durable extracts that are easy to query.');
136
+ }
137
+ if (policy.checkMemoryBeforeAskingContext) {
138
+ lines.push('- Check memory before asking the user to restate prior context.');
139
+ }
140
+ lines.push('- Use available Empirical MCP tools when appropriate: record_graph_memory, get_memory, list_memories, query_memories, add_note_by_query, update_memory_by_query, delete_memory_by_query, get_user.');
141
+ if (policy.durableWritesOnly) {
142
+ lines.push('- Persist only durable context; never store secrets, tokens, or one-off transient values.');
143
+ }
144
+ lines.push('- Keep writes concise, factual, and easy to query.');
145
+
146
+ return lines;
147
+ }
148
+
149
+ function workflowLabelsSummary(options = {}) {
150
+ const policy = normalizeInstallPromptOptions(options);
151
+ if (!policy.requireMemoryInWorkflows) {
152
+ return 'research, debugging, spec/PRD work, status updates, and handoffs';
153
+ }
154
+ return workflowIdsToLabels(policy.requiredWorkflows).join(', ');
155
+ }
156
+
157
+ function buildEmpiricalSkillFrontmatter(options = {}) {
158
+ return {
159
+ name: 'empirical-memory',
160
+ // Keep frontmatter deliberately simple: Copilot's YAML parser rejects an
161
+ // unquoted colon in a plain scalar, and does not support when_to_use.
162
+ description: 'Use Empirical CLI and MCP to recall and save durable project context across sessions and projects.'
163
+ };
164
+ }
165
+
166
+ function buildEmpiricalSkillBody(options = {}) {
167
+ const policyLines = buildSharedEmpiricalPolicyLines(options);
168
+ const workflows = workflowLabelsSummary(options);
169
+ return `## Empirical Memory - Use It Like grep/view
170
+
171
+ Use this skill for ${workflows}, whenever prior context may matter, and whenever the user asks to remember, recall, update, or forget information.
172
+
173
+ Prefer calling the \`empirical\` CLI directly via the available shell over the MCP tools when both are
174
+ available: \`empirical memory query|get|list|record|note|update|delete\`. The CLI doesn't require an
175
+ extra tool-discovery step the way the deferred Empirical MCP tools do. The MCP tools
176
+ (\`get_memory\`, \`list_memories\`, \`query_memories\`, \`record_graph_memory\`, \`add_note_by_query\`, \`update_memory_by_query\`,
177
+ \`delete_memory_by_query\`, \`get_user\`) remain available as a fallback or for structured/graph
178
+ queries the CLI doesn't cover.
179
+
180
+ ### Policy
181
+ ${policyLines.join('\n')}
182
+
183
+ ### Authored Skills - push what you write
184
+ - If you author a new \`SKILL.md\` for the user, or substantially edit an existing one, during a
185
+ session, tell the user about \`empirical skill push <path/to/SKILL.md>\` afterward so the skill
186
+ can follow them across machines/projects instead of staying local to this one.
187
+ - Offer, don't auto-push: surface the option (or offer to run it) and get the user's
188
+ confirmation before running \`empirical skill push\`. Do not run it unprompted the moment a new
189
+ or edited \`SKILL.md\` is detected — it uploads content to the user's account, which is a step
190
+ above a purely local file write.
191
+ - Run \`empirical doc\` or \`empirical skill push --help\` via the available shell for exact flags/usage.
192
+
193
+ ### Command reference
194
+ Run \`empirical doc memory\` via the available shell for exact flags/usage before guessing syntax.
195
+ `;
196
+ }
197
+
198
+ function buildEmpiricalSkillMarkdown(options = {}) {
199
+ const frontmatter = buildEmpiricalSkillFrontmatter(options);
200
+ const body = buildEmpiricalSkillBody(options);
201
+ return `---
202
+ name: ${frontmatter.name}
203
+ description: ${frontmatter.description}
204
+ ---
205
+
206
+ ${body}`;
207
+ }
208
+
209
+ function formatPromptOptionSummary(promptOptions) {
210
+ const entries = [
211
+ ['Require memory in selected workflows', promptOptions.requireMemoryInWorkflows],
212
+ ['Allow discretionary memory usage', promptOptions.allowDiscretionaryMemoryUsage],
213
+ ['Stop on Empirical tool failure', promptOptions.stopOnMemoryToolFailure],
214
+ ['Store coding style preferences', promptOptions.rememberCodingStyle],
215
+ ['Store architecture decisions', promptOptions.rememberArchitectureDecisions],
216
+ ['Record timestamped work history', promptOptions.recordWorkHistory],
217
+ ['Record scar-tissue guidance', promptOptions.recordScarTissue],
218
+ ['Enable project-based Todo skill', promptOptions.recordTodos],
219
+ ['Store/recall instruction artifacts', promptOptions.storeInstructionArtifacts],
220
+ ['Check memory before asking for context', promptOptions.checkMemoryBeforeAskingContext],
221
+ ['Store durable context only', promptOptions.durableWritesOnly]
222
+ ];
223
+ const lines = entries
224
+ .map(([label, enabled]) => ` ${enabled ? '[x]' : '[ ]'} ${label}`)
225
+ .join('\n');
226
+ const workflowLine = ` [${promptOptions.requireMemoryInWorkflows ? 'x' : ' '}] Required workflows: ${workflowIdsToLabels(promptOptions.requiredWorkflows).join(', ')}`;
227
+ return `${lines}\n${workflowLine}`;
228
+ }
229
+
230
+ async function collectInstallerPromptOptions() {
231
+ if (!process.stdin.isTTY) {
232
+ return {
233
+ savePolicy: true,
234
+ promptOptions: { ...INSTALL_PROMPT_OPTIONS_DEFAULTS }
235
+ };
236
+ }
237
+
238
+ const home = os.homedir();
239
+ log(uiHeader('Global instruction files'));
240
+ log(` - Copilot: ${path.join(home, '.copilot', 'copilot-instructions.md')}`);
241
+ log(` - Codex: ${path.join(home, '.codex', 'AGENTS.md')}`);
242
+ log(` - Claude: ${path.join(home, '.claude', 'CLAUDE.md')}`);
243
+ log('');
244
+
245
+ const savePolicy = await askYesNo(
246
+ 'Save these personalized Empirical behavior preferences for native skills?',
247
+ true
248
+ );
249
+ log('');
250
+ if (!savePolicy) {
251
+ log(uiSuccess('Personalized behavior policy not saved; native skills will use safe defaults.'));
252
+ log('');
253
+ return {
254
+ savePolicy: false,
255
+ promptOptions: { ...INSTALL_PROMPT_OPTIONS_DEFAULTS }
256
+ };
257
+ }
258
+
259
+ log(uiHeader('Empirical policy setup'));
260
+ log(uiDim('Answer a few questions to configure native Empirical skills.'));
261
+ log('');
262
+
263
+ const requireMemoryInWorkflows = await askYesNo(
264
+ 'Require Empirical memory-first behavior in specific workflows?',
265
+ true
266
+ );
267
+ let requiredWorkflows = [...INSTALL_PROMPT_OPTIONS_DEFAULTS.requiredWorkflows];
268
+ if (requireMemoryInWorkflows) {
269
+ log('');
270
+ requiredWorkflows = await askWorkflowMultiSelect(
271
+ 'Which workflows should require memory-first behavior? (all preselected)',
272
+ WORKFLOW_POLICY_OPTIONS,
273
+ INSTALL_PROMPT_OPTIONS_DEFAULTS.requiredWorkflows
274
+ );
275
+ }
276
+ const allowDiscretionaryMemoryUsage = await askYesNo(
277
+ 'Allow discretionary Empirical use outside required workflows?',
278
+ true
279
+ );
280
+ const stopOnMemoryToolFailure = await askYesNo(
281
+ 'If Empirical tools fail, should the agent stop before code changes?',
282
+ true
283
+ );
284
+ const rememberCodingStyle = await askYesNo(
285
+ 'Do you want Empirical to store coding style preferences?',
286
+ true
287
+ );
288
+ const rememberArchitectureDecisions = await askYesNo(
289
+ 'Do you want Empirical to store architecture/design decisions?',
290
+ true
291
+ );
292
+ const recordWorkHistory = await askYesNo(
293
+ 'Should Empirical record concise timestamped summaries of completed work for future agents?',
294
+ true
295
+ );
296
+ const recordScarTissue = await askYesNo(
297
+ 'Should Empirical record recurring failure patterns and prevention guidance (scar tissue) for future agents?',
298
+ true
299
+ );
300
+ const recordTodos = await askYesNo(
301
+ 'Install the project-based Todo skill for actionable follow-ups?',
302
+ false
303
+ );
304
+ const storeInstructionArtifacts = await askYesNo(
305
+ 'Store and recall instruction artifacts (AGENTS.md/CLAUDE.md/skills-style rules) in Empirical?',
306
+ false
307
+ );
308
+ const checkMemoryBeforeAskingContext = await askYesNo(
309
+ 'Should agents check memory before asking you to restate context?',
310
+ true
311
+ );
312
+ const durableWritesOnly = await askYesNo(
313
+ 'Restrict writes to durable context only (never secrets/tokens/transient values)?',
314
+ true
315
+ );
316
+
317
+ const promptOptions = normalizeInstallPromptOptions({
318
+ queryBeforeCodingTask: false,
319
+ requireMemoryInWorkflows,
320
+ requiredWorkflows,
321
+ allowDiscretionaryMemoryUsage,
322
+ stopOnMemoryToolFailure,
323
+ rememberCodingStyle,
324
+ rememberArchitectureDecisions,
325
+ recordWorkHistory,
326
+ recordScarTissue,
327
+ recordTodos,
328
+ storeInstructionArtifacts,
329
+ checkMemoryBeforeAskingContext,
330
+ durableWritesOnly
331
+ });
332
+
333
+ log('');
334
+ log(uiSuccess('Policy profile captured.'));
335
+ log(formatPromptOptionSummary(promptOptions));
336
+ log('');
337
+
338
+ return {
339
+ savePolicy: true,
340
+ promptOptions
341
+ };
342
+ }
343
+
344
+ function buildSkillFileContent({ mcpEnabled = false } = {}) {
345
+ if (mcpEnabled) {
346
+ return `# Empirical Memory Skill
347
+
348
+ Empirical memory is installed via MCP. Use MCP tools as the primary interface.
349
+
350
+ ## MCP tools (primary)
351
+
352
+ - \`query_memories\` — search memories by natural language
353
+ - \`record_graph_memory\` — save a new memory
354
+ - \`add_note_by_query\` — append a note to an existing memory
355
+ - \`update_memory_by_query\` — update fields on an existing memory
356
+ - \`delete_memory_by_query\` — delete a memory by query
357
+ - \`get_user\` — get current user identity and workspace
358
+
359
+ ## CLI fallback (when MCP is unavailable)
360
+
361
+ empirical doc List all available topics
362
+ empirical doc <topic> Load full CLI instructions for that topic
363
+ empirical doctor Verify connectivity and token status
364
+ `;
365
+ }
366
+
367
+ return `# Empirical Memory Skill
368
+
369
+ Empirical memory is installed in this environment.
370
+
371
+ ## Discover available commands
372
+
373
+ empirical doc List all available topics
374
+ empirical doc <topic> Load full instructions for that topic
375
+ empirical doctor Verify connectivity and token status
376
+
377
+ ## Topics
378
+
379
+ memory Query, record, update, and delete memories
380
+ user Get current user identity
381
+ workspace List and switch workspaces
382
+ auth Token types, bootstrap, and refresh
383
+ agents Integration modes and SKILL file paths
384
+ install Install Empirical for Codex / Copilot / Claude Code
385
+
386
+ Load any topic on demand — for example: \`empirical doc memory\` or \`empirical doc user\`.
387
+ `;
388
+ }
389
+
390
+ module.exports = {
391
+ INSTALL_PROMPT_OPTIONS_DEFAULTS,
392
+ WORKFLOW_POLICY_OPTIONS,
393
+ normalizeRequiredWorkflows,
394
+ workflowIdsToLabels,
395
+ normalizeInstallPromptOptions,
396
+ buildSharedEmpiricalPolicyLines,
397
+ workflowLabelsSummary,
398
+ buildEmpiricalSkillFrontmatter,
399
+ buildEmpiricalSkillBody,
400
+ buildEmpiricalSkillMarkdown,
401
+ formatPromptOptionSummary,
402
+ collectInstallerPromptOptions
403
+ };